blob: 74e91cd3b9a15ea50e2d2933f4823c48ead5f085 [file] [log] [blame]
// 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.
#include "platform/graphics/compositing/PropertyTreeManager.h"
#include "cc/layers/layer.h"
#include "cc/trees/clip_node.h"
#include "cc/trees/effect_node.h"
#include "cc/trees/layer_tree_host.h"
#include "cc/trees/property_tree.h"
#include "cc/trees/scroll_node.h"
#include "cc/trees/transform_node.h"
#include "platform/graphics/paint/ClipPaintPropertyNode.h"
#include "platform/graphics/paint/EffectPaintPropertyNode.h"
#include "platform/graphics/paint/GeometryMapper.h"
#include "platform/graphics/paint/ScrollPaintPropertyNode.h"
#include "platform/graphics/paint/TransformPaintPropertyNode.h"
#include "third_party/skia/include/effects/SkColorFilterImageFilter.h"
#include "third_party/skia/include/effects/SkLumaColorFilter.h"
namespace blink {
namespace {
static constexpr int kInvalidNodeId = -1;
// cc's property trees use 0 for the root node (always non-null).
static constexpr int kRealRootNodeId = 0;
// cc allocates special nodes for root effects such as the device scale.
static constexpr int kSecondaryRootNodeId = 1;
} // namespace
PropertyTreeManager::PropertyTreeManager(PropertyTreeManagerClient& client,
cc::PropertyTrees& property_trees,
cc::Layer* root_layer,
int sequence_number)
: client_(client),
property_trees_(property_trees),
root_layer_(root_layer),
sequence_number_(sequence_number) {
SetupRootTransformNode();
SetupRootClipNode();
SetupRootEffectNode();
SetupRootScrollNode();
}
void PropertyTreeManager::Finalize() {
while (effect_stack_.size())
CloseCcEffect();
}
cc::TransformTree& PropertyTreeManager::GetTransformTree() {
return property_trees_.transform_tree;
}
cc::ClipTree& PropertyTreeManager::GetClipTree() {
return property_trees_.clip_tree;
}
cc::EffectTree& PropertyTreeManager::GetEffectTree() {
return property_trees_.effect_tree;
}
cc::ScrollTree& PropertyTreeManager::GetScrollTree() {
return property_trees_.scroll_tree;
}
void PropertyTreeManager::SetupRootTransformNode() {
// cc is hardcoded to use transform node index 1 for device scale and
// transform.
cc::TransformTree& transform_tree = property_trees_.transform_tree;
transform_tree.clear();
property_trees_.element_id_to_transform_node_index.clear();
cc::TransformNode& transform_node = *transform_tree.Node(
transform_tree.Insert(cc::TransformNode(), kRealRootNodeId));
DCHECK_EQ(transform_node.id, kSecondaryRootNodeId);
transform_node.source_node_id = transform_node.parent_id;
// TODO(jaydasika): We shouldn't set ToScreen and FromScreen of root
// transform node here. They should be set while updating transform tree in
// cc.
float device_scale_factor =
root_layer_->layer_tree_host()->device_scale_factor();
gfx::Transform to_screen;
to_screen.Scale(device_scale_factor, device_scale_factor);
transform_tree.SetToScreen(kRealRootNodeId, to_screen);
gfx::Transform from_screen;
bool invertible = to_screen.GetInverse(&from_screen);
DCHECK(invertible);
transform_tree.SetFromScreen(kRealRootNodeId, from_screen);
transform_tree.set_needs_update(true);
transform_node_map_.Set(TransformPaintPropertyNode::Root(),
transform_node.id);
root_layer_->SetTransformTreeIndex(transform_node.id);
}
void PropertyTreeManager::SetupRootClipNode() {
// cc is hardcoded to use clip node index 1 for viewport clip.
cc::ClipTree& clip_tree = property_trees_.clip_tree;
clip_tree.clear();
cc::ClipNode& clip_node =
*clip_tree.Node(clip_tree.Insert(cc::ClipNode(), kRealRootNodeId));
DCHECK_EQ(clip_node.id, kSecondaryRootNodeId);
clip_node.clip_type = cc::ClipNode::ClipType::APPLIES_LOCAL_CLIP;
clip_node.clip = gfx::RectF(
gfx::SizeF(root_layer_->layer_tree_host()->device_viewport_size()));
clip_node.transform_id = kRealRootNodeId;
clip_node_map_.Set(ClipPaintPropertyNode::Root(), clip_node.id);
root_layer_->SetClipTreeIndex(clip_node.id);
}
void PropertyTreeManager::SetupRootEffectNode() {
// cc is hardcoded to use effect node index 1 for root render surface.
cc::EffectTree& effect_tree = property_trees_.effect_tree;
effect_tree.clear();
property_trees_.element_id_to_effect_node_index.clear();
cc::EffectNode& effect_node =
*effect_tree.Node(effect_tree.Insert(cc::EffectNode(), kInvalidNodeId));
DCHECK_EQ(effect_node.id, kSecondaryRootNodeId);
static UniqueObjectId unique_id = NewUniqueObjectId();
effect_node.stable_id =
CompositorElementIdFromUniqueObjectId(unique_id).ToInternalValue();
effect_node.transform_id = kRealRootNodeId;
effect_node.clip_id = kSecondaryRootNodeId;
effect_node.has_render_surface = true;
root_layer_->SetEffectTreeIndex(effect_node.id);
current_effect_id_ = effect_node.id;
current_effect_type_ = CcEffectType::kEffect;
current_effect_ = EffectPaintPropertyNode::Root();
current_clip_ = current_effect_->OutputClip();
}
void PropertyTreeManager::SetupRootScrollNode() {
cc::ScrollTree& scroll_tree = property_trees_.scroll_tree;
scroll_tree.clear();
property_trees_.element_id_to_scroll_node_index.clear();
cc::ScrollNode& scroll_node =
*scroll_tree.Node(scroll_tree.Insert(cc::ScrollNode(), kRealRootNodeId));
DCHECK_EQ(scroll_node.id, kSecondaryRootNodeId);
scroll_node.transform_id = kSecondaryRootNodeId;
scroll_node_map_.Set(ScrollPaintPropertyNode::Root(), scroll_node.id);
root_layer_->SetScrollTreeIndex(scroll_node.id);
}
int PropertyTreeManager::EnsureCompositorTransformNode(
const TransformPaintPropertyNode* transform_node) {
DCHECK(transform_node);
// TODO(crbug.com/645615): Remove the failsafe here.
if (!transform_node)
return kSecondaryRootNodeId;
auto it = transform_node_map_.find(transform_node);
if (it != transform_node_map_.end())
return it->value;
int parent_id = EnsureCompositorTransformNode(transform_node->Parent());
int id = GetTransformTree().Insert(cc::TransformNode(), parent_id);
cc::TransformNode& compositor_node = *GetTransformTree().Node(id);
compositor_node.source_node_id = parent_id;
FloatPoint3D origin = transform_node->Origin();
compositor_node.pre_local.matrix().setTranslate(-origin.X(), -origin.Y(),
-origin.Z());
compositor_node.local.matrix() =
TransformationMatrix::ToSkMatrix44(transform_node->Matrix());
compositor_node.post_local.matrix().setTranslate(origin.X(), origin.Y(),
origin.Z());
compositor_node.needs_local_transform_update = true;
compositor_node.flattens_inherited_transform =
transform_node->FlattensInheritedTransform();
compositor_node.sorting_context_id = transform_node->RenderingContextId();
CompositorElementId compositor_element_id =
transform_node->GetCompositorElementId();
if (compositor_element_id) {
property_trees_.element_id_to_transform_node_index[compositor_element_id] =
id;
}
// If this transform is a scroll offset translation, create the associated
// compositor scroll property node and adjust the compositor transform node's
// scroll offset.
if (auto* scroll_node = transform_node->ScrollNode()) {
// Blink creates a 2d transform node just for scroll offset whereas cc's
// transform node has a special scroll offset field. To handle this we
// adjust cc's transform node to remove the 2d scroll translation and
// instead set the scroll_offset field.
auto scroll_offset_size = transform_node->Matrix().To2DTranslation();
auto scroll_offset = gfx::ScrollOffset(-scroll_offset_size.Width(),
-scroll_offset_size.Height());
DCHECK(compositor_node.local.IsIdentityOr2DTranslation());
compositor_node.scroll_offset = scroll_offset;
compositor_node.local.MakeIdentity();
compositor_node.scrolls = true;
CreateCompositorScrollNode(scroll_node, compositor_node);
}
auto result = transform_node_map_.Set(transform_node, id);
DCHECK(result.is_new_entry);
GetTransformTree().set_needs_update(true);
return id;
}
int PropertyTreeManager::EnsureCompositorClipNode(
const ClipPaintPropertyNode* clip_node) {
DCHECK(clip_node);
// TODO(crbug.com/645615): Remove the failsafe here.
if (!clip_node)
return kSecondaryRootNodeId;
auto it = clip_node_map_.find(clip_node);
if (it != clip_node_map_.end())
return it->value;
int parent_id = EnsureCompositorClipNode(clip_node->Parent());
int id = GetClipTree().Insert(cc::ClipNode(), parent_id);
cc::ClipNode& compositor_node = *GetClipTree().Node(id);
compositor_node.clip = clip_node->ClipRect().Rect();
compositor_node.transform_id =
EnsureCompositorTransformNode(clip_node->LocalTransformSpace());
compositor_node.clip_type = cc::ClipNode::ClipType::APPLIES_LOCAL_CLIP;
auto result = clip_node_map_.Set(clip_node, id);
DCHECK(result.is_new_entry);
GetClipTree().set_needs_update(true);
return id;
}
void PropertyTreeManager::CreateCompositorScrollNode(
const ScrollPaintPropertyNode* scroll_node,
const cc::TransformNode& scroll_offset_translation) {
DCHECK(!scroll_node_map_.Contains(scroll_node));
auto parent_it = scroll_node_map_.find(scroll_node->Parent());
// Compositor transform nodes up to scroll_offset_translation must exist.
// Scrolling uses the transform tree for scroll offsets so this means all
// ancestor scroll nodes must also exist.
DCHECK(parent_it != scroll_node_map_.end());
int parent_id = parent_it->value;
int id = GetScrollTree().Insert(cc::ScrollNode(), parent_id);
cc::ScrollNode& compositor_node = *GetScrollTree().Node(id);
compositor_node.scrollable = true;
compositor_node.container_bounds = scroll_node->ContainerRect().Size();
compositor_node.bounds = scroll_node->ContentsRect().Size();
compositor_node.user_scrollable_horizontal =
scroll_node->UserScrollableHorizontal();
compositor_node.user_scrollable_vertical =
scroll_node->UserScrollableVertical();
compositor_node.main_thread_scrolling_reasons =
scroll_node->GetMainThreadScrollingReasons();
auto compositor_element_id = scroll_node->GetCompositorElementId();
if (compositor_element_id) {
compositor_node.element_id = compositor_element_id;
property_trees_.element_id_to_scroll_node_index[compositor_element_id] = id;
}
compositor_node.transform_id = scroll_offset_translation.id;
// TODO(pdr): Set the scroll node's non_fast_scrolling_region value.
auto result = scroll_node_map_.Set(scroll_node, id);
DCHECK(result.is_new_entry);
GetScrollTree().SetScrollOffset(compositor_element_id,
scroll_offset_translation.scroll_offset);
GetScrollTree().set_needs_update(true);
}
int PropertyTreeManager::EnsureCompositorScrollNode(
const TransformPaintPropertyNode* scroll_offset_translation) {
const auto* scroll_node = scroll_offset_translation->ScrollNode();
DCHECK(scroll_node);
EnsureCompositorTransformNode(scroll_offset_translation);
auto it = scroll_node_map_.find(scroll_node);
DCHECK(it != scroll_node_map_.end());
return it->value;
}
void PropertyTreeManager::EmitClipMaskLayer() {
int clip_id = EnsureCompositorClipNode(current_clip_);
CompositorElementId mask_isolation_id, mask_effect_id;
cc::Layer* mask_layer = client_.CreateOrReuseSynthesizedClipLayer(
current_clip_, mask_isolation_id, mask_effect_id);
cc::EffectNode& mask_isolation = *GetEffectTree().Node(current_effect_id_);
// Assignment of mask_isolation.stable_id was delayed until now.
// See PropertyTreeManager::SynthesizeCcEffectsForClipsIfNeeded().
DCHECK_EQ(static_cast<uint64_t>(cc::EffectNode::INVALID_STABLE_ID),
mask_isolation.stable_id);
mask_isolation.stable_id = mask_isolation_id.ToInternalValue();
cc::EffectNode& mask_effect = *GetEffectTree().Node(
GetEffectTree().Insert(cc::EffectNode(), current_effect_id_));
mask_effect.stable_id = mask_effect_id.ToInternalValue();
mask_effect.clip_id = clip_id;
mask_effect.has_render_surface = true;
mask_effect.blend_mode = SkBlendMode::kDstIn;
const TransformPaintPropertyNode* clip_space =
current_clip_->LocalTransformSpace();
root_layer_->AddChild(mask_layer);
mask_layer->set_property_tree_sequence_number(sequence_number_);
mask_layer->SetTransformTreeIndex(EnsureCompositorTransformNode(clip_space));
// TODO(pdr): This could be a performance issue because it crawls up the
// transform tree for each pending layer. If this is on profiles, we should
// cache a lookup of transform node to scroll translation transform node.
int scroll_id =
EnsureCompositorScrollNode(&clip_space->NearestScrollTranslationNode());
mask_layer->SetScrollTreeIndex(scroll_id);
mask_layer->SetClipTreeIndex(clip_id);
mask_layer->SetEffectTreeIndex(mask_effect.id);
}
void PropertyTreeManager::CloseCcEffect() {
DCHECK(effect_stack_.size());
const EffectStackEntry& previous_state = effect_stack_.back();
// An effect with exotic blending that is masked by a synthesized clip must
// have its blending to the outermost synthesized clip. It is because
// blending needs access to the backdrop of the enclosing effect. With
// the isolation for a synthesized clip, a blank backdrop will be seen.
// Therefore the blending is delegated to the outermost synthesized clip,
// thus the clip can't be shared with sibling layers, and must be closed now.
bool clear_synthetic_effects =
!IsCurrentCcEffectSynthetic() &&
current_effect_->BlendMode() != SkBlendMode::kSrcOver;
// We are about to close an effect that was synthesized for isolating
// a clip mask. Now emit the actual clip mask that will be composited on
// top of masked contents with SkBlendMode::kDstIn.
if (IsCurrentCcEffectSynthetic())
EmitClipMaskLayer();
current_effect_id_ = previous_state.effect_id;
current_effect_type_ = previous_state.effect_type;
current_effect_ = previous_state.effect;
current_clip_ = previous_state.clip;
effect_stack_.pop_back();
if (clear_synthetic_effects) {
while (IsCurrentCcEffectSynthetic())
CloseCcEffect();
}
}
int PropertyTreeManager::SwitchToEffectNodeWithSynthesizedClip(
const EffectPaintPropertyNode& next_effect,
const ClipPaintPropertyNode& next_clip) {
// This function is expected to be invoked right before emitting each layer.
// It keeps track of the nesting of clip and effects, output a composited
// effect node whenever an effect is entered, or a non-trivial clip is
// entered. In the latter case, the generated composited effect node is
// called a "synthetic effect", and the corresponding clip a "synthesized
// clip". Upon exiting a synthesized clip, a mask layer will be appended,
// which will be kDstIn blended on top of contents enclosed by the synthetic
// effect, i.e. applying the clip as a mask.
//
// For example with the following clip and effect tree and pending layers:
// E0 <-- E1
// C0 <-- C1(rounded)
// [P0(E1,C0), P1(E1,C1), P2(E0, C1)]
// In effect stack diagram:
// P0(C0) P1(C1)
// [ E1 ] P2(C1)
// [ E0 ]
//
// The following cc property trees and layers will be generated:
// E0 <+- E1 <-- E_C1_1 <-- E_C1_1M
// +- E_C1_2 <-- E_C1_2M
// C0 <-- C1
// [L0(E1,C0), L1(E_C1_1, C1), L_C1_1(E_C1_1M, C1), L2(E0, C1),
// L_C1_2(E_C1_2M, C1)]
// In effect stack diagram:
// L_C1_1
// L1(C1) [ E_C1_1M ] L_C2_2
// L0(C0) [ E_C1_1 ] L2(C1) [ E_C1_2M ]
// [ E1 ][ E_C1_2 ]
// [ E0 ]
//
// As the caller iterates the layer list, the sequence of events happen in
// the following order:
// Prior to emitting P0, this method is invoked with (E1, C0). A compositor
// effect node for E1 is generated as we are entering it. The caller emits P0.
// Prior to emitting P1, this method is invoked with (E1, C1). A synthetic
// compositor effect for C1 is generated as we are entering it. The caller
// emits P1.
// Prior to emitting P2, this method is invoked with (E0, C1). Both previously
// entered effects must be closed, because synthetic effect for C1 is enclosed
// by E1, thus must be closed before E1 can be closed. A mask layer L_C1_1
// is generated along with an internal effect node for blending. After closing
// both effects, C1 has to be entered again, thus generates another synthetic
// compositor effect. The caller emits P2.
// At last, the caller invokes Finalize() to close the unclosed synthetic
// effect. Another mask layer L_C1_2 is generated, along with its internal
// effect node for blending.
const auto& ancestor = LowestCommonAncestor(*current_effect_, next_effect);
while (current_effect_ != &ancestor)
CloseCcEffect();
bool newly_built = BuildEffectNodesRecursively(&next_effect);
SynthesizeCcEffectsForClipsIfNeeded(&next_clip, SkBlendMode::kSrcOver,
newly_built);
return current_effect_id_;
}
static bool IsNodeOnAncestorChain(const ClipPaintPropertyNode& find,
const ClipPaintPropertyNode& current,
const ClipPaintPropertyNode& ancestor) {
// Precondition: |ancestor| must be an (inclusive) ancestor of |current|
// otherwise the behavior is undefined.
// Returns true if node |find| is one of the node on the ancestor chain
// [current, ancestor). Returns false otherwise.
DCHECK(ancestor.IsAncestorOf(current));
for (const auto* node = &current; node != &ancestor; node = node->Parent()) {
if (node == &find)
return true;
}
return false;
}
SkBlendMode PropertyTreeManager::SynthesizeCcEffectsForClipsIfNeeded(
const ClipPaintPropertyNode* target_clip,
SkBlendMode delegated_blend,
bool effect_is_newly_built) {
if (delegated_blend != SkBlendMode::kSrcOver) {
// Exit all synthetic effect node for rounded clip if the next child has
// exotic blending mode because it has to access the backdrop of enclosing
// effect.
while (IsCurrentCcEffectSynthetic())
CloseCcEffect();
// An effect node can't omit render surface if it has child with exotic
// blending mode. See comments below for more detail.
// TODO(crbug.com/504464): Remove premature optimization here.
GetEffectTree().Node(current_effect_id_)->has_render_surface = true;
} else {
// Exit synthetic effects until there are no more synthesized clips below
// our lowest common ancestor.
const auto& lca = LowestCommonAncestor(*current_clip_, *target_clip);
while (current_clip_ != &lca) {
DCHECK(IsCurrentCcEffectSynthetic());
const auto* pre_exit_clip = current_clip_;
CloseCcEffect();
// We may run past the lowest common ancestor because it may not have
// been synthesized.
if (IsNodeOnAncestorChain(lca, *pre_exit_clip, *current_clip_))
break;
}
// If the effect is an existing node, i.e. already has at least one paint
// chunk or child effect, and by reaching here it implies we are going to
// attach either another paint chunk or child effect to it. We can no longer
// omit render surface for it even for opacity-only node.
// See comments in PropertyTreeManager::BuildEffectNodesRecursively().
// TODO(crbug.com/504464): Remove premature optimization here.
if (!effect_is_newly_built && !IsCurrentCcEffectSynthetic() &&
current_effect_->Opacity() != 1.f)
GetEffectTree().Node(current_effect_id_)->has_render_surface = true;
}
DCHECK(current_clip_->IsAncestorOf(*target_clip));
Vector<const ClipPaintPropertyNode*> pending_clips;
for (; target_clip != current_clip_; target_clip = target_clip->Parent()) {
DCHECK(target_clip);
bool should_synthesize =
target_clip->ClipRect().IsRounded() || target_clip->ClipPath();
if (should_synthesize)
pending_clips.push_back(target_clip);
}
for (size_t i = pending_clips.size(); i--;) {
const ClipPaintPropertyNode* next_clip = pending_clips[i];
// For each of clip synthesized, an isolation effect node needs to be
// created to enclose only the layers that should be masked by the clip.
cc::EffectNode& mask_isolation = *GetEffectTree().Node(
GetEffectTree().Insert(cc::EffectNode(), current_effect_id_));
// mask_isolation.stable_id will be assigned later when the effect is
// closed. For now the default value of INVALID_STABLE_ID is used.
// See PropertyTreeManager::EmitClipMaskLayer().
mask_isolation.clip_id = EnsureCompositorClipNode(next_clip);
mask_isolation.has_render_surface = true;
// Clip and kDstIn do not commute. This shall never be reached because
// kDstIn is only used internally to implement CSS clip-path and mask,
// and there is never a difference between the output clip of the effect
// and the mask content.
DCHECK(delegated_blend != SkBlendMode::kDstIn);
mask_isolation.blend_mode = delegated_blend;
delegated_blend = SkBlendMode::kSrcOver;
effect_stack_.emplace_back(
EffectStackEntry{current_effect_id_, current_effect_type_,
current_effect_, current_clip_});
current_effect_id_ = mask_isolation.id;
current_effect_type_ = CcEffectType::kSynthesizedClip;
current_clip_ = next_clip;
}
return delegated_blend;
}
bool PropertyTreeManager::BuildEffectNodesRecursively(
const EffectPaintPropertyNode* next_effect) {
if (next_effect == current_effect_)
return false;
DCHECK(next_effect);
bool newly_built = BuildEffectNodesRecursively(next_effect->Parent());
DCHECK_EQ(next_effect->Parent(), current_effect_);
#if DCHECK_IS_ON()
DCHECK(!effect_nodes_converted_.Contains(next_effect))
<< "Malformed paint artifact. Paint chunks under the same effect should "
"be contiguous.";
effect_nodes_converted_.insert(next_effect);
#endif
SkBlendMode used_blend_mode;
int output_clip_id;
if (next_effect->OutputClip()) {
used_blend_mode = SynthesizeCcEffectsForClipsIfNeeded(
next_effect->OutputClip(), next_effect->BlendMode(), newly_built);
output_clip_id = EnsureCompositorClipNode(next_effect->OutputClip());
} else {
while (IsCurrentCcEffectSynthetic())
CloseCcEffect();
// An effect node can't omit render surface if it has child with exotic
// blending mode, nor being opacity-only node with more than one child.
// TODO(crbug.com/504464): Remove premature optimization here.
if (next_effect->BlendMode() != SkBlendMode::kSrcOver ||
(!newly_built && current_effect_->Opacity() != 1.f))
GetEffectTree().Node(current_effect_id_)->has_render_surface = true;
used_blend_mode = next_effect->BlendMode();
output_clip_id = GetEffectTree().Node(current_effect_id_)->clip_id;
}
cc::EffectNode& effect_node = *GetEffectTree().Node(
GetEffectTree().Insert(cc::EffectNode(), current_effect_id_));
effect_node.stable_id =
next_effect->GetCompositorElementId().ToInternalValue();
effect_node.clip_id = output_clip_id;
// Every effect is supposed to have render surface enabled for grouping,
// but we can get away without one if the effect is opacity-only and has only
// one compositing child with kSrcOver blend mode. This is both for
// optimization and not introducing sub-pixel differences in layout tests.
// See PropertyTreeManager::switchToEffectNode() and above where we
// retrospectively enable render surface when more than one compositing child
// or a child with exotic blend mode is detected.
// TODO(crbug.com/504464): There is ongoing work in cc to delay render surface
// decision until later phase of the pipeline. Remove premature optimization
// here once the work is ready.
if (!next_effect->Filter().IsEmpty() ||
used_blend_mode != SkBlendMode::kSrcOver)
effect_node.has_render_surface = true;
effect_node.opacity = next_effect->Opacity();
if (next_effect->GetColorFilter() != kColorFilterNone) {
// Currently color filter is only used by SVG masks.
// We are cutting corner here by support only specific configuration.
DCHECK(next_effect->GetColorFilter() == kColorFilterLuminanceToAlpha);
DCHECK(used_blend_mode == SkBlendMode::kDstIn);
DCHECK(next_effect->Filter().IsEmpty());
effect_node.filters.Append(cc::FilterOperation::CreateReferenceFilter(
sk_make_sp<ColorFilterPaintFilter>(SkLumaColorFilter::Make(),
nullptr)));
} else {
effect_node.filters = next_effect->Filter().AsCcFilterOperations();
}
effect_node.blend_mode = used_blend_mode;
CompositorElementId compositor_element_id =
next_effect->GetCompositorElementId();
if (compositor_element_id) {
DCHECK(property_trees_.element_id_to_effect_node_index.find(
compositor_element_id) ==
property_trees_.element_id_to_effect_node_index.end());
property_trees_.element_id_to_effect_node_index[compositor_element_id] =
effect_node.id;
}
effect_stack_.emplace_back(EffectStackEntry{current_effect_id_,
current_effect_type_,
current_effect_, current_clip_});
current_effect_id_ = effect_node.id;
current_effect_type_ = CcEffectType::kEffect;
current_effect_ = next_effect;
if (next_effect->OutputClip())
current_clip_ = next_effect->OutputClip();
return true;
}
} // namespace blink