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