| // Copyright 2014 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "cc/trees/draw_property_utils.h" |
| |
| #include <vector> |
| |
| #include "cc/base/math_util.h" |
| #include "cc/layers/layer.h" |
| #include "cc/layers/layer_impl.h" |
| #include "cc/trees/layer_tree_impl.h" |
| #include "cc/trees/property_tree.h" |
| #include "cc/trees/property_tree_builder.h" |
| #include "ui/gfx/geometry/rect_conversions.h" |
| |
| namespace cc { |
| |
| namespace { |
| |
| template <typename LayerType> |
| void CalculateVisibleRects(const std::vector<LayerType*>& visible_layer_list, |
| const ClipTree& clip_tree, |
| const TransformTree& transform_tree) { |
| for (auto& layer : visible_layer_list) { |
| // TODO(ajuma): Compute content_scale rather than using it. Note that for |
| // PictureLayer and PictureImageLayers, content_bounds == bounds and |
| // content_scale_x == content_scale_y == 1.0, so once impl painting is on |
| // everywhere, this code will be unnecessary. |
| gfx::Size layer_bounds = layer->bounds(); |
| const bool has_clip = layer->clip_tree_index() > 0; |
| const TransformNode* transform_node = |
| transform_tree.Node(layer->transform_tree_index()); |
| if (has_clip) { |
| const ClipNode* clip_node = clip_tree.Node(layer->clip_tree_index()); |
| const TransformNode* clip_transform_node = |
| transform_tree.Node(clip_node->data.transform_id); |
| const bool target_is_root_surface = |
| transform_node->data.content_target_id == 1; |
| // When the target is the root surface, we need to include the root |
| // transform by walking up to the root of the transform tree. |
| const int target_id = |
| target_is_root_surface ? 0 : transform_node->data.content_target_id; |
| const TransformNode* target_node = transform_tree.Node(target_id); |
| |
| gfx::Transform content_to_target = transform_node->data.to_target; |
| |
| content_to_target.Translate(layer->offset_to_transform_parent().x(), |
| layer->offset_to_transform_parent().y()); |
| |
| gfx::Rect combined_clip_rect_in_target_space; |
| gfx::Rect clip_rect_in_target_space; |
| gfx::Transform clip_to_target; |
| bool success = true; |
| if (clip_transform_node->data.target_id == target_node->id) { |
| clip_to_target = clip_transform_node->data.to_target; |
| } else { |
| success = transform_tree.ComputeTransformWithDestinationSublayerScale( |
| clip_transform_node->id, target_node->id, &clip_to_target); |
| } |
| if (target_node->id > clip_node->data.transform_id) { |
| if (!success) { |
| DCHECK(target_node->data.to_screen_is_animated); |
| |
| // An animated singular transform may become non-singular during the |
| // animation, so we still need to compute a visible rect. In this |
| // situation, we treat the entire layer as visible. |
| layer->set_visible_rect_from_property_trees(gfx::Rect(layer_bounds)); |
| layer->set_clip_rect_in_target_space_from_property_trees( |
| gfx::ToEnclosingRect(clip_node->data.combined_clip)); |
| continue; |
| } |
| |
| combined_clip_rect_in_target_space = |
| gfx::ToEnclosingRect(MathUtil::ProjectClippedRect( |
| clip_to_target, clip_node->data.combined_clip)); |
| clip_rect_in_target_space = gfx::ToEnclosingRect( |
| MathUtil::ProjectClippedRect(clip_to_target, clip_node->data.clip)); |
| } else { |
| // Computing a transform to an ancestor should always succeed. |
| DCHECK(success); |
| combined_clip_rect_in_target_space = |
| gfx::ToEnclosingRect(MathUtil::MapClippedRect( |
| clip_to_target, clip_node->data.combined_clip)); |
| clip_rect_in_target_space = gfx::ToEnclosingRect( |
| MathUtil::MapClippedRect(clip_to_target, clip_node->data.clip)); |
| } |
| |
| if (clip_node->data.requires_tight_clip_rect) |
| layer->set_clip_rect_in_target_space_from_property_trees( |
| combined_clip_rect_in_target_space); |
| else |
| layer->set_clip_rect_in_target_space_from_property_trees( |
| clip_rect_in_target_space); |
| |
| gfx::Rect layer_content_rect = gfx::Rect(layer_bounds); |
| gfx::Rect layer_content_bounds_in_target_space = |
| MathUtil::MapEnclosingClippedRect(content_to_target, |
| layer_content_rect); |
| combined_clip_rect_in_target_space.Intersect( |
| layer_content_bounds_in_target_space); |
| clip_rect_in_target_space.Intersect(layer_content_bounds_in_target_space); |
| if (combined_clip_rect_in_target_space.IsEmpty()) { |
| layer->set_visible_rect_from_property_trees(gfx::Rect()); |
| continue; |
| } |
| |
| // If the layer is fully contained within the clip, treat it as fully |
| // visible. Since clip_rect_in_target_space has already been intersected |
| // with layer_content_bounds_in_target_space, the layer is fully contained |
| // within the clip iff these rects are equal. |
| if (combined_clip_rect_in_target_space == |
| layer_content_bounds_in_target_space) { |
| layer->set_visible_rect_from_property_trees(gfx::Rect(layer_bounds)); |
| continue; |
| } |
| |
| gfx::Transform target_to_content; |
| gfx::Transform target_to_layer; |
| |
| if (transform_node->data.ancestors_are_invertible) { |
| target_to_layer = transform_node->data.from_target; |
| success = true; |
| } else { |
| success = transform_tree.ComputeTransformWithSourceSublayerScale( |
| target_node->id, transform_node->id, &target_to_layer); |
| } |
| |
| if (!success) { |
| // An animated singular transform may become non-singular during the |
| // animation, so we still need to compute a visible rect. In this |
| // situation, we treat the entire layer as visible. |
| layer->set_visible_rect_from_property_trees(gfx::Rect(layer_bounds)); |
| continue; |
| } |
| |
| target_to_content.Translate(-layer->offset_to_transform_parent().x(), |
| -layer->offset_to_transform_parent().y()); |
| target_to_content.PreconcatTransform(target_to_layer); |
| |
| gfx::Rect visible_rect = MathUtil::ProjectEnclosingClippedRect( |
| target_to_content, combined_clip_rect_in_target_space); |
| |
| visible_rect.Intersect(gfx::Rect(layer_bounds)); |
| |
| layer->set_visible_rect_from_property_trees(visible_rect); |
| } else { |
| layer->set_visible_rect_from_property_trees(gfx::Rect(layer_bounds)); |
| layer->set_clip_rect_in_target_space_from_property_trees( |
| gfx::Rect(layer_bounds)); |
| } |
| } |
| } |
| |
| template <typename LayerType> |
| static bool IsRootLayerOfNewRenderingContext(LayerType* layer) { |
| if (layer->parent()) |
| return !layer->parent()->Is3dSorted() && layer->Is3dSorted(); |
| return layer->Is3dSorted(); |
| } |
| |
| template <typename LayerType> |
| static inline bool LayerIsInExisting3DRenderingContext(LayerType* layer) { |
| return layer->Is3dSorted() && layer->parent() && |
| layer->parent()->Is3dSorted() && |
| layer->parent()->sorting_context_id() == layer->sorting_context_id(); |
| } |
| |
| template <typename LayerType> |
| static bool TransformToScreenIsKnown(LayerType* layer, |
| const TransformTree& tree) { |
| const TransformNode* node = tree.Node(layer->transform_tree_index()); |
| return !node->data.to_screen_is_animated; |
| } |
| |
| template <typename LayerType> |
| static bool HasSingularTransform(LayerType* layer, const TransformTree& tree) { |
| const TransformNode* node = tree.Node(layer->transform_tree_index()); |
| return !node->data.is_invertible || !node->data.ancestors_are_invertible; |
| } |
| |
| template <typename LayerType> |
| static bool IsLayerBackFaceVisible(LayerType* layer, |
| const TransformTree& tree) { |
| // The current W3C spec on CSS transforms says that backface visibility should |
| // be determined differently depending on whether the layer is in a "3d |
| // rendering context" or not. For Chromium code, we can determine whether we |
| // are in a 3d rendering context by checking if the parent preserves 3d. |
| |
| if (LayerIsInExisting3DRenderingContext(layer)) |
| return DrawTransformFromPropertyTrees(layer, tree).IsBackFaceVisible(); |
| |
| // In this case, either the layer establishes a new 3d rendering context, or |
| // is not in a 3d rendering context at all. |
| return layer->transform().IsBackFaceVisible(); |
| } |
| |
| template <typename LayerType> |
| static bool IsSurfaceBackFaceVisible(LayerType* layer, |
| const TransformTree& tree) { |
| if (LayerIsInExisting3DRenderingContext(layer)) { |
| const TransformNode* node = tree.Node(layer->transform_tree_index()); |
| // Draw transform as a contributing render surface. |
| // TODO(enne): we shouldn't walk the tree during a tree walk. |
| gfx::Transform surface_draw_transform; |
| tree.ComputeTransform(node->id, node->data.target_id, |
| &surface_draw_transform); |
| return surface_draw_transform.IsBackFaceVisible(); |
| } |
| |
| if (IsRootLayerOfNewRenderingContext(layer)) |
| return layer->transform().IsBackFaceVisible(); |
| |
| // If the render_surface is not part of a new or existing rendering context, |
| // then the layers that contribute to this surface will decide back-face |
| // visibility for themselves. |
| return false; |
| } |
| |
| template <typename LayerType> |
| static bool IsAnimatingTransformToScreen(LayerType* layer, |
| const TransformTree& tree) { |
| const TransformNode* node = tree.Node(layer->transform_tree_index()); |
| return node->data.to_screen_is_animated; |
| } |
| |
| static inline bool TransformToScreenIsKnown(Layer* layer, |
| const TransformTree& tree) { |
| return !IsAnimatingTransformToScreen(layer, tree); |
| } |
| |
| static inline bool TransformToScreenIsKnown(LayerImpl* layer, |
| const TransformTree& tree) { |
| return true; |
| } |
| |
| template <typename LayerType> |
| static bool HasInvertibleOrAnimatedTransform(LayerType* layer) { |
| return layer->transform_is_invertible() || |
| layer->HasPotentiallyRunningTransformAnimation(); |
| } |
| |
| static inline bool SubtreeShouldBeSkipped(LayerImpl* layer, |
| bool layer_is_drawn, |
| const TransformTree& tree) { |
| // If the layer transform is not invertible, it should not be drawn. |
| // TODO(ajuma): Correctly process subtrees with singular transform for the |
| // case where we may animate to a non-singular transform and wish to |
| // pre-raster. |
| if (!HasInvertibleOrAnimatedTransform(layer)) |
| return true; |
| |
| // When we need to do a readback/copy of a layer's output, we can not skip |
| // it or any of its ancestors. |
| if (layer->draw_properties().layer_or_descendant_has_copy_request) |
| return false; |
| |
| // If the layer is not drawn, then skip it and its subtree. |
| if (!layer_is_drawn) |
| return true; |
| |
| if (layer->render_surface() && !layer->double_sided() && |
| IsSurfaceBackFaceVisible(layer, tree)) |
| return true; |
| |
| // If layer is on the pending tree and opacity is being animated then |
| // this subtree can't be skipped as we need to create, prioritize and |
| // include tiles for this layer when deciding if tree can be activated. |
| if (layer->layer_tree_impl()->IsPendingTree() && |
| layer->HasPotentiallyRunningOpacityAnimation()) |
| return false; |
| |
| // If layer has a background filter, don't skip the layer, even it the |
| // opacity is 0. |
| if (!layer->background_filters().IsEmpty()) |
| return false; |
| |
| // The opacity of a layer always applies to its children (either implicitly |
| // via a render surface or explicitly if the parent preserves 3D), so the |
| // entire subtree can be skipped if this layer is fully transparent. |
| return !layer->opacity(); |
| } |
| |
| static inline bool SubtreeShouldBeSkipped(Layer* layer, |
| bool layer_is_drawn, |
| const TransformTree& tree) { |
| // If the layer transform is not invertible, it should not be drawn. |
| if (!layer->transform_is_invertible() && |
| !layer->HasPotentiallyRunningTransformAnimation()) |
| return true; |
| |
| // When we need to do a readback/copy of a layer's output, we can not skip |
| // it or any of its ancestors. |
| if (layer->draw_properties().layer_or_descendant_has_copy_request) |
| return false; |
| |
| // If the layer is not drawn, then skip it and its subtree. |
| if (!layer_is_drawn) |
| return true; |
| |
| if (layer->render_surface() && !layer->double_sided() && |
| !layer->HasPotentiallyRunningTransformAnimation() && |
| IsSurfaceBackFaceVisible(layer, tree)) |
| return true; |
| |
| // If layer has a background filter, don't skip the layer, even it the |
| // opacity is 0. |
| if (!layer->background_filters().IsEmpty()) |
| return false; |
| |
| // If the opacity is being animated then the opacity on the main thread is |
| // unreliable (since the impl thread may be using a different opacity), so it |
| // should not be trusted. |
| // In particular, it should not cause the subtree to be skipped. |
| // Similarly, for layers that might animate opacity using an impl-only |
| // animation, their subtree should also not be skipped. |
| return !layer->opacity() && !layer->HasPotentiallyRunningOpacityAnimation() && |
| !layer->OpacityCanAnimateOnImplThread(); |
| } |
| |
| template <typename LayerType> |
| static bool LayerShouldBeSkipped(LayerType* layer, |
| bool layer_is_drawn, |
| const TransformTree& tree) { |
| // Layers can be skipped if any of these conditions are met. |
| // - is not drawn due to it or one of its ancestors being hidden (or having |
| // no copy requests). |
| // - does not draw content. |
| // - is transparent. |
| // - has empty bounds |
| // - the layer is not double-sided, but its back face is visible. |
| // |
| // Some additional conditions need to be computed at a later point after the |
| // recursion is finished. |
| // - the intersection of render_surface content and layer clip_rect is empty |
| // - the visible_layer_rect is empty |
| // |
| // Note, if the layer should not have been drawn due to being fully |
| // transparent, we would have skipped the entire subtree and never made it |
| // into this function, so it is safe to omit this check here. |
| if (!layer_is_drawn) |
| return true; |
| |
| if (!layer->DrawsContent() || layer->bounds().IsEmpty()) |
| return true; |
| |
| LayerType* backface_test_layer = layer; |
| if (layer->use_parent_backface_visibility()) { |
| DCHECK(layer->parent()); |
| DCHECK(!layer->parent()->use_parent_backface_visibility()); |
| backface_test_layer = layer->parent(); |
| } |
| |
| // The layer should not be drawn if (1) it is not double-sided and (2) the |
| // back of the layer is known to be facing the screen. |
| if (!backface_test_layer->double_sided() && |
| TransformToScreenIsKnown(backface_test_layer, tree) && |
| IsLayerBackFaceVisible(backface_test_layer, tree)) |
| return true; |
| |
| return false; |
| } |
| |
| template <typename LayerType> |
| void FindLayersThatNeedUpdates( |
| LayerType* layer, |
| const TransformTree& tree, |
| bool subtree_is_visible_from_ancestor, |
| typename LayerType::LayerListType* update_layer_list, |
| std::vector<LayerType*>* visible_layer_list) { |
| bool layer_is_drawn = |
| layer->HasCopyRequest() || |
| (subtree_is_visible_from_ancestor && !layer->hide_layer_and_subtree()); |
| |
| if (layer->parent() && SubtreeShouldBeSkipped(layer, layer_is_drawn, tree)) |
| return; |
| |
| if (!LayerShouldBeSkipped(layer, layer_is_drawn, tree)) { |
| visible_layer_list->push_back(layer); |
| update_layer_list->push_back(layer); |
| } |
| |
| // Append mask layers to the update layer list. They don't have valid visible |
| // rects, so need to get added after the above calculation. Replica layers |
| // don't need to be updated. |
| if (LayerType* mask_layer = layer->mask_layer()) |
| update_layer_list->push_back(mask_layer); |
| if (LayerType* replica_layer = layer->replica_layer()) { |
| if (LayerType* mask_layer = replica_layer->mask_layer()) |
| update_layer_list->push_back(mask_layer); |
| } |
| |
| for (size_t i = 0; i < layer->children().size(); ++i) { |
| FindLayersThatNeedUpdates(layer->child_at(i), tree, layer_is_drawn, |
| update_layer_list, visible_layer_list); |
| } |
| } |
| |
| } // namespace |
| |
| void ComputeClips(ClipTree* clip_tree, const TransformTree& transform_tree) { |
| if (!clip_tree->needs_update()) |
| return; |
| for (int i = 0; i < static_cast<int>(clip_tree->size()); ++i) { |
| ClipNode* clip_node = clip_tree->Node(i); |
| const TransformNode* transform_node = |
| transform_tree.Node(clip_node->data.transform_id); |
| |
| // Only descendants of a real clipping layer (i.e., not 0) may have their |
| // clip adjusted due to intersecting with an ancestor clip. |
| const bool is_clipped = clip_node->parent_id > 0; |
| if (!is_clipped) { |
| DCHECK(!clip_node->data.inherit_parent_target_space_clip); |
| clip_node->data.combined_clip = clip_node->data.clip; |
| if (clip_node->id > 0) { |
| gfx::Transform to_target = transform_node->data.to_target; |
| clip_node->data.clip_in_target_space = |
| MathUtil::MapClippedRect(to_target, clip_node->data.combined_clip); |
| } |
| continue; |
| } |
| |
| ClipNode* parent_clip_node = clip_tree->parent(clip_node); |
| const TransformNode* parent_transform_node = |
| transform_tree.Node(parent_clip_node->data.transform_id); |
| |
| // Clips must be combined in target space. We cannot, for example, combine |
| // clips in the space of the child clip. The reason is non-affine |
| // transforms. Say we have the following tree T->A->B->C, and B clips C, but |
| // draw into target T. It may be the case that A applies a perspective |
| // transform, and B and C are at different z positions. When projected into |
| // target space, the relative sizes and positions of B and C can shift. |
| // Since it's the relationship in target space that matters, that's where we |
| // must combine clips. |
| gfx::Transform parent_to_target; |
| gfx::Transform clip_to_target; |
| |
| gfx::Transform target_to_clip; |
| gfx::Transform parent_to_transform_target; |
| gfx::Transform transform_target_to_target; |
| |
| const bool target_is_root_surface = clip_node->data.target_id == 1; |
| // When the target is the root surface, we need to include the root |
| // transform by walking up to the root of the transform tree. |
| const int target_id = |
| target_is_root_surface ? 0 : clip_node->data.target_id; |
| |
| bool success = true; |
| // When render surface applies clip, we need the clip from the target's |
| // target space. But, as the combined clip is in parent clip's target |
| // space, we need to first transform it from parent's target space to |
| // target's target space. |
| if (clip_node->data.inherit_parent_target_space_clip) { |
| success &= transform_tree.ComputeTransformWithDestinationSublayerScale( |
| parent_transform_node->id, transform_node->data.target_id, |
| &parent_to_transform_target); |
| success &= transform_tree.ComputeTransformWithSourceSublayerScale( |
| transform_node->data.target_id, target_id, |
| &transform_target_to_target); |
| transform_target_to_target.matrix().postScale( |
| transform_node->data.sublayer_scale.x(), |
| transform_node->data.sublayer_scale.y(), 1.0); |
| } else if (parent_transform_node->data.content_target_id == |
| clip_node->data.target_id) { |
| parent_to_target = parent_transform_node->data.to_target; |
| } else { |
| success &= transform_tree.ComputeTransformWithDestinationSublayerScale( |
| parent_transform_node->id, target_id, &parent_to_target); |
| } |
| |
| if (transform_node->data.content_target_id == clip_node->data.target_id) { |
| clip_to_target = transform_node->data.to_target; |
| } else { |
| success &= transform_tree.ComputeTransformWithDestinationSublayerScale( |
| transform_node->id, target_id, &clip_to_target); |
| } |
| |
| if (transform_node->data.content_target_id == clip_node->data.target_id && |
| transform_node->data.ancestors_are_invertible) { |
| target_to_clip = transform_node->data.from_target; |
| } else { |
| success &= clip_to_target.GetInverse(&target_to_clip); |
| } |
| |
| // If we can't compute a transform, it's because we had to use the inverse |
| // of a singular transform. We won't draw in this case, so there's no need |
| // to compute clips. |
| if (!success) { |
| continue; |
| } |
| |
| // In order to intersect with as small a rect as possible, we do a |
| // preliminary clip in target space so that when we project back, there's |
| // less likelihood of intersecting the view plane. |
| gfx::RectF inherited_clip_in_target_space; |
| if (clip_node->data.inherit_parent_target_space_clip) { |
| gfx::RectF combined_clip_in_transform_target_space; |
| if (parent_transform_node->id > transform_node->data.target_id) |
| combined_clip_in_transform_target_space = MathUtil::MapClippedRect( |
| parent_to_transform_target, parent_clip_node->data.combined_clip); |
| else |
| combined_clip_in_transform_target_space = MathUtil::ProjectClippedRect( |
| parent_to_transform_target, parent_clip_node->data.combined_clip); |
| inherited_clip_in_target_space = MathUtil::ProjectClippedRect( |
| transform_target_to_target, combined_clip_in_transform_target_space); |
| } else if (parent_transform_node->id > target_id) { |
| inherited_clip_in_target_space = MathUtil::MapClippedRect( |
| parent_to_target, parent_clip_node->data.combined_clip); |
| } else { |
| inherited_clip_in_target_space = MathUtil::ProjectClippedRect( |
| parent_to_target, parent_clip_node->data.combined_clip); |
| } |
| |
| // When render surface inherits its parent target space clip, the layer |
| // that created the clip node doesn't apply any clip. So, we shouldn't clip |
| // using the clip value stored in the clip node. |
| gfx::RectF intersected_in_target_space; |
| if (!clip_node->data.inherit_parent_target_space_clip) { |
| gfx::RectF clip_in_target_space = |
| MathUtil::MapClippedRect(clip_to_target, clip_node->data.clip); |
| |
| intersected_in_target_space = gfx::IntersectRects( |
| inherited_clip_in_target_space, clip_in_target_space); |
| } else { |
| intersected_in_target_space = inherited_clip_in_target_space; |
| } |
| clip_node->data.clip_in_target_space = intersected_in_target_space; |
| |
| clip_node->data.combined_clip = MathUtil::ProjectClippedRect( |
| target_to_clip, intersected_in_target_space); |
| |
| if (!clip_node->data.inherit_parent_target_space_clip) |
| clip_node->data.combined_clip.Intersect(clip_node->data.clip); |
| } |
| clip_tree->set_needs_update(false); |
| } |
| |
| void ComputeTransforms(TransformTree* transform_tree) { |
| if (!transform_tree->needs_update()) |
| return; |
| for (int i = 1; i < static_cast<int>(transform_tree->size()); ++i) |
| transform_tree->UpdateTransforms(i); |
| transform_tree->set_needs_update(false); |
| } |
| |
| void ComputeOpacities(EffectTree* effect_tree) { |
| if (!effect_tree->needs_update()) |
| return; |
| for (int i = 1; i < static_cast<int>(effect_tree->size()); ++i) |
| effect_tree->UpdateOpacities(i); |
| effect_tree->set_needs_update(false); |
| } |
| |
| template <typename LayerType> |
| void ComputeVisibleRectsUsingPropertyTreesInternal( |
| LayerType* root_layer, |
| PropertyTrees* property_trees, |
| typename LayerType::LayerListType* update_layer_list) { |
| if (property_trees->transform_tree.needs_update()) |
| property_trees->clip_tree.set_needs_update(true); |
| ComputeTransforms(&property_trees->transform_tree); |
| ComputeClips(&property_trees->clip_tree, property_trees->transform_tree); |
| ComputeOpacities(&property_trees->effect_tree); |
| |
| const bool subtree_is_visible_from_ancestor = true; |
| std::vector<LayerType*> visible_layer_list; |
| FindLayersThatNeedUpdates(root_layer, property_trees->transform_tree, |
| subtree_is_visible_from_ancestor, update_layer_list, |
| &visible_layer_list); |
| CalculateVisibleRects<LayerType>(visible_layer_list, |
| property_trees->clip_tree, |
| property_trees->transform_tree); |
| } |
| |
| void BuildPropertyTreesAndComputeVisibleRects( |
| Layer* root_layer, |
| const Layer* page_scale_layer, |
| const Layer* inner_viewport_scroll_layer, |
| const Layer* outer_viewport_scroll_layer, |
| float page_scale_factor, |
| float device_scale_factor, |
| const gfx::Rect& viewport, |
| const gfx::Transform& device_transform, |
| PropertyTrees* property_trees, |
| LayerList* update_layer_list) { |
| PropertyTreeBuilder::BuildPropertyTrees( |
| root_layer, page_scale_layer, inner_viewport_scroll_layer, |
| outer_viewport_scroll_layer, page_scale_factor, device_scale_factor, |
| viewport, device_transform, property_trees); |
| ComputeVisibleRectsUsingPropertyTrees(root_layer, property_trees, |
| update_layer_list); |
| } |
| |
| void BuildPropertyTreesAndComputeVisibleRects( |
| LayerImpl* root_layer, |
| const LayerImpl* page_scale_layer, |
| const LayerImpl* inner_viewport_scroll_layer, |
| const LayerImpl* outer_viewport_scroll_layer, |
| float page_scale_factor, |
| float device_scale_factor, |
| const gfx::Rect& viewport, |
| const gfx::Transform& device_transform, |
| PropertyTrees* property_trees, |
| LayerImplList* update_layer_list) { |
| PropertyTreeBuilder::BuildPropertyTrees( |
| root_layer, page_scale_layer, inner_viewport_scroll_layer, |
| outer_viewport_scroll_layer, page_scale_factor, device_scale_factor, |
| viewport, device_transform, property_trees); |
| ComputeVisibleRectsUsingPropertyTrees(root_layer, property_trees, |
| update_layer_list); |
| } |
| |
| void ComputeVisibleRectsUsingPropertyTrees(Layer* root_layer, |
| PropertyTrees* property_trees, |
| LayerList* update_layer_list) { |
| ComputeVisibleRectsUsingPropertyTreesInternal(root_layer, property_trees, |
| update_layer_list); |
| } |
| |
| void ComputeVisibleRectsUsingPropertyTrees(LayerImpl* root_layer, |
| PropertyTrees* property_trees, |
| LayerImplList* update_layer_list) { |
| ComputeVisibleRectsUsingPropertyTreesInternal(root_layer, property_trees, |
| update_layer_list); |
| } |
| |
| template <typename LayerType> |
| gfx::Transform DrawTransformFromPropertyTreesInternal( |
| const LayerType* layer, |
| const TransformTree& tree) { |
| const TransformNode* node = tree.Node(layer->transform_tree_index()); |
| |
| gfx::Transform xform; |
| const bool owns_non_root_surface = layer->parent() && layer->render_surface(); |
| if (!owns_non_root_surface) { |
| // If you're not the root, or you don't own a surface, you need to apply |
| // your local offset. |
| xform = node->data.to_target; |
| if (layer->should_flatten_transform_from_property_tree()) |
| xform.FlattenTo2d(); |
| xform.Translate(layer->offset_to_transform_parent().x(), |
| layer->offset_to_transform_parent().y()); |
| } else { |
| // Surfaces need to apply their sublayer scale. |
| xform.Scale(node->data.sublayer_scale.x(), node->data.sublayer_scale.y()); |
| } |
| return xform; |
| } |
| |
| gfx::Transform DrawTransformFromPropertyTrees(const Layer* layer, |
| const TransformTree& tree) { |
| return DrawTransformFromPropertyTreesInternal(layer, tree); |
| } |
| |
| gfx::Transform DrawTransformFromPropertyTrees(const LayerImpl* layer, |
| const TransformTree& tree) { |
| return DrawTransformFromPropertyTreesInternal(layer, tree); |
| } |
| |
| gfx::Transform DrawTransformOfRenderSurfaceFromPropertyTrees( |
| const RenderSurfaceImpl* render_surface, |
| const TransformTree& tree) { |
| const TransformNode* node = tree.Node(render_surface->TransformTreeIndex()); |
| gfx::Transform render_surface_transform; |
| // The draw transform of root render surface is identity tranform. |
| if (node->id == 1) |
| return render_surface_transform; |
| const TransformNode* target_node = tree.Node(node->data.target_id); |
| if (target_node->id == 1) |
| target_node = tree.Node(0); |
| tree.ComputeTransformWithDestinationSublayerScale(node->id, target_node->id, |
| &render_surface_transform); |
| if (node->data.sublayer_scale.x() != 0.0 && |
| node->data.sublayer_scale.y() != 0.0) |
| render_surface_transform.Scale(1.0 / node->data.sublayer_scale.x(), |
| 1.0 / node->data.sublayer_scale.y()); |
| return render_surface_transform; |
| } |
| |
| bool RenderSurfaceIsClippedFromPropertyTrees( |
| const RenderSurfaceImpl* render_surface, |
| const ClipTree& tree) { |
| const ClipNode* node = tree.Node(render_surface->ClipTreeIndex()); |
| // If the render surface's owning layer doesn't form a clip node, it is not |
| // clipped. |
| if (render_surface->OwningLayerId() != node->owner_id) |
| return false; |
| return node->data.render_surface_is_clipped; |
| } |
| |
| gfx::Rect ClipRectOfRenderSurfaceFromPropertyTrees( |
| const RenderSurfaceImpl* render_surface, |
| const ClipTree& clip_tree) { |
| if (!RenderSurfaceIsClippedFromPropertyTrees(render_surface, clip_tree)) |
| return gfx::Rect(); |
| const ClipNode* clip_node = clip_tree.Node(render_surface->ClipTreeIndex()); |
| const ClipNode* parent_clip_node = clip_tree.parent(clip_node); |
| return gfx::ToEnclosingRect(parent_clip_node->data.clip_in_target_space); |
| } |
| |
| gfx::Transform ScreenSpaceTransformOfRenderSurfaceFromPropertyTrees( |
| RenderSurfaceImpl* render_surface, |
| const TransformTree& tree) { |
| const TransformNode* node = tree.Node(render_surface->TransformTreeIndex()); |
| gfx::Transform screen_space_transform; |
| // The screen space transform of root render surface is identity tranform. |
| if (node->id == 1) |
| return screen_space_transform; |
| screen_space_transform = node->data.to_screen; |
| if (node->data.sublayer_scale.x() != 0.0 && |
| node->data.sublayer_scale.y() != 0.0) |
| screen_space_transform.Scale(1.0 / node->data.sublayer_scale.x(), |
| 1.0 / node->data.sublayer_scale.y()); |
| return screen_space_transform; |
| } |
| |
| template <typename LayerType> |
| gfx::Transform ScreenSpaceTransformFromPropertyTreesInternal( |
| LayerType* layer, |
| const TransformTree& tree) { |
| gfx::Transform xform(1, 0, 0, 1, layer->offset_to_transform_parent().x(), |
| layer->offset_to_transform_parent().y()); |
| if (layer->transform_tree_index() >= 0) { |
| gfx::Transform ssxform = |
| tree.Node(layer->transform_tree_index())->data.to_screen; |
| xform.ConcatTransform(ssxform); |
| if (layer->should_flatten_transform_from_property_tree()) |
| xform.FlattenTo2d(); |
| } |
| return xform; |
| } |
| |
| gfx::Transform ScreenSpaceTransformFromPropertyTrees( |
| const Layer* layer, |
| const TransformTree& tree) { |
| return ScreenSpaceTransformFromPropertyTreesInternal(layer, tree); |
| } |
| |
| gfx::Transform ScreenSpaceTransformFromPropertyTrees( |
| const LayerImpl* layer, |
| const TransformTree& tree) { |
| return ScreenSpaceTransformFromPropertyTreesInternal(layer, tree); |
| } |
| |
| template <typename LayerType> |
| bool ScreenSpaceTransformIsAnimatingFromPropertyTreesInternal( |
| LayerType* layer, |
| const TransformTree& tree) { |
| return tree.Node(layer->transform_tree_index())->data.to_screen_is_animated; |
| } |
| |
| bool ScreenSpaceTransformIsAnimatingFromPropertyTrees( |
| const Layer* layer, |
| const TransformTree& tree) { |
| return ScreenSpaceTransformIsAnimatingFromPropertyTreesInternal(layer, tree); |
| } |
| |
| bool ScreenSpaceTransformIsAnimatingFromPropertyTrees( |
| const LayerImpl* layer, |
| const TransformTree& tree) { |
| return ScreenSpaceTransformIsAnimatingFromPropertyTreesInternal(layer, tree); |
| } |
| |
| template <typename LayerType> |
| float DrawOpacityFromPropertyTreesInternal(LayerType layer, |
| const EffectTree& tree) { |
| if (!layer->render_target()) |
| return 0.f; |
| |
| const EffectNode* target_node = |
| tree.Node(layer->render_target()->effect_tree_index()); |
| const EffectNode* node = tree.Node(layer->effect_tree_index()); |
| if (node == target_node) |
| return 1.f; |
| |
| float draw_opacity = 1.f; |
| while (node != target_node) { |
| draw_opacity *= node->data.opacity; |
| node = tree.parent(node); |
| } |
| return draw_opacity; |
| } |
| |
| float DrawOpacityFromPropertyTrees(const Layer* layer, const EffectTree& tree) { |
| return DrawOpacityFromPropertyTreesInternal(layer, tree); |
| } |
| |
| float DrawOpacityFromPropertyTrees(const LayerImpl* layer, |
| const EffectTree& tree) { |
| return DrawOpacityFromPropertyTreesInternal(layer, tree); |
| } |
| |
| float DrawOpacityOfRenderSurfaceFromPropertyTrees( |
| RenderSurfaceImpl* render_surface, |
| const EffectTree& tree) { |
| const EffectNode* node = tree.Node(render_surface->EffectTreeIndex()); |
| float target_opacity_tree_index = render_surface->TargetEffectTreeIndex(); |
| if (target_opacity_tree_index < 0) |
| return node->data.screen_space_opacity; |
| const EffectNode* target_node = tree.Node(target_opacity_tree_index); |
| float draw_opacity = 1.f; |
| while (node != target_node) { |
| draw_opacity *= node->data.opacity; |
| node = tree.parent(node); |
| } |
| return draw_opacity; |
| } |
| |
| bool CanUseLcdTextFromPropertyTrees(const LayerImpl* layer, |
| bool layers_always_allowed_lcd_text, |
| bool can_use_lcd_text, |
| PropertyTrees* property_trees) { |
| if (layers_always_allowed_lcd_text) |
| return true; |
| if (!can_use_lcd_text) |
| return false; |
| if (!layer->contents_opaque()) |
| return false; |
| DCHECK(!property_trees->transform_tree.needs_update()); |
| DCHECK(!property_trees->effect_tree.needs_update()); |
| |
| const EffectNode* opacity_node = |
| property_trees->effect_tree.Node(layer->effect_tree_index()); |
| if (opacity_node->data.screen_space_opacity != 1.f) |
| return false; |
| const TransformNode* transform_node = |
| property_trees->transform_tree.Node(layer->transform_tree_index()); |
| if (!transform_node->data.node_and_ancestors_have_only_integer_translation) |
| return false; |
| if (static_cast<int>(layer->offset_to_transform_parent().x()) != |
| layer->offset_to_transform_parent().x()) |
| return false; |
| if (static_cast<int>(layer->offset_to_transform_parent().y()) != |
| layer->offset_to_transform_parent().y()) |
| return false; |
| return true; |
| } |
| |
| gfx::Rect DrawableContentRectOfSurfaceFromPropertyTrees( |
| const RenderSurfaceImpl* render_surface, |
| const TransformTree& transform_tree) { |
| gfx::Rect drawable_content_rect = |
| gfx::ToEnclosingRect(MathUtil::MapClippedRect( |
| DrawTransformOfRenderSurfaceFromPropertyTrees(render_surface, |
| transform_tree), |
| render_surface->content_rect_from_property_trees())); |
| if (render_surface->HasReplica()) { |
| drawable_content_rect.Union(gfx::ToEnclosingRect(MathUtil::MapClippedRect( |
| render_surface->ReplicaDrawTransform(), |
| render_surface->content_rect_from_property_trees()))); |
| } |
| return drawable_content_rect; |
| } |
| |
| gfx::Rect DrawableContentRectFromPropertyTrees( |
| const LayerImpl* layer, |
| const TransformTree& transform_tree) { |
| gfx::Rect drawable_content_rect = MathUtil::MapEnclosingClippedRect( |
| DrawTransformFromPropertyTrees(layer, transform_tree), |
| gfx::Rect(layer->bounds())); |
| if (layer->is_clipped() && layer->clip_tree_index() > 0) { |
| drawable_content_rect.Intersect( |
| layer->clip_rect_in_target_space_from_property_trees()); |
| } |
| return drawable_content_rect; |
| } |
| |
| gfx::Rect ClipRectFromPropertyTrees(const LayerImpl* layer, |
| const TransformTree& transform_tree) { |
| if (layer->is_clipped() && layer->clip_tree_index() > 0) |
| return layer->clip_rect_in_target_space_from_property_trees(); |
| return MathUtil::MapEnclosingClippedRect( |
| DrawTransformFromPropertyTrees(layer, transform_tree), |
| gfx::Rect(layer->bounds())); |
| } |
| |
| gfx::Rect ViewportRectFromPropertyTrees(const ClipTree& tree) { |
| return gfx::ToEnclosingRect(tree.Node(1)->data.clip); |
| } |
| |
| } // namespace cc |