diff options
-rw-r--r-- | cc/cc.gyp | 1 | ||||
-rw-r--r-- | cc/proto/BUILD.gn | 1 | ||||
-rw-r--r-- | cc/proto/gfx_conversions_unittest.cc | 74 | ||||
-rw-r--r-- | cc/proto/property_tree.proto | 148 | ||||
-rw-r--r-- | cc/trees/property_tree.cc | 464 | ||||
-rw-r--r-- | cc/trees/property_tree.h | 76 | ||||
-rw-r--r-- | cc/trees/property_tree_unittest.cc | 1491 |
7 files changed, 1694 insertions, 561 deletions
@@ -594,6 +594,7 @@ 'proto/point.proto', 'proto/point3f.proto', 'proto/pointf.proto', + 'proto/property_tree.proto', 'proto/rect.proto', 'proto/rectf.proto', 'proto/region.proto', diff --git a/cc/proto/BUILD.gn b/cc/proto/BUILD.gn index 07258a6..884045f 100644 --- a/cc/proto/BUILD.gn +++ b/cc/proto/BUILD.gn @@ -36,6 +36,7 @@ proto_library("proto_internal") { "point.proto", "point3f.proto", "pointf.proto", + "property_tree.proto", "rect.proto", "rectf.proto", "region.proto", diff --git a/cc/proto/gfx_conversions_unittest.cc b/cc/proto/gfx_conversions_unittest.cc index 6affc1c..cf46be8 100644 --- a/cc/proto/gfx_conversions_unittest.cc +++ b/cc/proto/gfx_conversions_unittest.cc @@ -194,29 +194,79 @@ TEST(GfxProtoConversionsTest, SerializeDeserializeTransform) { } TEST(GfxProtoConversionsTest, SerializeDeserializeVector2dF) { - const gfx::Vector2dF vector(5.1f, 10.2f); + // Test Case 1 + const gfx::Vector2dF vector1(5.1f, 10.2f); // Test Vector2dFToProto - proto::Vector2dF proto; - Vector2dFToProto(vector, &proto); - EXPECT_EQ(vector.x(), proto.x()); - EXPECT_EQ(vector.y(), proto.y()); + proto::Vector2dF proto1; + Vector2dFToProto(vector1, &proto1); + EXPECT_EQ(vector1.x(), proto1.x()); + EXPECT_EQ(vector1.y(), proto1.y()); // Test ProtoToVector2dF - EXPECT_EQ(vector, ProtoToVector2dF(proto)); + EXPECT_EQ(vector1, ProtoToVector2dF(proto1)); + + // Test Case 2 + const gfx::Vector2dF vector2(-3.1f, 0.2f); + + // Test Vector2dFToProto + proto::Vector2dF proto2; + Vector2dFToProto(vector2, &proto2); + EXPECT_EQ(vector2.x(), proto2.x()); + EXPECT_EQ(vector2.y(), proto2.y()); + + // Test ProtoToVector2dF + EXPECT_EQ(vector2, ProtoToVector2dF(proto2)); + + // Test Case 3 + const gfx::Vector2dF vector3(2.0f, -1.5f); + + // Test Vector2dFToProto + proto::Vector2dF proto3; + Vector2dFToProto(vector3, &proto3); + EXPECT_EQ(vector3.x(), proto3.x()); + EXPECT_EQ(vector3.y(), proto3.y()); + + // Test ProtoToVector2dF + EXPECT_EQ(vector3, ProtoToVector2dF(proto3)); } TEST(GfxProtoConversionsTest, SerializeDeserializeScrollOffset) { - const gfx::ScrollOffset scroll_offset(5.1f, 10.2f); + // Test Case 1 + const gfx::ScrollOffset scroll_offset1(5.1f, 10.2f); + + // Test ScrollOffsetToProto + proto::ScrollOffset proto1; + ScrollOffsetToProto(scroll_offset1, &proto1); + EXPECT_EQ(scroll_offset1.x(), proto1.x()); + EXPECT_EQ(scroll_offset1.y(), proto1.y()); + + // Test ProtoToScrollOffset + EXPECT_EQ(scroll_offset1, ProtoToScrollOffset(proto1)); + + // Test Case 2 + const gfx::ScrollOffset scroll_offset2(-0.1f, 0.2f); + + // Test ScrollOffsetToProto + proto::ScrollOffset proto2; + ScrollOffsetToProto(scroll_offset2, &proto2); + EXPECT_EQ(scroll_offset2.x(), proto2.x()); + EXPECT_EQ(scroll_offset2.y(), proto2.y()); + + // Test ProtoToScrollOffset + EXPECT_EQ(scroll_offset2, ProtoToScrollOffset(proto2)); + + // Test Case 3 + const gfx::ScrollOffset scroll_offset3(4.0f, -3.2f); // Test ScrollOffsetToProto - proto::ScrollOffset proto; - ScrollOffsetToProto(scroll_offset, &proto); - EXPECT_EQ(scroll_offset.x(), proto.x()); - EXPECT_EQ(scroll_offset.y(), proto.y()); + proto::ScrollOffset proto3; + ScrollOffsetToProto(scroll_offset3, &proto3); + EXPECT_EQ(scroll_offset3.x(), proto3.x()); + EXPECT_EQ(scroll_offset3.y(), proto3.y()); // Test ProtoToScrollOffset - EXPECT_EQ(scroll_offset, ProtoToScrollOffset(proto)); + EXPECT_EQ(scroll_offset3, ProtoToScrollOffset(proto3)); } } // namespace diff --git a/cc/proto/property_tree.proto b/cc/proto/property_tree.proto new file mode 100644 index 0000000..6bf2a7f --- /dev/null +++ b/cc/proto/property_tree.proto @@ -0,0 +1,148 @@ +// Copyright 2015 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. + +syntax = "proto2"; + +import "rectf.proto"; +import "scroll_offset.proto"; +import "transform.proto"; +import "vector2df.proto"; + +option optimize_for = LITE_RUNTIME; + +package cc.proto; + +// The messages declared in this file correspond to the classes declared in +// cc/trees/property_tree.h + +// Proto for struct TransformNodeData. +message TranformNodeData { + optional Transform pre_local = 1; + optional Transform local = 2; + optional Transform post_local = 3; + optional Transform to_parent = 4; + optional Transform to_target = 5; + optional Transform from_target = 6; + optional Transform to_screen = 7; + optional Transform from_screen = 8; + + optional int64 target_id = 9; + optional int64 content_target_id = 10; + optional int64 source_node_id = 11; + optional bool needs_local_transform_update = 12; + optional bool is_invertible = 13; + optional bool ancestors_are_invertible = 14; + optional bool is_animated = 15; + optional bool to_screen_is_animated = 16; + optional bool has_only_translation_animations = 17; + optional bool to_screen_has_scale_animation = 18; + optional bool flattens_inherited_transform = 19; + optional bool node_and_ancestors_are_flat = 20; + optional bool node_and_ancestors_have_only_integer_translation = 21; + optional bool scrolls = 22; + optional bool needs_sublayer_scale = 23; + optional bool affected_by_inner_viewport_bounds_delta_x = 24; + optional bool affected_by_inner_viewport_bounds_delta_y = 25; + optional bool affected_by_outer_viewport_bounds_delta_x = 26; + optional bool affected_by_outer_viewport_bounds_delta_y = 27; + optional bool in_subtree_of_page_scale_layer = 28; + optional float post_local_scale_factor = 29; + optional float local_maximum_animation_target_scale = 30; + optional float local_starting_animation_scale = 31; + optional float combined_maximum_animation_target_scale = 32; + optional float combined_starting_animation_scale = 33; + + optional Vector2dF sublayer_scale = 34; + optional ScrollOffset scroll_offset = 35; + optional Vector2dF scroll_snap = 36; + optional Vector2dF source_offset = 37; + optional Vector2dF source_to_parent = 38; +} + +// Proto for struct ClipNodeData. +message ClipNodeData { + optional RectF clip = 1; + optional RectF combined_clip_in_target_space = 2; + optional RectF clip_in_target_space = 3; + + optional int64 transform_id = 4; + optional int64 target_id = 5; + optional bool applies_local_clip = 6; + optional bool layer_clipping_uses_only_local_clip = 7; + optional bool target_is_clipped = 8; + optional bool layers_are_clipped = 9; + optional bool layers_are_clipped_when_surfaces_disabled = 10; + optional bool resets_clip = 11; +} + +// Proto for struct EffectNodeData. +message EffectNodeData { + optional float opacity = 1; + optional float screen_space_opacity = 2; + optional bool has_render_surface = 3; + optional int64 transform_id = 4; + optional int64 clip_id = 5; +} + +// This defines the proto used for all types of struct TreeNode. +message TreeNode { + // The following fields are the base TreeNode properties. This list + // corresponds to the data members from struct TreeNode. + optional int64 id = 1; + optional int64 parent_id = 2; + optional int64 owner_id = 3; + + // The following fields correspond to the possible values for TreeNode::data. + // Only one of these fields should be set, based on the type of property tree + // this node belongs to. + optional TranformNodeData transform_node_data = 1000; + optional ClipNodeData clip_node_data = 1001; + optional EffectNodeData effect_node_data = 1002; +} + +// This defines the proto used for all property trees. PropertyType denotes the +// type of this tree. +message PropertyTree { + enum PropertyType { + Transform = 1; + Clip = 2; + Effect = 3; + } + + // The following fields are the base PropertyTree properties. This list + // corresponds to the data members from class PropertyTree. + optional PropertyType property_type = 1; + repeated TreeNode nodes = 2; + optional bool needs_update = 3; + + // The following fields denote the data members for each subclass of + // PropertyTree. Only one of these fields should be set, depending on the type + // of this property tree. + optional TransformTreeData transform_tree_data = 1000; +} + +// Proto for data members of class TransformTree. +message TransformTreeData { + optional bool source_to_parent_updates_allowed = 1; + optional float page_scale_factor = 2; + optional float device_scale_factor = 3; + optional float device_transform_scale_factor = 4; + optional Vector2dF inner_viewport_bounds_delta = 5; + optional Vector2dF outer_viewport_bounds_delta = 6; + repeated int64 nodes_affected_by_inner_viewport_bounds_delta = 7 + [packed = true]; + repeated int64 nodes_affected_by_outer_viewport_bounds_delta = 8 + [packed = true]; +} + +// Proto for class PropertyTrees. +message PropertyTrees { + optional PropertyTree transform_tree = 1; + optional PropertyTree effect_tree = 2; + optional PropertyTree clip_tree = 3; + + optional bool needs_rebuild = 4; + optional bool non_root_surfaces_enabled = 5; + optional int64 sequence_number = 6; +}
\ No newline at end of file diff --git a/cc/trees/property_tree.cc b/cc/trees/property_tree.cc index 570a687..a6b72e6 100644 --- a/cc/trees/property_tree.cc +++ b/cc/trees/property_tree.cc @@ -7,11 +7,42 @@ #include "base/logging.h" #include "cc/base/math_util.h" +#include "cc/proto/gfx_conversions.h" +#include "cc/proto/property_tree.pb.h" +#include "cc/proto/scroll_offset.pb.h" +#include "cc/proto/transform.pb.h" +#include "cc/proto/vector2df.pb.h" #include "cc/trees/property_tree.h" namespace cc { template <typename T> +bool TreeNode<T>::operator==(const TreeNode<T>& other) const { + return id == other.id && parent_id == other.parent_id && + owner_id == other.owner_id && data == other.data; +} + +template <typename T> +void TreeNode<T>::ToProtobuf(proto::TreeNode* proto) const { + proto->set_id(id); + proto->set_parent_id(parent_id); + proto->set_owner_id(owner_id); + data.ToProtobuf(proto); +} + +template <typename T> +void TreeNode<T>::FromProtobuf(const proto::TreeNode& proto) { + id = proto.id(); + parent_id = proto.parent_id(); + owner_id = proto.owner_id(); + data.FromProtobuf(proto); +} + +template struct TreeNode<TransformNodeData>; +template struct TreeNode<ClipNodeData>; +template struct TreeNode<EffectNodeData>; + +template <typename T> PropertyTree<T>::PropertyTree() : needs_update_(false) { nodes_.push_back(T()); @@ -50,6 +81,38 @@ void PropertyTree<T>::clear() { back()->parent_id = -1; } +template <typename T> +bool PropertyTree<T>::operator==(const PropertyTree<T>& other) const { + return nodes_ == other.nodes() && needs_update_ == other.needs_update(); +} + +template <typename T> +void PropertyTree<T>::ToProtobuf(proto::PropertyTree* proto) const { + DCHECK_EQ(0, proto->nodes_size()); + for (const auto& node : nodes_) + node.ToProtobuf(proto->add_nodes()); + proto->set_needs_update(needs_update_); +} + +template <typename T> +void PropertyTree<T>::FromProtobuf(const proto::PropertyTree& proto) { + // Verify that the property tree is empty. + DCHECK_EQ(static_cast<int>(nodes_.size()), 1); + DCHECK_EQ(back()->id, 0); + DCHECK_EQ(back()->parent_id, -1); + + // Add the first node. + DCHECK_GT(proto.nodes_size(), 0); + nodes_.back().FromProtobuf(proto.nodes(0)); + + for (int i = 1; i < proto.nodes_size(); ++i) { + nodes_.push_back(T()); + nodes_.back().FromProtobuf(proto.nodes(i)); + } + + needs_update_ = proto.needs_update(); +} + template class PropertyTree<TransformNode>; template class PropertyTree<ClipNode>; template class PropertyTree<EffectNode>; @@ -84,6 +147,54 @@ TransformNodeData::TransformNodeData() TransformNodeData::~TransformNodeData() { } +bool TransformNodeData::operator==(const TransformNodeData& other) const { + return pre_local == other.pre_local && local == other.local && + post_local == other.post_local && to_parent == other.to_parent && + to_target == other.to_target && from_target == other.from_target && + to_screen == other.to_screen && from_screen == other.from_screen && + target_id == other.target_id && + content_target_id == other.content_target_id && + source_node_id == other.source_node_id && + needs_local_transform_update == other.needs_local_transform_update && + is_invertible == other.is_invertible && + ancestors_are_invertible == other.ancestors_are_invertible && + is_animated == other.is_animated && + to_screen_is_animated == other.to_screen_is_animated && + has_only_translation_animations == + other.has_only_translation_animations && + to_screen_has_scale_animation == other.to_screen_has_scale_animation && + flattens_inherited_transform == other.flattens_inherited_transform && + node_and_ancestors_are_flat == other.node_and_ancestors_are_flat && + node_and_ancestors_have_only_integer_translation == + other.node_and_ancestors_have_only_integer_translation && + scrolls == other.scrolls && + needs_sublayer_scale == other.needs_sublayer_scale && + affected_by_inner_viewport_bounds_delta_x == + other.affected_by_inner_viewport_bounds_delta_x && + affected_by_inner_viewport_bounds_delta_y == + other.affected_by_inner_viewport_bounds_delta_y && + affected_by_outer_viewport_bounds_delta_x == + other.affected_by_outer_viewport_bounds_delta_x && + affected_by_outer_viewport_bounds_delta_y == + other.affected_by_outer_viewport_bounds_delta_y && + in_subtree_of_page_scale_layer == + other.in_subtree_of_page_scale_layer && + post_local_scale_factor == other.post_local_scale_factor && + local_maximum_animation_target_scale == + other.local_maximum_animation_target_scale && + local_starting_animation_scale == + other.local_starting_animation_scale && + combined_maximum_animation_target_scale == + other.combined_maximum_animation_target_scale && + combined_starting_animation_scale == + other.combined_starting_animation_scale && + sublayer_scale == other.sublayer_scale && + scroll_offset == other.scroll_offset && + scroll_snap == other.scroll_snap && + source_offset == other.source_offset && + source_to_parent == other.source_to_parent; +} + void TransformNodeData::update_pre_local_transform( const gfx::Point3F& transform_origin) { pre_local.MakeIdentity(); @@ -102,6 +213,133 @@ void TransformNodeData::update_post_local_transform( transform_origin.z()); } +void TransformNodeData::ToProtobuf(proto::TreeNode* proto) const { + DCHECK(!proto->has_transform_node_data()); + proto::TranformNodeData* data = proto->mutable_transform_node_data(); + + TransformToProto(pre_local, data->mutable_pre_local()); + TransformToProto(local, data->mutable_local()); + TransformToProto(post_local, data->mutable_post_local()); + + TransformToProto(to_parent, data->mutable_to_parent()); + + TransformToProto(to_target, data->mutable_to_target()); + TransformToProto(from_target, data->mutable_from_target()); + + TransformToProto(to_screen, data->mutable_to_screen()); + TransformToProto(from_screen, data->mutable_from_screen()); + + data->set_target_id(target_id); + data->set_content_target_id(content_target_id); + data->set_source_node_id(source_node_id); + + data->set_needs_local_transform_update(needs_local_transform_update); + + data->set_is_invertible(is_invertible); + data->set_ancestors_are_invertible(ancestors_are_invertible); + + data->set_is_animated(is_animated); + data->set_to_screen_is_animated(to_screen_is_animated); + data->set_has_only_translation_animations(has_only_translation_animations); + data->set_to_screen_has_scale_animation(to_screen_has_scale_animation); + + data->set_flattens_inherited_transform(flattens_inherited_transform); + data->set_node_and_ancestors_are_flat(node_and_ancestors_are_flat); + + data->set_node_and_ancestors_have_only_integer_translation( + node_and_ancestors_have_only_integer_translation); + data->set_scrolls(scrolls); + data->set_needs_sublayer_scale(needs_sublayer_scale); + + data->set_affected_by_inner_viewport_bounds_delta_x( + affected_by_inner_viewport_bounds_delta_x); + data->set_affected_by_inner_viewport_bounds_delta_y( + affected_by_inner_viewport_bounds_delta_y); + data->set_affected_by_outer_viewport_bounds_delta_x( + affected_by_outer_viewport_bounds_delta_x); + data->set_affected_by_outer_viewport_bounds_delta_y( + affected_by_outer_viewport_bounds_delta_y); + + data->set_in_subtree_of_page_scale_layer(in_subtree_of_page_scale_layer); + data->set_post_local_scale_factor(post_local_scale_factor); + data->set_local_maximum_animation_target_scale( + local_maximum_animation_target_scale); + data->set_local_starting_animation_scale(local_starting_animation_scale); + data->set_combined_maximum_animation_target_scale( + combined_maximum_animation_target_scale); + data->set_combined_starting_animation_scale( + combined_starting_animation_scale); + + Vector2dFToProto(sublayer_scale, data->mutable_sublayer_scale()); + ScrollOffsetToProto(scroll_offset, data->mutable_scroll_offset()); + Vector2dFToProto(scroll_snap, data->mutable_scroll_snap()); + Vector2dFToProto(source_offset, data->mutable_source_offset()); + Vector2dFToProto(source_to_parent, data->mutable_source_to_parent()); +} + +void TransformNodeData::FromProtobuf(const proto::TreeNode& proto) { + DCHECK(proto.has_transform_node_data()); + const proto::TranformNodeData& data = proto.transform_node_data(); + + pre_local = ProtoToTransform(data.pre_local()); + local = ProtoToTransform(data.local()); + post_local = ProtoToTransform(data.post_local()); + + to_parent = ProtoToTransform(data.to_parent()); + + to_target = ProtoToTransform(data.to_target()); + from_target = ProtoToTransform(data.from_target()); + + to_screen = ProtoToTransform(data.to_screen()); + from_screen = ProtoToTransform(data.from_screen()); + + target_id = data.target_id(); + content_target_id = data.content_target_id(); + source_node_id = data.source_node_id(); + + needs_local_transform_update = data.needs_local_transform_update(); + + is_invertible = data.is_invertible(); + ancestors_are_invertible = data.ancestors_are_invertible(); + + is_animated = data.is_animated(); + to_screen_is_animated = data.to_screen_is_animated(); + has_only_translation_animations = data.has_only_translation_animations(); + to_screen_has_scale_animation = data.to_screen_has_scale_animation(); + + flattens_inherited_transform = data.flattens_inherited_transform(); + node_and_ancestors_are_flat = data.node_and_ancestors_are_flat(); + + node_and_ancestors_have_only_integer_translation = + data.node_and_ancestors_have_only_integer_translation(); + scrolls = data.scrolls(); + needs_sublayer_scale = data.needs_sublayer_scale(); + + affected_by_inner_viewport_bounds_delta_x = + data.affected_by_inner_viewport_bounds_delta_x(); + affected_by_inner_viewport_bounds_delta_y = + data.affected_by_inner_viewport_bounds_delta_y(); + affected_by_outer_viewport_bounds_delta_x = + data.affected_by_outer_viewport_bounds_delta_x(); + affected_by_outer_viewport_bounds_delta_y = + data.affected_by_outer_viewport_bounds_delta_y(); + + in_subtree_of_page_scale_layer = data.in_subtree_of_page_scale_layer(); + post_local_scale_factor = data.post_local_scale_factor(); + local_maximum_animation_target_scale = + data.local_maximum_animation_target_scale(); + local_starting_animation_scale = data.local_starting_animation_scale(); + combined_maximum_animation_target_scale = + data.combined_maximum_animation_target_scale(); + combined_starting_animation_scale = data.combined_starting_animation_scale(); + + sublayer_scale = ProtoToVector2dF(data.sublayer_scale()); + scroll_offset = ProtoToScrollOffset(data.scroll_offset()); + scroll_snap = ProtoToVector2dF(data.scroll_snap()); + source_offset = ProtoToVector2dF(data.source_offset()); + source_to_parent = ProtoToVector2dF(data.source_to_parent()); +} + ClipNodeData::ClipNodeData() : transform_id(-1), target_id(-1), @@ -112,6 +350,63 @@ ClipNodeData::ClipNodeData() layers_are_clipped_when_surfaces_disabled(false), resets_clip(false) {} +bool ClipNodeData::operator==(const ClipNodeData& other) const { + return clip == other.clip && + combined_clip_in_target_space == other.combined_clip_in_target_space && + clip_in_target_space == other.clip_in_target_space && + transform_id == other.transform_id && target_id == other.target_id && + applies_local_clip == other.applies_local_clip && + layer_clipping_uses_only_local_clip == + other.layer_clipping_uses_only_local_clip && + target_is_clipped == other.target_is_clipped && + layers_are_clipped == other.layers_are_clipped && + layers_are_clipped_when_surfaces_disabled == + other.layers_are_clipped_when_surfaces_disabled && + resets_clip == other.resets_clip; +} + +void ClipNodeData::ToProtobuf(proto::TreeNode* proto) const { + DCHECK(!proto->has_clip_node_data()); + proto::ClipNodeData* data = proto->mutable_clip_node_data(); + + RectFToProto(clip, data->mutable_clip()); + RectFToProto(combined_clip_in_target_space, + data->mutable_combined_clip_in_target_space()); + RectFToProto(clip_in_target_space, data->mutable_clip_in_target_space()); + + data->set_transform_id(transform_id); + data->set_target_id(target_id); + data->set_applies_local_clip(applies_local_clip); + data->set_layer_clipping_uses_only_local_clip( + layer_clipping_uses_only_local_clip); + data->set_target_is_clipped(target_is_clipped); + data->set_layers_are_clipped(layers_are_clipped); + data->set_layers_are_clipped_when_surfaces_disabled( + layers_are_clipped_when_surfaces_disabled); + data->set_resets_clip(resets_clip); +} + +void ClipNodeData::FromProtobuf(const proto::TreeNode& proto) { + DCHECK(proto.has_clip_node_data()); + const proto::ClipNodeData& data = proto.clip_node_data(); + + clip = ProtoToRectF(data.clip()); + combined_clip_in_target_space = + ProtoToRectF(data.combined_clip_in_target_space()); + clip_in_target_space = ProtoToRectF(data.clip_in_target_space()); + + transform_id = data.transform_id(); + target_id = data.target_id(); + applies_local_clip = data.applies_local_clip(); + layer_clipping_uses_only_local_clip = + data.layer_clipping_uses_only_local_clip(); + target_is_clipped = data.target_is_clipped(); + layers_are_clipped = data.layers_are_clipped(); + layers_are_clipped_when_surfaces_disabled = + data.layers_are_clipped_when_surfaces_disabled(); + resets_clip = data.resets_clip(); +} + EffectNodeData::EffectNodeData() : opacity(1.f), screen_space_opacity(1.f), @@ -119,6 +414,34 @@ EffectNodeData::EffectNodeData() transform_id(0), clip_id(0) {} +bool EffectNodeData::operator==(const EffectNodeData& other) const { + return opacity == other.opacity && + screen_space_opacity == other.screen_space_opacity && + has_render_surface == other.has_render_surface && + transform_id == other.transform_id && clip_id == other.clip_id; +} + +void EffectNodeData::ToProtobuf(proto::TreeNode* proto) const { + DCHECK(!proto->has_effect_node_data()); + proto::EffectNodeData* data = proto->mutable_effect_node_data(); + data->set_opacity(opacity); + data->set_screen_space_opacity(screen_space_opacity); + data->set_has_render_surface(has_render_surface); + data->set_transform_id(transform_id); + data->set_clip_id(clip_id); +} + +void EffectNodeData::FromProtobuf(const proto::TreeNode& proto) { + DCHECK(proto.has_effect_node_data()); + const proto::EffectNodeData& data = proto.effect_node_data(); + + opacity = data.opacity(); + screen_space_opacity = data.screen_space_opacity(); + has_render_surface = data.has_render_surface(); + transform_id = data.transform_id(); + clip_id = data.clip_id(); +} + void TransformTree::clear() { PropertyTree<TransformNode>::clear(); @@ -624,6 +947,78 @@ bool TransformTree::HasNodesAffectedByOuterViewportBoundsDelta() const { return !nodes_affected_by_outer_viewport_bounds_delta_.empty(); } +bool TransformTree::operator==(const TransformTree& other) const { + return PropertyTree::operator==(other) && + source_to_parent_updates_allowed_ == + other.source_to_parent_updates_allowed() && + page_scale_factor_ == other.page_scale_factor() && + device_scale_factor_ == other.device_scale_factor() && + device_transform_scale_factor_ == + other.device_transform_scale_factor() && + inner_viewport_bounds_delta_ == other.inner_viewport_bounds_delta() && + outer_viewport_bounds_delta_ == other.outer_viewport_bounds_delta() && + nodes_affected_by_inner_viewport_bounds_delta_ == + other.nodes_affected_by_inner_viewport_bounds_delta() && + nodes_affected_by_outer_viewport_bounds_delta_ == + other.nodes_affected_by_outer_viewport_bounds_delta(); +} + +void TransformTree::ToProtobuf(proto::PropertyTree* proto) const { + DCHECK(!proto->has_property_type()); + proto->set_property_type(proto::PropertyTree::Transform); + + PropertyTree::ToProtobuf(proto); + proto::TransformTreeData* data = proto->mutable_transform_tree_data(); + + data->set_source_to_parent_updates_allowed(source_to_parent_updates_allowed_); + data->set_page_scale_factor(page_scale_factor_); + data->set_device_scale_factor(device_scale_factor_); + data->set_device_transform_scale_factor(device_transform_scale_factor_); + + Vector2dFToProto(inner_viewport_bounds_delta_, + data->mutable_inner_viewport_bounds_delta()); + Vector2dFToProto(outer_viewport_bounds_delta_, + data->mutable_outer_viewport_bounds_delta()); + + for (auto i : nodes_affected_by_inner_viewport_bounds_delta_) + data->add_nodes_affected_by_inner_viewport_bounds_delta(i); + + for (auto i : nodes_affected_by_outer_viewport_bounds_delta_) + data->add_nodes_affected_by_outer_viewport_bounds_delta(i); +} + +void TransformTree::FromProtobuf(const proto::PropertyTree& proto) { + DCHECK(proto.has_property_type()); + DCHECK_EQ(proto.property_type(), proto::PropertyTree::Transform); + + PropertyTree::FromProtobuf(proto); + const proto::TransformTreeData& data = proto.transform_tree_data(); + + source_to_parent_updates_allowed_ = data.source_to_parent_updates_allowed(); + page_scale_factor_ = data.page_scale_factor(); + device_scale_factor_ = data.device_scale_factor(); + device_transform_scale_factor_ = data.device_transform_scale_factor(); + + inner_viewport_bounds_delta_ = + ProtoToVector2dF(data.inner_viewport_bounds_delta()); + outer_viewport_bounds_delta_ = + ProtoToVector2dF(data.outer_viewport_bounds_delta()); + + DCHECK(nodes_affected_by_inner_viewport_bounds_delta_.empty()); + for (int i = 0; i < data.nodes_affected_by_inner_viewport_bounds_delta_size(); + ++i) { + nodes_affected_by_inner_viewport_bounds_delta_.push_back( + data.nodes_affected_by_inner_viewport_bounds_delta(i)); + } + + DCHECK(nodes_affected_by_outer_viewport_bounds_delta_.empty()); + for (int i = 0; i < data.nodes_affected_by_outer_viewport_bounds_delta_size(); + ++i) { + nodes_affected_by_outer_viewport_bounds_delta_.push_back( + data.nodes_affected_by_outer_viewport_bounds_delta(i)); + } +} + void EffectTree::UpdateOpacities(int id) { EffectNode* node = Node(id); node->data.screen_space_opacity = node->data.opacity; @@ -660,9 +1055,78 @@ gfx::RectF ClipTree::ViewportClip() { return Node(1)->data.clip; } +bool ClipTree::operator==(const ClipTree& other) const { + return PropertyTree::operator==(other); +} + +void ClipTree::ToProtobuf(proto::PropertyTree* proto) const { + DCHECK(!proto->has_property_type()); + proto->set_property_type(proto::PropertyTree::Clip); + + PropertyTree::ToProtobuf(proto); +} + +void ClipTree::FromProtobuf(const proto::PropertyTree& proto) { + DCHECK(proto.has_property_type()); + DCHECK_EQ(proto.property_type(), proto::PropertyTree::Clip); + + PropertyTree::FromProtobuf(proto); +} + +bool EffectTree::operator==(const EffectTree& other) const { + return PropertyTree::operator==(other); +} + +void EffectTree::ToProtobuf(proto::PropertyTree* proto) const { + DCHECK(!proto->has_property_type()); + proto->set_property_type(proto::PropertyTree::Effect); + + PropertyTree::ToProtobuf(proto); +} + +void EffectTree::FromProtobuf(const proto::PropertyTree& proto) { + DCHECK(proto.has_property_type()); + DCHECK_EQ(proto.property_type(), proto::PropertyTree::Effect); + + PropertyTree::FromProtobuf(proto); +} + PropertyTrees::PropertyTrees() : needs_rebuild(true), non_root_surfaces_enabled(true), sequence_number(0) {} +bool PropertyTrees::operator==(const PropertyTrees& other) const { + return transform_tree == other.transform_tree && + effect_tree == other.effect_tree && clip_tree == other.clip_tree && + needs_rebuild == other.needs_rebuild && + non_root_surfaces_enabled == other.non_root_surfaces_enabled && + sequence_number == other.sequence_number; +} + +void PropertyTrees::ToProtobuf(proto::PropertyTrees* proto) const { + // TODO(khushalsagar): Add support for sending diffs when serializaing + // property trees. See crbug/555370. + transform_tree.ToProtobuf(proto->mutable_transform_tree()); + effect_tree.ToProtobuf(proto->mutable_effect_tree()); + clip_tree.ToProtobuf(proto->mutable_clip_tree()); + proto->set_needs_rebuild(needs_rebuild); + proto->set_non_root_surfaces_enabled(non_root_surfaces_enabled); + + // TODO(khushalsagar): Consider using the sequence number to decide if + // property trees need to be serialized again for a commit. See crbug/555370. + proto->set_sequence_number(sequence_number); +} + +// static +void PropertyTrees::FromProtobuf(const proto::PropertyTrees& proto) { + transform_tree.FromProtobuf(proto.transform_tree()); + effect_tree.FromProtobuf(proto.effect_tree()); + clip_tree.FromProtobuf(proto.clip_tree()); + + needs_rebuild = proto.needs_rebuild(); + non_root_surfaces_enabled = proto.non_root_surfaces_enabled(); + sequence_number = proto.sequence_number(); +} + } // namespace cc diff --git a/cc/trees/property_tree.h b/cc/trees/property_tree.h index 6f26ef6..4fbcc43 100644 --- a/cc/trees/property_tree.h +++ b/cc/trees/property_tree.h @@ -15,6 +15,23 @@ namespace cc { +namespace proto { +class ClipNodeData; +class EffectNodeData; +class PropertyTree; +class PropertyTrees; +class TranformNodeData; +class TransformTreeData; +class TreeNode; +} + +// ------------------------------*IMPORTANT*--------------------------------- +// Each class declared here has a corresponding proto defined in +// cc/proto/property_tree.proto. When making any changes to a class structure +// including addition/deletion/updation of a field, please also make the +// change to its proto and the ToProtobuf and FromProtobuf methods for that +// class. + template <typename T> struct CC_EXPORT TreeNode { TreeNode() : id(-1), parent_id(-1), owner_id(-1), data() {} @@ -22,6 +39,11 @@ struct CC_EXPORT TreeNode { int parent_id; int owner_id; T data; + + bool operator==(const TreeNode<T>& other) const; + + void ToProtobuf(proto::TreeNode* proto) const; + void FromProtobuf(const proto::TreeNode& proto); }; struct CC_EXPORT TransformNodeData { @@ -141,6 +163,8 @@ struct CC_EXPORT TransformNodeData { gfx::Vector2dF source_offset; gfx::Vector2dF source_to_parent; + bool operator==(const TransformNodeData& other) const; + void set_to_parent(const gfx::Transform& transform) { to_parent = transform; is_invertible = to_parent.IsInvertible(); @@ -150,6 +174,9 @@ struct CC_EXPORT TransformNodeData { void update_post_local_transform(const gfx::PointF& position, const gfx::Point3F& transform_origin); + + void ToProtobuf(proto::TreeNode* proto) const; + void FromProtobuf(const proto::TreeNode& proto); }; typedef TreeNode<TransformNodeData> TransformNode; @@ -198,6 +225,11 @@ struct CC_EXPORT ClipNodeData { // Nodes that correspond to unclipped surfaces disregard ancestor clips. bool resets_clip : 1; + + bool operator==(const ClipNodeData& other) const; + + void ToProtobuf(proto::TreeNode* proto) const; + void FromProtobuf(const proto::TreeNode& proto); }; typedef TreeNode<ClipNodeData> ClipNode; @@ -211,6 +243,11 @@ struct CC_EXPORT EffectNodeData { bool has_render_surface; int transform_id; int clip_id; + + bool operator==(const EffectNodeData& other) const; + + void ToProtobuf(proto::TreeNode* proto) const; + void FromProtobuf(const proto::TreeNode& proto); }; typedef TreeNode<EffectNodeData> EffectNode; @@ -221,6 +258,8 @@ class CC_EXPORT PropertyTree { PropertyTree(); virtual ~PropertyTree(); + bool operator==(const PropertyTree<T>& other) const; + int Insert(const T& tree_node, int parent_id); T* Node(int i) { @@ -248,8 +287,13 @@ class CC_EXPORT PropertyTree { void set_needs_update(bool needs_update) { needs_update_ = needs_update; } bool needs_update() const { return needs_update_; } + const std::vector<T>& nodes() const { return nodes_; } + int next_available_id() const { return static_cast<int>(size()); } + void ToProtobuf(proto::PropertyTree* proto) const; + void FromProtobuf(const proto::PropertyTree& proto); + private: // Copy and assign are permitted. This is how we do tree sync. std::vector<T> nodes_; @@ -262,6 +306,8 @@ class CC_EXPORT TransformTree final : public PropertyTree<TransformNode> { TransformTree(); ~TransformTree() override; + bool operator==(const TransformTree& other) const; + void clear() override; // Computes the change of basis transform from node |source_id| to |dest_id|. @@ -331,6 +377,9 @@ class CC_EXPORT TransformTree final : public PropertyTree<TransformNode> { void SetDeviceTransform(const gfx::Transform& transform, gfx::PointF root_position); void SetDeviceTransformScaleFactor(const gfx::Transform& transform); + float device_transform_scale_factor() const { + return device_transform_scale_factor_; + } void SetInnerViewportBoundsDelta(gfx::Vector2dF bounds_delta); gfx::Vector2dF inner_viewport_bounds_delta() const { @@ -348,6 +397,18 @@ class CC_EXPORT TransformTree final : public PropertyTree<TransformNode> { bool HasNodesAffectedByInnerViewportBoundsDelta() const; bool HasNodesAffectedByOuterViewportBoundsDelta() const; + const std::vector<int>& nodes_affected_by_inner_viewport_bounds_delta() + const { + return nodes_affected_by_inner_viewport_bounds_delta_; + } + const std::vector<int>& nodes_affected_by_outer_viewport_bounds_delta() + const { + return nodes_affected_by_outer_viewport_bounds_delta_; + } + + void ToProtobuf(proto::PropertyTree* proto) const; + void FromProtobuf(const proto::PropertyTree& proto); + private: // Returns true iff the node at |desc_id| is a descendant of the node at // |anc_id|. @@ -398,19 +459,34 @@ class CC_EXPORT TransformTree final : public PropertyTree<TransformNode> { class CC_EXPORT ClipTree final : public PropertyTree<ClipNode> { public: + bool operator==(const ClipTree& other) const; + void SetViewportClip(gfx::RectF viewport_rect); gfx::RectF ViewportClip(); + + void ToProtobuf(proto::PropertyTree* proto) const; + void FromProtobuf(const proto::PropertyTree& proto); }; class CC_EXPORT EffectTree final : public PropertyTree<EffectNode> { public: + bool operator==(const EffectTree& other) const; + void UpdateOpacities(int id); + + void ToProtobuf(proto::PropertyTree* proto) const; + void FromProtobuf(const proto::PropertyTree& proto); }; class CC_EXPORT PropertyTrees final { public: PropertyTrees(); + bool operator==(const PropertyTrees& other) const; + + void ToProtobuf(proto::PropertyTrees* proto) const; + void FromProtobuf(const proto::PropertyTrees& proto); + TransformTree transform_tree; EffectTree effect_tree; ClipTree clip_tree; diff --git a/cc/trees/property_tree_unittest.cc b/cc/trees/property_tree_unittest.cc index e72a9ad..e9b8c2d 100644 --- a/cc/trees/property_tree_unittest.cc +++ b/cc/trees/property_tree_unittest.cc @@ -4,584 +4,977 @@ #include "cc/trees/property_tree.h" +#include "cc/proto/property_tree.pb.h" #include "cc/test/geometry_test_utils.h" #include "cc/trees/draw_property_utils.h" #include "testing/gtest/include/gtest/gtest.h" namespace cc { - -TEST(PropertyTreeTest, ComputeTransformRoot) { - TransformTree tree; - TransformNode& root = *tree.Node(0); - root.data.local.Translate(2, 2); - root.data.target_id = 0; - tree.UpdateTransforms(0); - - gfx::Transform expected; - gfx::Transform transform; - bool success = tree.ComputeTransform(0, 0, &transform); - EXPECT_TRUE(success); - EXPECT_TRANSFORMATION_MATRIX_EQ(expected, transform); - - transform.MakeIdentity(); - expected.Translate(2, 2); - success = tree.ComputeTransform(0, -1, &transform); - EXPECT_TRUE(success); - EXPECT_TRANSFORMATION_MATRIX_EQ(expected, transform); - - transform.MakeIdentity(); - expected.MakeIdentity(); - expected.Translate(-2, -2); - success = tree.ComputeTransform(-1, 0, &transform); - EXPECT_TRUE(success); - EXPECT_TRANSFORMATION_MATRIX_EQ(expected, transform); +namespace { + +TEST(PropertyTreeSerializationTest, TransformNodeDataSerialization) { + TransformNodeData original; + original.pre_local.Translate3d(1.f, 2.f, 3.f); + original.local.Translate3d(3.f, 1.f, 5.f); + original.post_local.Translate3d(1.f, 8.f, 3.f); + original.to_parent.Translate3d(3.2f, 2.f, 3.f); + original.to_target.Translate3d(2.6f, 2.f, 3.f); + original.from_target.Translate3d(4.3f, 2.f, 3.f); + original.to_screen.Translate3d(7.2f, 2.f, 4.5f); + original.from_screen.Translate3d(2.f, 2.f, 7.f); + original.target_id = 3; + original.content_target_id = 4; + original.source_node_id = 5; + original.needs_local_transform_update = false; + original.is_invertible = false; + original.ancestors_are_invertible = false; + original.is_animated = false; + original.to_screen_is_animated = false; + original.has_only_translation_animations = false; + original.to_screen_has_scale_animation = false; + original.flattens_inherited_transform = false; + original.node_and_ancestors_are_flat = false; + original.node_and_ancestors_have_only_integer_translation = false; + original.scrolls = false; + original.needs_sublayer_scale = false; + original.affected_by_inner_viewport_bounds_delta_x = false; + original.affected_by_inner_viewport_bounds_delta_y = false; + original.affected_by_outer_viewport_bounds_delta_x = false; + original.affected_by_outer_viewport_bounds_delta_y = false; + original.in_subtree_of_page_scale_layer = false; + original.post_local_scale_factor = 0.5f; + original.local_maximum_animation_target_scale = 0.6f; + original.local_starting_animation_scale = 0.7f; + original.combined_maximum_animation_target_scale = 0.8f; + original.combined_starting_animation_scale = 0.9f; + original.sublayer_scale = gfx::Vector2dF(0.5f, 0.5f); + original.scroll_offset = gfx::ScrollOffset(1.5f, 1.5f); + original.scroll_snap = gfx::Vector2dF(0.4f, 0.4f); + original.source_offset = gfx::Vector2dF(2.5f, 2.4f); + original.source_to_parent = gfx::Vector2dF(3.2f, 3.2f); + + proto::TreeNode proto; + original.ToProtobuf(&proto); + TransformNodeData result; + result.FromProtobuf(proto); + + EXPECT_EQ(original, result); } -TEST(PropertyTreeTest, ComputeTransformChild) { - TransformTree tree; - TransformNode& root = *tree.Node(0); - root.data.local.Translate(2, 2); - root.data.target_id = 0; - tree.UpdateTransforms(0); - - TransformNode child; - child.data.local.Translate(3, 3); - child.data.target_id = 0; - child.data.source_node_id = 0; - - tree.Insert(child, 0); - tree.UpdateTransforms(1); - - gfx::Transform expected; - gfx::Transform transform; - - expected.Translate(3, 3); - bool success = tree.ComputeTransform(1, 0, &transform); - EXPECT_TRUE(success); - EXPECT_TRANSFORMATION_MATRIX_EQ(expected, transform); - - transform.MakeIdentity(); - expected.MakeIdentity(); - expected.Translate(-3, -3); - success = tree.ComputeTransform(0, 1, &transform); - EXPECT_TRUE(success); - EXPECT_TRANSFORMATION_MATRIX_EQ(expected, transform); - - transform.MakeIdentity(); - expected.MakeIdentity(); - expected.Translate(5, 5); - success = tree.ComputeTransform(1, -1, &transform); - EXPECT_TRUE(success); - EXPECT_TRANSFORMATION_MATRIX_EQ(expected, transform); - - transform.MakeIdentity(); - expected.MakeIdentity(); - expected.Translate(-5, -5); - success = tree.ComputeTransform(-1, 1, &transform); - EXPECT_TRUE(success); - EXPECT_TRANSFORMATION_MATRIX_EQ(expected, transform); -} +TEST(PropertyTreeSerializationTest, TransformNodeSerialization) { + TransformNode original; + original.id = 3; + original.parent_id = 2; + original.owner_id = 4; -TEST(PropertyTreeTest, ComputeTransformSibling) { - TransformTree tree; - TransformNode& root = *tree.Node(0); - root.data.local.Translate(2, 2); - root.data.target_id = 0; - tree.UpdateTransforms(0); - - TransformNode child; - child.data.local.Translate(3, 3); - child.data.source_node_id = 0; - child.data.target_id = 0; - - TransformNode sibling; - sibling.data.local.Translate(7, 7); - sibling.data.source_node_id = 0; - sibling.data.target_id = 0; - - tree.Insert(child, 0); - tree.Insert(sibling, 0); - - tree.UpdateTransforms(1); - tree.UpdateTransforms(2); - - gfx::Transform expected; - gfx::Transform transform; - - expected.Translate(4, 4); - bool success = tree.ComputeTransform(2, 1, &transform); - EXPECT_TRUE(success); - EXPECT_TRANSFORMATION_MATRIX_EQ(expected, transform); - - transform.MakeIdentity(); - expected.MakeIdentity(); - expected.Translate(-4, -4); - success = tree.ComputeTransform(1, 2, &transform); - EXPECT_TRUE(success); - EXPECT_TRANSFORMATION_MATRIX_EQ(expected, transform); -} + proto::TreeNode proto; + original.ToProtobuf(&proto); + TransformNode result; + result.FromProtobuf(proto); -TEST(PropertyTreeTest, ComputeTransformSiblingSingularAncestor) { - // In this test, we have the following tree: - // root - // + singular - // + child - // + sibling - // Since the lowest common ancestor of |child| and |sibling| has a singular - // transform, we cannot use screen space transforms to compute change of basis - // transforms between these nodes. - TransformTree tree; - TransformNode& root = *tree.Node(0); - root.data.local.Translate(2, 2); - root.data.target_id = 0; - tree.UpdateTransforms(0); - - TransformNode singular; - singular.data.local.matrix().set(2, 2, 0.0); - singular.data.source_node_id = 0; - singular.data.target_id = 0; - - TransformNode child; - child.data.local.Translate(3, 3); - child.data.source_node_id = 1; - child.data.target_id = 0; - - TransformNode sibling; - sibling.data.local.Translate(7, 7); - sibling.data.source_node_id = 1; - sibling.data.target_id = 0; - - tree.Insert(singular, 0); - tree.Insert(child, 1); - tree.Insert(sibling, 1); - - tree.UpdateTransforms(1); - tree.UpdateTransforms(2); - tree.UpdateTransforms(3); - - gfx::Transform expected; - gfx::Transform transform; - - expected.Translate(4, 4); - bool success = tree.ComputeTransform(3, 2, &transform); - EXPECT_TRUE(success); - EXPECT_TRANSFORMATION_MATRIX_EQ(expected, transform); - - transform.MakeIdentity(); - expected.MakeIdentity(); - expected.Translate(-4, -4); - success = tree.ComputeTransform(2, 3, &transform); - EXPECT_TRUE(success); - EXPECT_TRANSFORMATION_MATRIX_EQ(expected, transform); + EXPECT_EQ(original, result); } -TEST(PropertyTreeTest, TransformsWithFlattening) { - TransformTree tree; - - int grand_parent = tree.Insert(TransformNode(), 0); - tree.Node(grand_parent)->data.content_target_id = grand_parent; - tree.Node(grand_parent)->data.target_id = grand_parent; - tree.Node(grand_parent)->data.source_node_id = 0; - - gfx::Transform rotation_about_x; - rotation_about_x.RotateAboutXAxis(15); - - int parent = tree.Insert(TransformNode(), grand_parent); - tree.Node(parent)->data.needs_sublayer_scale = true; - tree.Node(parent)->data.target_id = grand_parent; - tree.Node(parent)->data.content_target_id = parent; - tree.Node(parent)->data.source_node_id = grand_parent; - tree.Node(parent)->data.local = rotation_about_x; - - int child = tree.Insert(TransformNode(), parent); - tree.Node(child)->data.target_id = parent; - tree.Node(child)->data.content_target_id = parent; - tree.Node(child)->data.source_node_id = parent; - tree.Node(child)->data.flattens_inherited_transform = true; - tree.Node(child)->data.local = rotation_about_x; - - int grand_child = tree.Insert(TransformNode(), child); - tree.Node(grand_child)->data.target_id = parent; - tree.Node(grand_child)->data.content_target_id = parent; - tree.Node(grand_child)->data.source_node_id = child; - tree.Node(grand_child)->data.flattens_inherited_transform = true; - tree.Node(grand_child)->data.local = rotation_about_x; - - tree.set_needs_update(true); - ComputeTransforms(&tree); - - gfx::Transform flattened_rotation_about_x = rotation_about_x; - flattened_rotation_about_x.FlattenTo2d(); - - EXPECT_TRANSFORMATION_MATRIX_EQ(rotation_about_x, - tree.Node(child)->data.to_target); - - EXPECT_TRANSFORMATION_MATRIX_EQ(flattened_rotation_about_x * rotation_about_x, - tree.Node(child)->data.to_screen); - - EXPECT_TRANSFORMATION_MATRIX_EQ(flattened_rotation_about_x * rotation_about_x, - tree.Node(grand_child)->data.to_target); - - EXPECT_TRANSFORMATION_MATRIX_EQ(flattened_rotation_about_x * - flattened_rotation_about_x * - rotation_about_x, - tree.Node(grand_child)->data.to_screen); - - gfx::Transform grand_child_to_child; - bool success = - tree.ComputeTransform(grand_child, child, &grand_child_to_child); - EXPECT_TRUE(success); - EXPECT_TRANSFORMATION_MATRIX_EQ(rotation_about_x, grand_child_to_child); - - // Remove flattening at grand_child, and recompute transforms. - tree.Node(grand_child)->data.flattens_inherited_transform = false; - tree.set_needs_update(true); - ComputeTransforms(&tree); - - EXPECT_TRANSFORMATION_MATRIX_EQ(rotation_about_x * rotation_about_x, - tree.Node(grand_child)->data.to_target); - - EXPECT_TRANSFORMATION_MATRIX_EQ( - flattened_rotation_about_x * rotation_about_x * rotation_about_x, - tree.Node(grand_child)->data.to_screen); - - success = tree.ComputeTransform(grand_child, child, &grand_child_to_child); - EXPECT_TRUE(success); - EXPECT_TRANSFORMATION_MATRIX_EQ(rotation_about_x, grand_child_to_child); +TEST(PropertyTreeSerializationTest, TransformTreeSerialization) { + TransformTree original; + TransformNode& root = *original.Node(0); + root.data.target_id = 3; + root.data.content_target_id = 4; + TransformNode second; + second.data.local.Translate3d(2.f, 2.f, 0.f); + second.data.source_node_id = 0; + second.data.target_id = 0; + TransformNode third; + third.data.scrolls = true; + third.data.source_node_id = 1; + third.data.target_id = 0; + + original.Insert(second, 0); + original.Insert(third, 1); + original.set_needs_update(true); + + original.set_page_scale_factor(0.5f); + original.set_device_scale_factor(0.6f); + gfx::Transform transform = + gfx::Transform(1.05f, 2.15f, 3.14f, 4.13f, 5.12f, 6.11f, 7.1f, 8.9f, 9.8f, + 10.7f, 11.6f, 12.5f, 13.4f, 14.3f, 15.2f, 16.1f); + original.SetDeviceTransformScaleFactor(transform); + original.SetInnerViewportBoundsDelta(gfx::Vector2dF(0.4f, 0.6f)); + original.SetOuterViewportBoundsDelta(gfx::Vector2dF(0.5f, 0.7f)); + original.AddNodeAffectedByInnerViewportBoundsDelta(0); + original.AddNodeAffectedByOuterViewportBoundsDelta(1); + + proto::PropertyTree proto; + original.ToProtobuf(&proto); + TransformTree result; + result.FromProtobuf(proto); + + EXPECT_EQ(original, result); } -TEST(PropertyTreeTest, MultiplicationOrder) { - TransformTree tree; - TransformNode& root = *tree.Node(0); - root.data.local.Translate(2, 2); - root.data.target_id = 0; - tree.UpdateTransforms(0); - - TransformNode child; - child.data.local.Scale(2, 2); - child.data.target_id = 0; - child.data.source_node_id = 0; - - tree.Insert(child, 0); - tree.UpdateTransforms(1); - - gfx::Transform expected; - expected.Translate(2, 2); - expected.Scale(2, 2); - - gfx::Transform transform; - gfx::Transform inverse; - - bool success = tree.ComputeTransform(1, -1, &transform); - EXPECT_TRUE(success); - EXPECT_TRANSFORMATION_MATRIX_EQ(expected, transform); - - success = tree.ComputeTransform(-1, 1, &inverse); - EXPECT_TRUE(success); - - transform = transform * inverse; - expected.MakeIdentity(); - EXPECT_TRANSFORMATION_MATRIX_EQ(expected, transform); +TEST(PropertyTreeSerializationTest, ClipNodeDataSerialization) { + ClipNodeData original; + original.clip = gfx::RectF(0.5f, 0.5f); + original.combined_clip_in_target_space = gfx::RectF(0.6f, 0.6f); + original.clip_in_target_space = gfx::RectF(0.7f, 0.7f); + original.transform_id = 2; + original.target_id = 3; + original.applies_local_clip = false; + original.layer_clipping_uses_only_local_clip = false; + original.target_is_clipped = false; + original.layers_are_clipped = false; + original.layers_are_clipped_when_surfaces_disabled = false; + original.resets_clip = false; + + proto::TreeNode proto; + original.ToProtobuf(&proto); + ClipNodeData result; + result.FromProtobuf(proto); + + EXPECT_EQ(original, result); } -TEST(PropertyTreeTest, ComputeTransformWithUninvertibleTransform) { - TransformTree tree; - TransformNode& root = *tree.Node(0); - root.data.target_id = 0; - tree.UpdateTransforms(0); - - TransformNode child; - child.data.local.Scale(0, 0); - child.data.target_id = 0; - child.data.source_node_id = 0; - - tree.Insert(child, 0); - tree.UpdateTransforms(1); - - gfx::Transform expected; - expected.Scale(0, 0); +TEST(PropertyTreeSerializationTest, ClipNodeSerialization) { + ClipNode original; + original.id = 3; + original.parent_id = 2; + original.owner_id = 4; - gfx::Transform transform; - gfx::Transform inverse; + proto::TreeNode proto; + original.ToProtobuf(&proto); + ClipNode result; + result.FromProtobuf(proto); - bool success = tree.ComputeTransform(1, 0, &transform); - EXPECT_TRUE(success); - EXPECT_TRANSFORMATION_MATRIX_EQ(expected, transform); - - // To compute this would require inverting the 0 matrix, so we cannot - // succeed. - success = tree.ComputeTransform(0, 1, &inverse); - EXPECT_FALSE(success); + EXPECT_EQ(original, result); } -TEST(PropertyTreeTest, ComputeTransformWithSublayerScale) { - TransformTree tree; - TransformNode& root = *tree.Node(0); - root.data.target_id = 0; - tree.UpdateTransforms(0); - - TransformNode grand_parent; - grand_parent.data.local.Scale(2.f, 2.f); - grand_parent.data.target_id = 0; - grand_parent.data.source_node_id = 0; - grand_parent.data.needs_sublayer_scale = true; - int grand_parent_id = tree.Insert(grand_parent, 0); - tree.UpdateTransforms(grand_parent_id); - - TransformNode parent; - parent.data.local.Translate(15.f, 15.f); - parent.data.target_id = grand_parent_id; - parent.data.source_node_id = grand_parent_id; - int parent_id = tree.Insert(parent, grand_parent_id); - tree.UpdateTransforms(parent_id); - - TransformNode child; - child.data.local.Scale(3.f, 3.f); - child.data.target_id = grand_parent_id; - child.data.source_node_id = parent_id; - int child_id = tree.Insert(child, parent_id); - tree.UpdateTransforms(child_id); - - TransformNode grand_child; - grand_child.data.local.Scale(5.f, 5.f); - grand_child.data.target_id = grand_parent_id; - grand_child.data.source_node_id = child_id; - grand_child.data.needs_sublayer_scale = true; - int grand_child_id = tree.Insert(grand_child, child_id); - tree.UpdateTransforms(grand_child_id); - - EXPECT_EQ(gfx::Vector2dF(2.f, 2.f), - tree.Node(grand_parent_id)->data.sublayer_scale); - EXPECT_EQ(gfx::Vector2dF(30.f, 30.f), - tree.Node(grand_child_id)->data.sublayer_scale); - - // Compute transform from grand_parent to grand_child. - gfx::Transform expected_transform_without_sublayer_scale; - expected_transform_without_sublayer_scale.Scale(1.f / 15.f, 1.f / 15.f); - expected_transform_without_sublayer_scale.Translate(-15.f, -15.f); - - gfx::Transform expected_transform_with_dest_sublayer_scale; - expected_transform_with_dest_sublayer_scale.Scale(30.f, 30.f); - expected_transform_with_dest_sublayer_scale.Scale(1.f / 15.f, 1.f / 15.f); - expected_transform_with_dest_sublayer_scale.Translate(-15.f, -15.f); - - gfx::Transform expected_transform_with_source_sublayer_scale; - expected_transform_with_source_sublayer_scale.Scale(1.f / 15.f, 1.f / 15.f); - expected_transform_with_source_sublayer_scale.Translate(-15.f, -15.f); - expected_transform_with_source_sublayer_scale.Scale(0.5f, 0.5f); - - gfx::Transform transform; - bool success = - tree.ComputeTransform(grand_parent_id, grand_child_id, &transform); - EXPECT_TRUE(success); - EXPECT_TRANSFORMATION_MATRIX_EQ(expected_transform_without_sublayer_scale, - transform); - - success = tree.ComputeTransformWithDestinationSublayerScale( - grand_parent_id, grand_child_id, &transform); - EXPECT_TRUE(success); - EXPECT_TRANSFORMATION_MATRIX_EQ(expected_transform_with_dest_sublayer_scale, - transform); - - success = tree.ComputeTransformWithSourceSublayerScale( - grand_parent_id, grand_child_id, &transform); - EXPECT_TRUE(success); - EXPECT_TRANSFORMATION_MATRIX_EQ(expected_transform_with_source_sublayer_scale, - transform); - - // Now compute transform from grand_child to grand_parent. - expected_transform_without_sublayer_scale.MakeIdentity(); - expected_transform_without_sublayer_scale.Translate(15.f, 15.f); - expected_transform_without_sublayer_scale.Scale(15.f, 15.f); - - expected_transform_with_dest_sublayer_scale.MakeIdentity(); - expected_transform_with_dest_sublayer_scale.Scale(2.f, 2.f); - expected_transform_with_dest_sublayer_scale.Translate(15.f, 15.f); - expected_transform_with_dest_sublayer_scale.Scale(15.f, 15.f); - - expected_transform_with_source_sublayer_scale.MakeIdentity(); - expected_transform_with_source_sublayer_scale.Translate(15.f, 15.f); - expected_transform_with_source_sublayer_scale.Scale(15.f, 15.f); - expected_transform_with_source_sublayer_scale.Scale(1.f / 30.f, 1.f / 30.f); - - success = tree.ComputeTransform(grand_child_id, grand_parent_id, &transform); - EXPECT_TRUE(success); - EXPECT_TRANSFORMATION_MATRIX_EQ(expected_transform_without_sublayer_scale, - transform); - - success = tree.ComputeTransformWithDestinationSublayerScale( - grand_child_id, grand_parent_id, &transform); - EXPECT_TRUE(success); - EXPECT_TRANSFORMATION_MATRIX_EQ(expected_transform_with_dest_sublayer_scale, - transform); - - success = tree.ComputeTransformWithSourceSublayerScale( - grand_child_id, grand_parent_id, &transform); - EXPECT_TRUE(success); - EXPECT_TRANSFORMATION_MATRIX_EQ(expected_transform_with_source_sublayer_scale, - transform); +TEST(PropertyTreeSerializationTest, ClipTreeSerialization) { + ClipTree original; + ClipNode& root = *original.Node(0); + root.data.transform_id = 2; + root.data.target_id = 1; + ClipNode second; + second.data.transform_id = 4; + second.data.applies_local_clip = true; + ClipNode third; + third.data.target_id = 3; + third.data.target_is_clipped = false; + + original.Insert(second, 0); + original.Insert(third, 1); + original.set_needs_update(true); + + proto::PropertyTree proto; + original.ToProtobuf(&proto); + ClipTree result; + result.FromProtobuf(proto); + + EXPECT_EQ(original, result); } -TEST(PropertyTreeTest, ComputeTransformToTargetWithZeroSublayerScale) { - TransformTree tree; - TransformNode& root = *tree.Node(0); - root.data.target_id = 0; - tree.UpdateTransforms(0); - - TransformNode grand_parent; - grand_parent.data.local.Scale(2.f, 0.f); - grand_parent.data.target_id = 0; - grand_parent.data.source_node_id = 0; - grand_parent.data.needs_sublayer_scale = true; - int grand_parent_id = tree.Insert(grand_parent, 0); - tree.Node(grand_parent_id)->data.content_target_id = grand_parent_id; - tree.UpdateTransforms(grand_parent_id); - - TransformNode parent; - parent.data.local.Translate(1.f, 1.f); - parent.data.target_id = grand_parent_id; - parent.data.content_target_id = grand_parent_id; - parent.data.source_node_id = grand_parent_id; - int parent_id = tree.Insert(parent, grand_parent_id); - tree.UpdateTransforms(parent_id); - - TransformNode child; - child.data.local.Translate(3.f, 4.f); - child.data.target_id = grand_parent_id; - child.data.content_target_id = grand_parent_id; - child.data.source_node_id = parent_id; - int child_id = tree.Insert(child, parent_id); - tree.UpdateTransforms(child_id); - - gfx::Transform expected_transform; - expected_transform.Translate(4.f, 5.f); - - gfx::Transform transform; - bool success = tree.ComputeTransform(child_id, grand_parent_id, &transform); - EXPECT_TRUE(success); - EXPECT_TRANSFORMATION_MATRIX_EQ(expected_transform, transform); - - tree.Node(grand_parent_id)->data.local.MakeIdentity(); - tree.Node(grand_parent_id)->data.local.Scale(0.f, 2.f); - tree.Node(grand_parent_id)->data.needs_local_transform_update = true; - tree.set_needs_update(true); - - ComputeTransforms(&tree); - - success = tree.ComputeTransform(child_id, grand_parent_id, &transform); - EXPECT_TRUE(success); - EXPECT_TRANSFORMATION_MATRIX_EQ(expected_transform, transform); - - tree.Node(grand_parent_id)->data.local.MakeIdentity(); - tree.Node(grand_parent_id)->data.local.Scale(0.f, 0.f); - tree.Node(grand_parent_id)->data.needs_local_transform_update = true; - tree.set_needs_update(true); - - ComputeTransforms(&tree); - - success = tree.ComputeTransform(child_id, grand_parent_id, &transform); - EXPECT_TRUE(success); - EXPECT_TRANSFORMATION_MATRIX_EQ(expected_transform, transform); -} +TEST(PropertyTreeSerializationTest, EffectNodeDataSerialization) { + EffectNodeData original; + original.opacity = 0.5f; + original.screen_space_opacity = 0.6f; + original.has_render_surface = false; + original.transform_id = 2; + original.clip_id = 3; -TEST(PropertyTreeTest, FlatteningWhenDestinationHasOnlyFlatAncestors) { - // This tests that flattening is performed correctly when - // destination and its ancestors are flat, but there are 3d transforms - // and flattening between the source and destination. - TransformTree tree; - - int parent = tree.Insert(TransformNode(), 0); - tree.Node(parent)->data.content_target_id = parent; - tree.Node(parent)->data.target_id = parent; - tree.Node(parent)->data.source_node_id = 0; - tree.Node(parent)->data.local.Translate(2, 2); - - gfx::Transform rotation_about_x; - rotation_about_x.RotateAboutXAxis(15); - - int child = tree.Insert(TransformNode(), parent); - tree.Node(child)->data.content_target_id = child; - tree.Node(child)->data.target_id = child; - tree.Node(child)->data.source_node_id = parent; - tree.Node(child)->data.local = rotation_about_x; - - - int grand_child = tree.Insert(TransformNode(), child); - tree.Node(grand_child)->data.content_target_id = grand_child; - tree.Node(grand_child)->data.target_id = grand_child; - tree.Node(grand_child)->data.source_node_id = child; - tree.Node(grand_child)->data.flattens_inherited_transform = true; - - tree.set_needs_update(true); - ComputeTransforms(&tree); - - gfx::Transform flattened_rotation_about_x = rotation_about_x; - flattened_rotation_about_x.FlattenTo2d(); - - gfx::Transform grand_child_to_parent; - bool success = - tree.ComputeTransform(grand_child, parent, &grand_child_to_parent); - EXPECT_TRUE(success); - EXPECT_TRANSFORMATION_MATRIX_EQ(flattened_rotation_about_x, - grand_child_to_parent); + proto::TreeNode proto; + original.ToProtobuf(&proto); + EffectNodeData result; + result.FromProtobuf(proto); + + EXPECT_EQ(original, result); } -TEST(PropertyTreeTest, ScreenSpaceOpacityUpdateTest) { - // This tests that screen space opacity is updated for the subtree when - // opacity of a node changes. - EffectTree tree; +TEST(PropertyTreeSerializationTest, EffectNodeSerialization) { + EffectNode original; + original.id = 3; + original.parent_id = 2; + original.owner_id = 4; - int parent = tree.Insert(EffectNode(), 0); - int child = tree.Insert(EffectNode(), parent); + proto::TreeNode proto; + original.ToProtobuf(&proto); + EffectNode result; + result.FromProtobuf(proto); - EXPECT_EQ(tree.Node(child)->data.screen_space_opacity, 1.f); - tree.Node(parent)->data.opacity = 0.5f; - tree.set_needs_update(true); - ComputeOpacities(&tree); - EXPECT_EQ(tree.Node(child)->data.screen_space_opacity, 0.5f); + EXPECT_EQ(original, result); +} - tree.Node(child)->data.opacity = 0.5f; - tree.set_needs_update(true); - ComputeOpacities(&tree); - EXPECT_EQ(tree.Node(child)->data.screen_space_opacity, 0.25f); +TEST(PropertyTreeSerializationTest, EffectTreeSerialization) { + EffectTree original; + EffectNode& root = *original.Node(0); + root.data.transform_id = 2; + root.data.clip_id = 1; + EffectNode second; + second.data.transform_id = 4; + second.data.opacity = true; + EffectNode third; + third.data.clip_id = 3; + third.data.has_render_surface = false; + + original.Insert(second, 0); + original.Insert(third, 1); + original.set_needs_update(true); + + proto::PropertyTree proto; + original.ToProtobuf(&proto); + EffectTree result; + result.FromProtobuf(proto); + + EXPECT_EQ(original, result); } -TEST(PropertyTreeTest, NonIntegerTranslationTest) { - // This tests that when a node has non-integer translation, the information - // is propagated to the subtree. - TransformTree tree; - - int parent = tree.Insert(TransformNode(), 0); - tree.Node(parent)->data.target_id = parent; - tree.Node(parent)->data.local.Translate(1.5f, 1.5f); - - int child = tree.Insert(TransformNode(), parent); - tree.Node(child)->data.target_id = parent; - tree.Node(child)->data.local.Translate(1, 1); - tree.set_needs_update(true); - ComputeTransforms(&tree); - EXPECT_FALSE( - tree.Node(parent)->data.node_and_ancestors_have_only_integer_translation); - EXPECT_FALSE( - tree.Node(child)->data.node_and_ancestors_have_only_integer_translation); - - tree.Node(parent)->data.local.Translate(0.5f, 0.5f); - tree.Node(child)->data.local.Translate(0.5f, 0.5f); - tree.set_needs_update(true); - ComputeTransforms(&tree); - EXPECT_TRUE( - tree.Node(parent)->data.node_and_ancestors_have_only_integer_translation); - EXPECT_FALSE( - tree.Node(child)->data.node_and_ancestors_have_only_integer_translation); - - tree.Node(child)->data.local.Translate(0.5f, 0.5f); - tree.Node(child)->data.target_id = child; - tree.set_needs_update(true); - ComputeTransforms(&tree); - EXPECT_TRUE( - tree.Node(parent)->data.node_and_ancestors_have_only_integer_translation); - EXPECT_TRUE( - tree.Node(child)->data.node_and_ancestors_have_only_integer_translation); +TEST(PropertyTreeSerializationTest, PropertyTrees) { + PropertyTrees original; + original.transform_tree.Insert(TransformNode(), 0); + original.transform_tree.Insert(TransformNode(), 1); + original.clip_tree.Insert(ClipNode(), 0); + original.clip_tree.Insert(ClipNode(), 1); + original.effect_tree.Insert(EffectNode(), 0); + original.effect_tree.Insert(EffectNode(), 1); + original.needs_rebuild = false; + original.non_root_surfaces_enabled = false; + original.sequence_number = 3; + + proto::PropertyTrees proto; + original.ToProtobuf(&proto); + PropertyTrees result; + result.FromProtobuf(proto); + + EXPECT_EQ(original, result); } +class PropertyTreeTest : public testing::Test { + public: + PropertyTreeTest() : test_serialization_(false) {} + + protected: + void RunTest(bool test_serialization) { + test_serialization_ = test_serialization; + StartTest(); + } + + virtual void StartTest() = 0; + + TransformTree TransformTreeForTest(const TransformTree& transform_tree) { + if (!test_serialization_) { + return transform_tree; + } + TransformTree new_tree; + proto::PropertyTree proto; + transform_tree.ToProtobuf(&proto); + new_tree.FromProtobuf(proto); + + EXPECT_EQ(transform_tree, new_tree); + return new_tree; + } + + EffectTree EffectTreeForTest(const EffectTree& effect_tree) { + if (!test_serialization_) { + return effect_tree; + } + EffectTree new_tree; + proto::PropertyTree proto; + effect_tree.ToProtobuf(&proto); + new_tree.FromProtobuf(proto); + + EXPECT_EQ(effect_tree, new_tree); + return new_tree; + } + + private: + bool test_serialization_; +}; + +#define DIRECT_PROPERTY_TREE_TEST_F(TEST_FIXTURE_NAME) \ + TEST_F(TEST_FIXTURE_NAME, RunDirect) { RunTest(false); } + +#define SERIALIZED_PROPERTY_TREE_TEST_F(TEST_FIXTURE_NAME) \ + TEST_F(TEST_FIXTURE_NAME, RunSerialized) { RunTest(true); } + +#define DIRECT_AND_SERIALIZED_PROPERTY_TREE_TEST_F(TEST_FIXTURE_NAME) \ + DIRECT_PROPERTY_TREE_TEST_F(TEST_FIXTURE_NAME); \ + SERIALIZED_PROPERTY_TREE_TEST_F(TEST_FIXTURE_NAME) + +class PropertyTreeTestComputeTransformRoot : public PropertyTreeTest { + protected: + void StartTest() override { + TransformTree tree; + TransformNode& root = *tree.Node(0); + root.data.local.Translate(2, 2); + root.data.target_id = 0; + tree = TransformTreeForTest(tree); + tree.UpdateTransforms(0); + + gfx::Transform expected; + gfx::Transform transform; + bool success = tree.ComputeTransform(0, 0, &transform); + EXPECT_TRUE(success); + EXPECT_TRANSFORMATION_MATRIX_EQ(expected, transform); + + transform.MakeIdentity(); + expected.Translate(2, 2); + success = tree.ComputeTransform(0, -1, &transform); + EXPECT_TRUE(success); + EXPECT_TRANSFORMATION_MATRIX_EQ(expected, transform); + + transform.MakeIdentity(); + expected.MakeIdentity(); + expected.Translate(-2, -2); + success = tree.ComputeTransform(-1, 0, &transform); + EXPECT_TRUE(success); + EXPECT_TRANSFORMATION_MATRIX_EQ(expected, transform); + } +}; + +DIRECT_AND_SERIALIZED_PROPERTY_TREE_TEST_F( + PropertyTreeTestComputeTransformRoot); + +class PropertyTreeTestComputeTransformChild : public PropertyTreeTest { + protected: + void StartTest() override { + TransformTree tree; + TransformNode& root = *tree.Node(0); + root.data.local.Translate(2, 2); + root.data.target_id = 0; + tree.UpdateTransforms(0); + + TransformNode child; + child.data.local.Translate(3, 3); + child.data.target_id = 0; + child.data.source_node_id = 0; + + tree.Insert(child, 0); + tree = TransformTreeForTest(tree); + tree.UpdateTransforms(1); + + gfx::Transform expected; + gfx::Transform transform; + + expected.Translate(3, 3); + bool success = tree.ComputeTransform(1, 0, &transform); + EXPECT_TRUE(success); + EXPECT_TRANSFORMATION_MATRIX_EQ(expected, transform); + + transform.MakeIdentity(); + expected.MakeIdentity(); + expected.Translate(-3, -3); + success = tree.ComputeTransform(0, 1, &transform); + EXPECT_TRUE(success); + EXPECT_TRANSFORMATION_MATRIX_EQ(expected, transform); + + transform.MakeIdentity(); + expected.MakeIdentity(); + expected.Translate(5, 5); + success = tree.ComputeTransform(1, -1, &transform); + EXPECT_TRUE(success); + EXPECT_TRANSFORMATION_MATRIX_EQ(expected, transform); + + transform.MakeIdentity(); + expected.MakeIdentity(); + expected.Translate(-5, -5); + success = tree.ComputeTransform(-1, 1, &transform); + EXPECT_TRUE(success); + EXPECT_TRANSFORMATION_MATRIX_EQ(expected, transform); + } +}; + +DIRECT_AND_SERIALIZED_PROPERTY_TREE_TEST_F( + PropertyTreeTestComputeTransformChild); + +class PropertyTreeTestComputeTransformSibling : public PropertyTreeTest { + protected: + void StartTest() override { + TransformTree tree; + TransformNode& root = *tree.Node(0); + root.data.local.Translate(2, 2); + root.data.target_id = 0; + tree.UpdateTransforms(0); + + TransformNode child; + child.data.local.Translate(3, 3); + child.data.source_node_id = 0; + child.data.target_id = 0; + + TransformNode sibling; + sibling.data.local.Translate(7, 7); + sibling.data.source_node_id = 0; + sibling.data.target_id = 0; + + tree.Insert(child, 0); + tree.Insert(sibling, 0); + + tree = TransformTreeForTest(tree); + + tree.UpdateTransforms(1); + tree.UpdateTransforms(2); + + gfx::Transform expected; + gfx::Transform transform; + + expected.Translate(4, 4); + bool success = tree.ComputeTransform(2, 1, &transform); + EXPECT_TRUE(success); + EXPECT_TRANSFORMATION_MATRIX_EQ(expected, transform); + + transform.MakeIdentity(); + expected.MakeIdentity(); + expected.Translate(-4, -4); + success = tree.ComputeTransform(1, 2, &transform); + EXPECT_TRUE(success); + EXPECT_TRANSFORMATION_MATRIX_EQ(expected, transform); + } +}; + +DIRECT_AND_SERIALIZED_PROPERTY_TREE_TEST_F( + PropertyTreeTestComputeTransformSibling); + +class PropertyTreeTestComputeTransformSiblingSingularAncestor + : public PropertyTreeTest { + protected: + void StartTest() override { + // In this test, we have the following tree: + // root + // + singular + // + child + // + sibling + // Since the lowest common ancestor of |child| and |sibling| has a singular + // transform, we cannot use screen space transforms to compute change of + // basis + // transforms between these nodes. + TransformTree tree; + TransformNode& root = *tree.Node(0); + root.data.local.Translate(2, 2); + root.data.target_id = 0; + tree.UpdateTransforms(0); + + TransformNode singular; + singular.data.local.matrix().set(2, 2, 0.0); + singular.data.source_node_id = 0; + singular.data.target_id = 0; + + TransformNode child; + child.data.local.Translate(3, 3); + child.data.source_node_id = 1; + child.data.target_id = 0; + + TransformNode sibling; + sibling.data.local.Translate(7, 7); + sibling.data.source_node_id = 1; + sibling.data.target_id = 0; + + tree.Insert(singular, 0); + tree.Insert(child, 1); + tree.Insert(sibling, 1); + + tree = TransformTreeForTest(tree); + + tree.UpdateTransforms(1); + tree.UpdateTransforms(2); + tree.UpdateTransforms(3); + + gfx::Transform expected; + gfx::Transform transform; + + expected.Translate(4, 4); + bool success = tree.ComputeTransform(3, 2, &transform); + EXPECT_TRUE(success); + EXPECT_TRANSFORMATION_MATRIX_EQ(expected, transform); + + transform.MakeIdentity(); + expected.MakeIdentity(); + expected.Translate(-4, -4); + success = tree.ComputeTransform(2, 3, &transform); + EXPECT_TRUE(success); + EXPECT_TRANSFORMATION_MATRIX_EQ(expected, transform); + } +}; + +DIRECT_AND_SERIALIZED_PROPERTY_TREE_TEST_F( + PropertyTreeTestComputeTransformSiblingSingularAncestor); + +class PropertyTreeTestTransformsWithFlattening : public PropertyTreeTest { + protected: + void StartTest() override { + TransformTree tree; + + int grand_parent = tree.Insert(TransformNode(), 0); + tree.Node(grand_parent)->data.content_target_id = grand_parent; + tree.Node(grand_parent)->data.target_id = grand_parent; + tree.Node(grand_parent)->data.source_node_id = 0; + + gfx::Transform rotation_about_x; + rotation_about_x.RotateAboutXAxis(15); + + int parent = tree.Insert(TransformNode(), grand_parent); + tree.Node(parent)->data.needs_sublayer_scale = true; + tree.Node(parent)->data.target_id = grand_parent; + tree.Node(parent)->data.content_target_id = parent; + tree.Node(parent)->data.source_node_id = grand_parent; + tree.Node(parent)->data.local = rotation_about_x; + + int child = tree.Insert(TransformNode(), parent); + tree.Node(child)->data.target_id = parent; + tree.Node(child)->data.content_target_id = parent; + tree.Node(child)->data.source_node_id = parent; + tree.Node(child)->data.flattens_inherited_transform = true; + tree.Node(child)->data.local = rotation_about_x; + + int grand_child = tree.Insert(TransformNode(), child); + tree.Node(grand_child)->data.target_id = parent; + tree.Node(grand_child)->data.content_target_id = parent; + tree.Node(grand_child)->data.source_node_id = child; + tree.Node(grand_child)->data.flattens_inherited_transform = true; + tree.Node(grand_child)->data.local = rotation_about_x; + + tree.set_needs_update(true); + tree = TransformTreeForTest(tree); + ComputeTransforms(&tree); + + gfx::Transform flattened_rotation_about_x = rotation_about_x; + flattened_rotation_about_x.FlattenTo2d(); + + EXPECT_TRANSFORMATION_MATRIX_EQ(rotation_about_x, + tree.Node(child)->data.to_target); + + EXPECT_TRANSFORMATION_MATRIX_EQ( + flattened_rotation_about_x * rotation_about_x, + tree.Node(child)->data.to_screen); + + EXPECT_TRANSFORMATION_MATRIX_EQ( + flattened_rotation_about_x * rotation_about_x, + tree.Node(grand_child)->data.to_target); + + EXPECT_TRANSFORMATION_MATRIX_EQ(flattened_rotation_about_x * + flattened_rotation_about_x * + rotation_about_x, + tree.Node(grand_child)->data.to_screen); + + gfx::Transform grand_child_to_child; + bool success = + tree.ComputeTransform(grand_child, child, &grand_child_to_child); + EXPECT_TRUE(success); + EXPECT_TRANSFORMATION_MATRIX_EQ(rotation_about_x, grand_child_to_child); + + // Remove flattening at grand_child, and recompute transforms. + tree.Node(grand_child)->data.flattens_inherited_transform = false; + tree.set_needs_update(true); + tree = TransformTreeForTest(tree); + ComputeTransforms(&tree); + + EXPECT_TRANSFORMATION_MATRIX_EQ(rotation_about_x * rotation_about_x, + tree.Node(grand_child)->data.to_target); + + EXPECT_TRANSFORMATION_MATRIX_EQ( + flattened_rotation_about_x * rotation_about_x * rotation_about_x, + tree.Node(grand_child)->data.to_screen); + + success = tree.ComputeTransform(grand_child, child, &grand_child_to_child); + EXPECT_TRUE(success); + EXPECT_TRANSFORMATION_MATRIX_EQ(rotation_about_x, grand_child_to_child); + } +}; + +DIRECT_AND_SERIALIZED_PROPERTY_TREE_TEST_F( + PropertyTreeTestTransformsWithFlattening); + +class PropertyTreeTestMultiplicationOrder : public PropertyTreeTest { + protected: + void StartTest() override { + TransformTree tree; + TransformNode& root = *tree.Node(0); + root.data.local.Translate(2, 2); + root.data.target_id = 0; + tree.UpdateTransforms(0); + + TransformNode child; + child.data.local.Scale(2, 2); + child.data.target_id = 0; + child.data.source_node_id = 0; + + tree.Insert(child, 0); + tree = TransformTreeForTest(tree); + tree.UpdateTransforms(1); + + gfx::Transform expected; + expected.Translate(2, 2); + expected.Scale(2, 2); + + gfx::Transform transform; + gfx::Transform inverse; + + bool success = tree.ComputeTransform(1, -1, &transform); + EXPECT_TRUE(success); + EXPECT_TRANSFORMATION_MATRIX_EQ(expected, transform); + + success = tree.ComputeTransform(-1, 1, &inverse); + EXPECT_TRUE(success); + + transform = transform * inverse; + expected.MakeIdentity(); + EXPECT_TRANSFORMATION_MATRIX_EQ(expected, transform); + } +}; + +DIRECT_AND_SERIALIZED_PROPERTY_TREE_TEST_F(PropertyTreeTestMultiplicationOrder); + +class PropertyTreeTestComputeTransformWithUninvertibleTransform + : public PropertyTreeTest { + protected: + void StartTest() override { + TransformTree tree; + TransformNode& root = *tree.Node(0); + root.data.target_id = 0; + tree.UpdateTransforms(0); + + TransformNode child; + child.data.local.Scale(0, 0); + child.data.target_id = 0; + child.data.source_node_id = 0; + + tree.Insert(child, 0); + tree = TransformTreeForTest(tree); + tree.UpdateTransforms(1); + + gfx::Transform expected; + expected.Scale(0, 0); + + gfx::Transform transform; + gfx::Transform inverse; + + bool success = tree.ComputeTransform(1, 0, &transform); + EXPECT_TRUE(success); + EXPECT_TRANSFORMATION_MATRIX_EQ(expected, transform); + + // To compute this would require inverting the 0 matrix, so we cannot + // succeed. + success = tree.ComputeTransform(0, 1, &inverse); + EXPECT_FALSE(success); + } +}; + +DIRECT_AND_SERIALIZED_PROPERTY_TREE_TEST_F( + PropertyTreeTestComputeTransformWithUninvertibleTransform); + +class PropertyTreeTestComputeTransformWithSublayerScale + : public PropertyTreeTest { + protected: + void StartTest() override { + TransformTree tree; + TransformNode& root = *tree.Node(0); + root.data.target_id = 0; + tree.UpdateTransforms(0); + + TransformNode grand_parent; + grand_parent.data.local.Scale(2.f, 2.f); + grand_parent.data.target_id = 0; + grand_parent.data.source_node_id = 0; + grand_parent.data.needs_sublayer_scale = true; + int grand_parent_id = tree.Insert(grand_parent, 0); + tree.UpdateTransforms(grand_parent_id); + + TransformNode parent; + parent.data.local.Translate(15.f, 15.f); + parent.data.target_id = grand_parent_id; + parent.data.source_node_id = grand_parent_id; + int parent_id = tree.Insert(parent, grand_parent_id); + tree.UpdateTransforms(parent_id); + + TransformNode child; + child.data.local.Scale(3.f, 3.f); + child.data.target_id = grand_parent_id; + child.data.source_node_id = parent_id; + int child_id = tree.Insert(child, parent_id); + tree.UpdateTransforms(child_id); + + TransformNode grand_child; + grand_child.data.local.Scale(5.f, 5.f); + grand_child.data.target_id = grand_parent_id; + grand_child.data.source_node_id = child_id; + grand_child.data.needs_sublayer_scale = true; + int grand_child_id = tree.Insert(grand_child, child_id); + tree = TransformTreeForTest(tree); + tree.UpdateTransforms(grand_child_id); + + EXPECT_EQ(gfx::Vector2dF(2.f, 2.f), + tree.Node(grand_parent_id)->data.sublayer_scale); + EXPECT_EQ(gfx::Vector2dF(30.f, 30.f), + tree.Node(grand_child_id)->data.sublayer_scale); + + // Compute transform from grand_parent to grand_child. + gfx::Transform expected_transform_without_sublayer_scale; + expected_transform_without_sublayer_scale.Scale(1.f / 15.f, 1.f / 15.f); + expected_transform_without_sublayer_scale.Translate(-15.f, -15.f); + + gfx::Transform expected_transform_with_dest_sublayer_scale; + expected_transform_with_dest_sublayer_scale.Scale(30.f, 30.f); + expected_transform_with_dest_sublayer_scale.Scale(1.f / 15.f, 1.f / 15.f); + expected_transform_with_dest_sublayer_scale.Translate(-15.f, -15.f); + + gfx::Transform expected_transform_with_source_sublayer_scale; + expected_transform_with_source_sublayer_scale.Scale(1.f / 15.f, 1.f / 15.f); + expected_transform_with_source_sublayer_scale.Translate(-15.f, -15.f); + expected_transform_with_source_sublayer_scale.Scale(0.5f, 0.5f); + + gfx::Transform transform; + bool success = + tree.ComputeTransform(grand_parent_id, grand_child_id, &transform); + EXPECT_TRUE(success); + EXPECT_TRANSFORMATION_MATRIX_EQ(expected_transform_without_sublayer_scale, + transform); + + success = tree.ComputeTransformWithDestinationSublayerScale( + grand_parent_id, grand_child_id, &transform); + EXPECT_TRUE(success); + EXPECT_TRANSFORMATION_MATRIX_EQ(expected_transform_with_dest_sublayer_scale, + transform); + + success = tree.ComputeTransformWithSourceSublayerScale( + grand_parent_id, grand_child_id, &transform); + EXPECT_TRUE(success); + EXPECT_TRANSFORMATION_MATRIX_EQ( + expected_transform_with_source_sublayer_scale, transform); + + // Now compute transform from grand_child to grand_parent. + expected_transform_without_sublayer_scale.MakeIdentity(); + expected_transform_without_sublayer_scale.Translate(15.f, 15.f); + expected_transform_without_sublayer_scale.Scale(15.f, 15.f); + + expected_transform_with_dest_sublayer_scale.MakeIdentity(); + expected_transform_with_dest_sublayer_scale.Scale(2.f, 2.f); + expected_transform_with_dest_sublayer_scale.Translate(15.f, 15.f); + expected_transform_with_dest_sublayer_scale.Scale(15.f, 15.f); + + expected_transform_with_source_sublayer_scale.MakeIdentity(); + expected_transform_with_source_sublayer_scale.Translate(15.f, 15.f); + expected_transform_with_source_sublayer_scale.Scale(15.f, 15.f); + expected_transform_with_source_sublayer_scale.Scale(1.f / 30.f, 1.f / 30.f); + + success = + tree.ComputeTransform(grand_child_id, grand_parent_id, &transform); + EXPECT_TRUE(success); + EXPECT_TRANSFORMATION_MATRIX_EQ(expected_transform_without_sublayer_scale, + transform); + + success = tree.ComputeTransformWithDestinationSublayerScale( + grand_child_id, grand_parent_id, &transform); + EXPECT_TRUE(success); + EXPECT_TRANSFORMATION_MATRIX_EQ(expected_transform_with_dest_sublayer_scale, + transform); + + success = tree.ComputeTransformWithSourceSublayerScale( + grand_child_id, grand_parent_id, &transform); + EXPECT_TRUE(success); + EXPECT_TRANSFORMATION_MATRIX_EQ( + expected_transform_with_source_sublayer_scale, transform); + } +}; + +DIRECT_AND_SERIALIZED_PROPERTY_TREE_TEST_F( + PropertyTreeTestComputeTransformWithSublayerScale); + +class PropertyTreeTestComputeTransformToTargetWithZeroSublayerScale + : public PropertyTreeTest { + protected: + void StartTest() override { + TransformTree tree; + TransformNode& root = *tree.Node(0); + root.data.target_id = 0; + tree.UpdateTransforms(0); + + TransformNode grand_parent; + grand_parent.data.local.Scale(2.f, 0.f); + grand_parent.data.target_id = 0; + grand_parent.data.source_node_id = 0; + grand_parent.data.needs_sublayer_scale = true; + int grand_parent_id = tree.Insert(grand_parent, 0); + tree.Node(grand_parent_id)->data.content_target_id = grand_parent_id; + tree.UpdateTransforms(grand_parent_id); + + TransformNode parent; + parent.data.local.Translate(1.f, 1.f); + parent.data.target_id = grand_parent_id; + parent.data.content_target_id = grand_parent_id; + parent.data.source_node_id = grand_parent_id; + int parent_id = tree.Insert(parent, grand_parent_id); + tree.UpdateTransforms(parent_id); + + TransformNode child; + child.data.local.Translate(3.f, 4.f); + child.data.target_id = grand_parent_id; + child.data.content_target_id = grand_parent_id; + child.data.source_node_id = parent_id; + int child_id = tree.Insert(child, parent_id); + tree = TransformTreeForTest(tree); + tree.UpdateTransforms(child_id); + + gfx::Transform expected_transform; + expected_transform.Translate(4.f, 5.f); + + gfx::Transform transform; + bool success = tree.ComputeTransform(child_id, grand_parent_id, &transform); + EXPECT_TRUE(success); + EXPECT_TRANSFORMATION_MATRIX_EQ(expected_transform, transform); + + tree.Node(grand_parent_id)->data.local.MakeIdentity(); + tree.Node(grand_parent_id)->data.local.Scale(0.f, 2.f); + tree.Node(grand_parent_id)->data.needs_local_transform_update = true; + tree.set_needs_update(true); + tree = TransformTreeForTest(tree); + + ComputeTransforms(&tree); + + success = tree.ComputeTransform(child_id, grand_parent_id, &transform); + EXPECT_TRUE(success); + EXPECT_TRANSFORMATION_MATRIX_EQ(expected_transform, transform); + + tree.Node(grand_parent_id)->data.local.MakeIdentity(); + tree.Node(grand_parent_id)->data.local.Scale(0.f, 0.f); + tree.Node(grand_parent_id)->data.needs_local_transform_update = true; + tree.set_needs_update(true); + tree = TransformTreeForTest(tree); + + ComputeTransforms(&tree); + + success = tree.ComputeTransform(child_id, grand_parent_id, &transform); + EXPECT_TRUE(success); + EXPECT_TRANSFORMATION_MATRIX_EQ(expected_transform, transform); + } +}; + +DIRECT_AND_SERIALIZED_PROPERTY_TREE_TEST_F( + PropertyTreeTestComputeTransformToTargetWithZeroSublayerScale); + +class PropertyTreeTestFlatteningWhenDestinationHasOnlyFlatAncestors + : public PropertyTreeTest { + protected: + void StartTest() override { + // This tests that flattening is performed correctly when + // destination and its ancestors are flat, but there are 3d transforms + // and flattening between the source and destination. + TransformTree tree; + + int parent = tree.Insert(TransformNode(), 0); + tree.Node(parent)->data.content_target_id = parent; + tree.Node(parent)->data.target_id = parent; + tree.Node(parent)->data.source_node_id = 0; + tree.Node(parent)->data.local.Translate(2, 2); + + gfx::Transform rotation_about_x; + rotation_about_x.RotateAboutXAxis(15); + + int child = tree.Insert(TransformNode(), parent); + tree.Node(child)->data.content_target_id = child; + tree.Node(child)->data.target_id = child; + tree.Node(child)->data.source_node_id = parent; + tree.Node(child)->data.local = rotation_about_x; + + int grand_child = tree.Insert(TransformNode(), child); + tree.Node(grand_child)->data.content_target_id = grand_child; + tree.Node(grand_child)->data.target_id = grand_child; + tree.Node(grand_child)->data.source_node_id = child; + tree.Node(grand_child)->data.flattens_inherited_transform = true; + + tree.set_needs_update(true); + tree = TransformTreeForTest(tree); + ComputeTransforms(&tree); + + gfx::Transform flattened_rotation_about_x = rotation_about_x; + flattened_rotation_about_x.FlattenTo2d(); + + gfx::Transform grand_child_to_parent; + bool success = + tree.ComputeTransform(grand_child, parent, &grand_child_to_parent); + EXPECT_TRUE(success); + EXPECT_TRANSFORMATION_MATRIX_EQ(flattened_rotation_about_x, + grand_child_to_parent); + } +}; + +DIRECT_AND_SERIALIZED_PROPERTY_TREE_TEST_F( + PropertyTreeTestFlatteningWhenDestinationHasOnlyFlatAncestors); + +class PropertyTreeTestScreenSpaceOpacityUpdateTest : public PropertyTreeTest { + protected: + void StartTest() override { + // This tests that screen space opacity is updated for the subtree when + // opacity of a node changes. + EffectTree tree; + + int parent = tree.Insert(EffectNode(), 0); + int child = tree.Insert(EffectNode(), parent); + tree = EffectTreeForTest(tree); + + EXPECT_EQ(tree.Node(child)->data.screen_space_opacity, 1.f); + tree.Node(parent)->data.opacity = 0.5f; + tree.set_needs_update(true); + tree = EffectTreeForTest(tree); + ComputeOpacities(&tree); + EXPECT_EQ(tree.Node(child)->data.screen_space_opacity, 0.5f); + + tree.Node(child)->data.opacity = 0.5f; + tree.set_needs_update(true); + tree = EffectTreeForTest(tree); + ComputeOpacities(&tree); + EXPECT_EQ(tree.Node(child)->data.screen_space_opacity, 0.25f); + } +}; + +DIRECT_AND_SERIALIZED_PROPERTY_TREE_TEST_F( + PropertyTreeTestScreenSpaceOpacityUpdateTest); + +class PropertyTreeTestNonIntegerTranslationTest : public PropertyTreeTest { + protected: + void StartTest() override { + // This tests that when a node has non-integer translation, the information + // is propagated to the subtree. + TransformTree tree; + + int parent = tree.Insert(TransformNode(), 0); + tree.Node(parent)->data.target_id = parent; + tree.Node(parent)->data.local.Translate(1.5f, 1.5f); + + int child = tree.Insert(TransformNode(), parent); + tree.Node(child)->data.target_id = parent; + tree.Node(child)->data.local.Translate(1, 1); + tree.set_needs_update(true); + tree = TransformTreeForTest(tree); + ComputeTransforms(&tree); + EXPECT_FALSE(tree.Node(parent) + ->data.node_and_ancestors_have_only_integer_translation); + EXPECT_FALSE(tree.Node(child) + ->data.node_and_ancestors_have_only_integer_translation); + + tree.Node(parent)->data.local.Translate(0.5f, 0.5f); + tree.Node(child)->data.local.Translate(0.5f, 0.5f); + tree.set_needs_update(true); + tree = TransformTreeForTest(tree); + ComputeTransforms(&tree); + EXPECT_TRUE(tree.Node(parent) + ->data.node_and_ancestors_have_only_integer_translation); + EXPECT_FALSE(tree.Node(child) + ->data.node_and_ancestors_have_only_integer_translation); + + tree.Node(child)->data.local.Translate(0.5f, 0.5f); + tree.Node(child)->data.target_id = child; + tree.set_needs_update(true); + tree = TransformTreeForTest(tree); + ComputeTransforms(&tree); + EXPECT_TRUE(tree.Node(parent) + ->data.node_and_ancestors_have_only_integer_translation); + EXPECT_TRUE(tree.Node(child) + ->data.node_and_ancestors_have_only_integer_translation); + } +}; + +DIRECT_AND_SERIALIZED_PROPERTY_TREE_TEST_F( + PropertyTreeTestNonIntegerTranslationTest); + +#undef DIRECT_AND_SERIALIZED_PROPERTY_TREE_TEST_F +#undef SERIALIZED_PROPERTY_TREE_TEST_F +#undef DIRECT_PROPERTY_TREE_TEST_F + +} // namespace } // namespace cc |