blob: 9b54fcb0c36e3a0bd9a29eee45e91d87a51437c7 [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 "third_party/blink/renderer/core/paint/paint_layer_painter.h"
#include "base/optional.h"
#include "third_party/blink/renderer/core/frame/local_frame_view.h"
#include "third_party/blink/renderer/core/frame/settings.h"
#include "third_party/blink/renderer/core/layout/layout_video.h"
#include "third_party/blink/renderer/core/layout/layout_view.h"
#include "third_party/blink/renderer/core/paint/clip_path_clipper.h"
#include "third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.h"
#include "third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.h"
#include "third_party/blink/renderer/core/paint/object_paint_properties.h"
#include "third_party/blink/renderer/core/paint/paint_info.h"
#include "third_party/blink/renderer/core/paint/paint_layer.h"
#include "third_party/blink/renderer/core/paint/paint_layer_paint_order_iterator.h"
#include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h"
#include "third_party/blink/renderer/core/paint/paint_timing_detector.h"
#include "third_party/blink/renderer/core/paint/scrollable_area_painter.h"
#include "third_party/blink/renderer/platform/geometry/float_point_3d.h"
#include "third_party/blink/renderer/platform/graphics/graphics_layer.h"
#include "third_party/blink/renderer/platform/graphics/paint/drawing_recorder.h"
#include "third_party/blink/renderer/platform/graphics/paint/geometry_mapper.h"
#include "third_party/blink/renderer/platform/graphics/paint/scoped_display_item_fragment.h"
#include "third_party/blink/renderer/platform/graphics/paint/scoped_paint_chunk_hint.h"
#include "third_party/blink/renderer/platform/graphics/paint/subsequence_recorder.h"
#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
namespace blink {
void PaintLayerPainter::Paint(GraphicsContext& context,
const CullRect& cull_rect,
const GlobalPaintFlags global_paint_flags,
PaintLayerFlags paint_flags) {
PaintLayerPaintingInfo painting_info(&paint_layer_, cull_rect,
global_paint_flags, PhysicalOffset());
if (!paint_layer_.PaintsIntoOwnOrGroupedBacking(global_paint_flags))
Paint(context, painting_info, paint_flags);
}
static ShouldRespectOverflowClipType ShouldRespectOverflowClip(
PaintLayerFlags paint_flags,
const LayoutObject& layout_object) {
return (paint_flags & kPaintLayerPaintingOverflowContents)
? kIgnoreOverflowClip
: kRespectOverflowClip;
}
bool PaintLayerPainter::PaintedOutputInvisible(const ComputedStyle& style) {
if (style.HasNonInitialBackdropFilter())
return false;
// Always paint when 'will-change: opacity' is present. Reduces jank for
// common animation implementation approaches, for example, an element that
// starts with opacity zero and later begins to animate.
if (style.HasWillChangeOpacityHint())
return false;
// 0.0004f < 1/2048. With 10-bit color channels (only available on the
// newest Macs; otherwise it's 8-bit), we see that an alpha of 1/2048 or
// less leads to a color output of less than 0.5 in all channels, hence
// not visible.
static const float kMinimumVisibleOpacity = 0.0004f;
if (style.Opacity() < kMinimumVisibleOpacity)
return true;
return false;
}
PaintResult PaintLayerPainter::Paint(
GraphicsContext& context,
const PaintLayerPaintingInfo& painting_info,
PaintLayerFlags paint_flags) {
if (paint_layer_.GetLayoutObject().GetFrameView()->ShouldThrottleRendering())
return kFullyPainted;
// Non self-painting layers without self-painting descendants don't need to be
// painted as their layoutObject() should properly paint itself.
if (!paint_layer_.IsSelfPaintingLayer() &&
!paint_layer_.HasSelfPaintingLayerDescendant())
return kFullyPainted;
// If this layer is totally invisible then there is nothing to paint.
// In CompositeAfterPaint we simplify this optimization by painting even when
// effectively invisible but skipping the painted content during layerization
// in PaintArtifactCompositor.
if (paint_layer_.PaintsWithTransparency(
painting_info.GetGlobalPaintFlags())) {
if (!RuntimeEnabledFeatures::CompositeAfterPaintEnabled() &&
PaintedOutputInvisible(paint_layer_.GetLayoutObject().StyleRef()))
return kFullyPainted;
paint_flags |= kPaintLayerHaveTransparency;
}
// If the transform can't be inverted, then don't paint anything.
if (paint_layer_.PaintsWithTransform(painting_info.GetGlobalPaintFlags()) &&
!paint_layer_.RenderableTransform(painting_info.GetGlobalPaintFlags())
.IsInvertible()) {
return kFullyPainted;
}
paint_flags |= kPaintLayerPaintingCompositingAllPhases;
return PaintLayerContents(context, painting_info, paint_flags);
}
static bool ShouldCreateSubsequence(
const PaintLayer& paint_layer,
const GraphicsContext& context,
const PaintLayerPaintingInfo& painting_info) {
// Caching is not needed during printing or painting previews.
if (paint_layer.GetLayoutObject().GetDocument().IsPrintingOrPaintingPreview())
return false;
if (context.GetPaintController().IsSkippingCache())
return false;
if (!paint_layer.SupportsSubsequenceCaching())
return false;
// Don't create subsequence during special painting to avoid cache conflict
// with normal painting.
if (painting_info.GetGlobalPaintFlags() &
kGlobalPaintFlattenCompositingLayers)
return false;
// Don't create subsequence for a composited layer because if it can be
// cached, we can skip the whole painting in GraphicsLayer::paint() with
// CachedDisplayItemList. This also avoids conflict of
// PaintLayer::previousXXX() when paintLayer is composited scrolling and is
// painted twice for GraphicsLayers of container and scrolling contents.
if (!RuntimeEnabledFeatures::CompositeAfterPaintEnabled() &&
(paint_layer.GetCompositingState() == kPaintsIntoOwnBacking))
return false;
return true;
}
static bool ShouldRepaintSubsequence(
PaintLayer& paint_layer,
const PaintLayerPaintingInfo& painting_info) {
// Repaint subsequence if the layer is marked for needing repaint.
if (paint_layer.SelfOrDescendantNeedsRepaint())
return true;
// Repaint if previously the layer may be clipped by cull rect, and cull rect
// changes.
if (!RuntimeEnabledFeatures::CullRectUpdateEnabled() &&
paint_layer.PreviousCullRect() != painting_info.cull_rect) {
if (paint_layer.PreviousPaintResult() == kMayBeClippedByCullRect)
return true;
// When PaintUnderInvalidationChecking is enabled, always repaint the
// subsequence when the paint rect changes because we will strictly match
// new and cached subsequences. Normally we can reuse the cached fully
// painted subsequence even if we would partially paint this time.
if (RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled())
return true;
}
return false;
}
static bool IsUnclippedLayoutView(const PaintLayer& layer) {
// If MainFrameClipsContent is false which means that WebPreferences::
// record_whole_document is true, we should not cull the scrolling contents
// of the main frame.
if (IsA<LayoutView>(layer.GetLayoutObject())) {
const auto* frame = layer.GetLayoutObject().GetFrame();
if (frame && !frame->ClipsContent())
return true;
}
return false;
}
bool PaintLayerPainter::ShouldUseInfiniteCullRect(
GlobalPaintFlags global_flags) {
bool is_printing = paint_layer_.GetLayoutObject().GetDocument().Printing();
if (IsUnclippedLayoutView(paint_layer_) && !is_printing)
return true;
// Cull rects and clips can't be propagated across a filter which moves
// pixels, since the input of the filter may be outside the cull rect /
// clips yet still result in painted output.
// TODO(wangxianzhu): With CullRectUpdate, we can let CullRect support
// mapping for pixel moving filters to avoid this infinite cull rect.
if (paint_layer_.HasFilterThatMovesPixels() &&
// However during printing, we don't want filter outset to cross page
// boundaries. This also avoids performance issue because the PDF renderer
// is super slow for big filters. Otherwise all filtered contents would
// appear in the painted result of every page.
// TODO(crbug.com/1098995): For now we don't adjust cull rect for clips.
// When we do, we need to check if we are painting under a real clip.
// This won't be a problem when we use block fragments for printing.
!is_printing)
return true;
// Cull rect mapping doesn't work under perspective in some cases.
// See http://crbug.com/887558 for details.
if (paint_layer_.GetLayoutObject().StyleRef().HasPerspective())
return true;
// We do not apply cull rect optimizations across transforms for two
// reasons:
// 1) Performance: We can optimize transform changes by not repainting.
// 2) Complexity: Difficulty updating clips when ancestor transforms
// change.
// For these reasons, we use an infinite dirty rect here.
// The reasons don't apply for CullRectUpdate.
if (!RuntimeEnabledFeatures::CullRectUpdateEnabled() &&
paint_layer_.PaintsWithTransform(global_flags) &&
// The reasons don't apply for printing though, because when we enter and
// leaving printing mode, full invalidations occur.
!is_printing)
return true;
return false;
}
void PaintLayerPainter::AdjustForPaintProperties(
const GraphicsContext& context,
PaintLayerPaintingInfo& painting_info,
PaintLayerFlags& paint_flags) {
const auto& first_fragment = paint_layer_.GetLayoutObject().FirstFragment();
bool should_use_infinite_cull_rect =
ShouldUseInfiniteCullRect(painting_info.GetGlobalPaintFlags());
if (should_use_infinite_cull_rect) {
painting_info.cull_rect = CullRect::Infinite();
// Avoid clipping during CollectFragments.
if (IsUnclippedLayoutView(paint_layer_))
paint_flags |= kPaintLayerPaintingOverflowContents;
}
if (painting_info.root_layer == &paint_layer_)
return;
if (!should_use_infinite_cull_rect) {
// painting_info.cull_rect is currently in |painting_info.root_layer|'s
// pixel-snapped border box space. We need to adjust it into
// |paint_layer_|'s space. This handles the following cases:
// - The current layer has PaintOffsetTranslation;
// - The current layer's transform state escapes the root layers contents
// transform, e.g. a fixed-position layer;
// - Scroll offsets.
const auto& first_root_fragment =
painting_info.root_layer->GetLayoutObject().FirstFragment();
const auto* source_transform =
&first_root_fragment.LocalBorderBoxProperties().Transform();
const auto& destination_transform =
first_fragment.LocalBorderBoxProperties().Transform();
if (source_transform == &destination_transform)
return;
if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) {
auto& cull_rect = painting_info.cull_rect;
// CullRect::ApplyTransforms() requires the cull rect in the source
// transform space. Convert cull_rect from the root layer's local space.
cull_rect.MoveBy(RoundedIntPoint(first_root_fragment.PaintOffset()));
base::Optional<CullRect> old_cull_rect;
if (!paint_layer_.SelfOrDescendantNeedsRepaint() &&
!RuntimeEnabledFeatures::CullRectUpdateEnabled()) {
old_cull_rect = paint_layer_.PreviousCullRect();
// Convert old_cull_rect into the layer's transform space.
old_cull_rect->MoveBy(RoundedIntPoint(first_fragment.PaintOffset()));
}
if (paint_flags & kPaintLayerPaintingOverflowContents) {
// Use PostScrollTranslation as the source transform to avoid clipping
// of the scrolling contents in CullRect::ApplyTransforms().
source_transform = &first_root_fragment.PostScrollTranslation();
// Map cull_rect into scrolling contents space (i.e. source_transform).
if (const auto* properties = first_root_fragment.PaintProperties()) {
if (const auto* scroll_translation = properties->ScrollTranslation())
cull_rect.Move(-scroll_translation->Translation2D());
}
}
cull_rect.ApplyTransforms(source_transform->Unalias(),
destination_transform.Unalias(), old_cull_rect);
// Convert cull_rect from the layer's transform space to the layer's local
// space.
cull_rect.MoveBy(-RoundedIntPoint(first_fragment.PaintOffset()));
} else if (!painting_info.cull_rect.IsInfinite()) {
auto rect = painting_info.cull_rect.Rect();
first_root_fragment.MapRectToFragment(first_fragment, rect);
painting_info.cull_rect = CullRect(rect);
}
}
// We reach here if the layer requires infinite cull rect or has different
// transform space from the current root layer. Use the current layer as
// the new root layer.
painting_info.root_layer = &paint_layer_;
// These flags no longer apply for the new root layer.
paint_flags &= ~kPaintLayerPaintingSkipRootBackground;
paint_flags &= ~kPaintLayerPaintingOverflowContents;
paint_flags &= ~kPaintLayerPaintingCompositingScrollingPhase;
if (first_fragment.PaintProperties() &&
first_fragment.PaintProperties()->PaintOffsetTranslation()) {
painting_info.sub_pixel_accumulation = first_fragment.PaintOffset();
}
}
static IntRect FirstFragmentVisualRect(const LayoutBoxModelObject& object) {
// We don't want to include overflowing contents.
PhysicalRect overflow_rect =
object.IsBox() ? To<LayoutBox>(object).PhysicalSelfVisualOverflowRect()
: object.PhysicalVisualOverflowRect();
overflow_rect.Move(object.FirstFragment().PaintOffset());
return EnclosingIntRect(overflow_rect);
}
PaintResult PaintLayerPainter::PaintLayerContents(
GraphicsContext& context,
const PaintLayerPaintingInfo& painting_info_arg,
PaintLayerFlags paint_flags_arg) {
DCHECK(paint_layer_.IsSelfPaintingLayer() ||
paint_layer_.HasSelfPaintingLayerDescendant());
const auto& object = paint_layer_.GetLayoutObject();
PaintResult result = kFullyPainted;
if (object.GetFrameView()->ShouldThrottleRendering())
return result;
// A paint layer should always have LocalBorderBoxProperties when it's ready
// for paint.
if (!object.FirstFragment().HasLocalBorderBoxProperties()) {
// TODO(crbug.com/848056): This can happen e.g. when we paint a filter
// referencing a SVG foreign object through feImage, especially when there
// is circular references. Should find a better solution.
if (!RuntimeEnabledFeatures::CullRectUpdateEnabled())
paint_layer_.SetPreviousCullRect(CullRect());
return kMayBeClippedByCullRect;
}
bool selection_drag_image_only = painting_info_arg.GetGlobalPaintFlags() &
kGlobalPaintSelectionDragImageOnly;
if (selection_drag_image_only && !object.IsSelected())
return result;
IgnorePaintTimingScope ignore_paint_timing;
if (object.StyleRef().Opacity() == 0.0f) {
IgnorePaintTimingScope::IncrementIgnoreDepth();
}
// Explicitly compute opacity of documentElement, as it is special-cased in
// Largest Contentful Paint.
bool is_document_element_invisible = false;
if (const auto* document_element = object.GetDocument().documentElement()) {
if (document_element->GetLayoutObject() &&
document_element->GetLayoutObject()->StyleRef().Opacity() == 0.0f) {
is_document_element_invisible = true;
}
}
IgnorePaintTimingScope::SetIsDocumentElementInvisible(
is_document_element_invisible);
PaintLayerFlags paint_flags = paint_flags_arg;
PaintLayerPaintingInfo painting_info = painting_info_arg;
AdjustForPaintProperties(context, painting_info, paint_flags);
bool is_self_painting_layer = paint_layer_.IsSelfPaintingLayer();
bool is_painting_overlay_overflow_controls =
paint_flags & kPaintLayerPaintingOverlayOverflowControls;
bool is_painting_scrolling_content =
paint_flags & kPaintLayerPaintingCompositingScrollingPhase;
bool is_painting_composited_foreground =
paint_flags & kPaintLayerPaintingCompositingForegroundPhase;
bool is_painting_composited_background =
paint_flags & kPaintLayerPaintingCompositingBackgroundPhase;
bool is_painting_composited_decoration =
paint_flags & kPaintLayerPaintingCompositingDecorationPhase;
bool is_painting_overflow_contents =
paint_flags & kPaintLayerPaintingOverflowContents;
bool is_painting_mask = paint_flags & kPaintLayerPaintingCompositingMaskPhase;
// Outline always needs to be painted even if we have no visible content.
// It is painted as part of the decoration phase which paints content that
// is not scrolled and should be above scrolled content.
bool should_paint_self_outline =
is_self_painting_layer && !is_painting_overlay_overflow_controls &&
is_painting_composited_decoration && object.StyleRef().HasOutline();
PhysicalOffset subpixel_accumulation =
(!RuntimeEnabledFeatures::CompositeAfterPaintEnabled() &&
!(painting_info.GetGlobalPaintFlags() &
kGlobalPaintFlattenCompositingLayers) &&
paint_layer_.GetCompositingState() == kPaintsIntoOwnBacking)
? paint_layer_.SubpixelAccumulation()
: painting_info.sub_pixel_accumulation;
ShouldRespectOverflowClipType respect_overflow_clip =
ShouldRespectOverflowClip(paint_flags, object);
bool should_paint_content =
paint_layer_.HasVisibleContent() &&
// Content under a LayoutSVGHiddenContainer is auxiliary resources for
// painting. Foreign content should never paint in this situation, as it
// is primary, not auxiliary.
!paint_layer_.IsUnderSVGHiddenContainer() && is_self_painting_layer &&
!is_painting_overlay_overflow_controls;
bool should_create_subsequence =
should_paint_content &&
ShouldCreateSubsequence(paint_layer_, context, painting_info);
base::Optional<SubsequenceRecorder> subsequence_recorder;
if (should_create_subsequence) {
if (!ShouldRepaintSubsequence(paint_layer_, painting_info) &&
SubsequenceRecorder::UseCachedSubsequenceIfPossible(context,
paint_layer_)) {
return paint_layer_.PreviousPaintResult();
}
DCHECK(paint_layer_.SupportsSubsequenceCaching());
subsequence_recorder.emplace(context, paint_layer_);
}
PhysicalOffset offset_from_root =
paint_layer_.GetLayoutObject().FirstFragment().PaintOffset();
if (const PaintLayer* root = painting_info.root_layer)
offset_from_root -= root->GetLayoutObject().FirstFragment().PaintOffset();
offset_from_root += subpixel_accumulation;
IntRect visual_rect = FirstFragmentVisualRect(object);
if (RuntimeEnabledFeatures::CullRectUpdateEnabled()) {
if (object.FirstFragment().NextFragment()) {
result = kMayBeClippedByCullRect;
} else if (!object.FirstFragment().GetCullRect().Rect().Contains(
visual_rect)) {
result = kMayBeClippedByCullRect;
} else if (const auto* box = DynamicTo<LayoutBox>(object)) {
PhysicalRect contents_visual_rect =
box->PhysicalContentsVisualOverflowRect();
contents_visual_rect.Move(object.FirstFragment().PaintOffset());
if (!PhysicalRect(object.FirstFragment().GetContentsCullRect().Rect())
.Contains(contents_visual_rect)) {
result = kMayBeClippedByCullRect;
}
}
} else {
PhysicalRect bounds = paint_layer_.PhysicalBoundingBox(offset_from_root);
if (!PhysicalRect(painting_info.cull_rect.Rect()).Contains(bounds))
result = kMayBeClippedByCullRect;
}
PaintLayerPaintingInfo local_painting_info(painting_info);
local_painting_info.sub_pixel_accumulation = subpixel_accumulation;
PaintLayerFragments layer_fragments;
if (should_paint_content || should_paint_self_outline ||
is_painting_overlay_overflow_controls) {
// Collect the fragments. This will compute the clip rectangles and paint
// offsets for each layer fragment.
paint_layer_.CollectFragments(
layer_fragments, local_painting_info.root_layer,
&local_painting_info.cull_rect, kIgnoreOverlayScrollbarSize,
respect_overflow_clip, &offset_from_root,
local_painting_info.sub_pixel_accumulation);
// PaintLayer::CollectFragments depends on the paint dirty rect in
// complicated ways. For now, always assume a partially painted output
// for fragmented content.
if (layer_fragments.size() > 1)
result = kMayBeClippedByCullRect;
if (should_paint_content) {
should_paint_content = AtLeastOneFragmentIntersectsDamageRect(
layer_fragments, local_painting_info, paint_flags, offset_from_root);
if (!should_paint_content)
result = kMayBeClippedByCullRect;
}
}
bool is_painting_root_layer = (&paint_layer_) == painting_info.root_layer;
bool should_paint_background =
should_paint_content && !selection_drag_image_only &&
(is_painting_composited_background ||
(is_painting_root_layer &&
!(paint_flags & kPaintLayerPaintingSkipRootBackground)));
bool should_paint_neg_z_order_list =
!is_painting_overlay_overflow_controls &&
(is_painting_scrolling_content ? is_painting_overflow_contents
: is_painting_composited_background);
bool should_paint_own_contents =
is_painting_composited_foreground && should_paint_content;
bool should_paint_normal_flow_and_pos_z_order_lists =
is_painting_composited_foreground &&
!is_painting_overlay_overflow_controls;
bool is_video = IsA<LayoutVideo>(object);
base::Optional<ScopedPaintChunkHint> paint_chunk_hint;
if (should_paint_content) {
paint_chunk_hint.emplace(context.GetPaintController(),
object.FirstFragment().LocalBorderBoxProperties(),
paint_layer_, DisplayItem::kLayerChunk,
visual_rect);
}
if (should_paint_background) {
PaintBackgroundForFragmentsWithPhase(PaintPhase::kSelfBlockBackgroundOnly,
layer_fragments, context,
local_painting_info, paint_flags);
}
if (should_paint_neg_z_order_list) {
if (PaintChildren(kNegativeZOrderChildren, context, painting_info,
paint_flags) == kMayBeClippedByCullRect)
result = kMayBeClippedByCullRect;
}
if (should_paint_own_contents) {
base::Optional<ScopedPaintChunkHint> paint_chunk_hint_foreground;
if (paint_chunk_hint && paint_chunk_hint->HasCreatedPaintChunk()) {
// Hint a foreground chunk if we have created any chunks, to give the
// paint chunk after the previous forced paint chunks a stable id.
paint_chunk_hint_foreground.emplace(
context.GetPaintController(), paint_layer_,
DisplayItem::kLayerChunkForeground, visual_rect);
}
if (selection_drag_image_only) {
PaintForegroundForFragmentsWithPhase(PaintPhase::kSelectionDragImage,
layer_fragments, context,
local_painting_info, paint_flags);
} else {
PaintForegroundForFragments(layer_fragments, context, local_painting_info,
paint_flags);
}
}
if (!is_video && should_paint_self_outline) {
PaintBackgroundForFragmentsWithPhase(PaintPhase::kSelfOutlineOnly,
layer_fragments, context,
local_painting_info, paint_flags);
}
if (should_paint_normal_flow_and_pos_z_order_lists) {
if (PaintChildren(kNormalFlowAndPositiveZOrderChildren, context,
painting_info, paint_flags) == kMayBeClippedByCullRect)
result = kMayBeClippedByCullRect;
}
if (paint_layer_.GetScrollableArea() &&
paint_layer_.GetScrollableArea()
->ShouldOverflowControlsPaintAsOverlay()) {
if (is_painting_overlay_overflow_controls ||
!paint_layer_.NeedsReorderOverlayOverflowControls()) {
PaintOverlayOverflowControlsForFragments(
layer_fragments, context, local_painting_info, paint_flags);
}
}
if (is_video && should_paint_self_outline) {
// We paint outlines for video later so that they aren't obscured by the
// video controls.
PaintBackgroundForFragmentsWithPhase(PaintPhase::kSelfOutlineOnly,
layer_fragments, context,
local_painting_info, paint_flags);
}
if (is_painting_mask && should_paint_content && !selection_drag_image_only) {
if (const auto* properties = object.FirstFragment().PaintProperties()) {
if (properties->Mask()) {
PaintBackgroundForFragmentsWithPhase(PaintPhase::kMask, layer_fragments,
context, local_painting_info,
paint_flags);
}
if (properties->ClipPathMask()) {
PhysicalOffset visual_offset_from_root =
paint_layer_.EnclosingPaginationLayer()
? paint_layer_.VisualOffsetFromAncestor(
local_painting_info.root_layer, subpixel_accumulation)
: offset_from_root;
ClipPathClipper::PaintClipPathAsMaskImage(context, object, object,
visual_offset_from_root);
}
}
}
paint_layer_.SetPreviousPaintResult(result);
if (!RuntimeEnabledFeatures::CullRectUpdateEnabled())
paint_layer_.SetPreviousCullRect(local_painting_info.cull_rect);
return result;
}
bool PaintLayerPainter::AtLeastOneFragmentIntersectsDamageRect(
PaintLayerFragments& fragments,
const PaintLayerPaintingInfo& local_painting_info,
PaintLayerFlags local_paint_flags,
const PhysicalOffset& offset_from_root) {
if (&paint_layer_ == local_painting_info.root_layer &&
(local_paint_flags & kPaintLayerPaintingOverflowContents))
return true;
// Skip the optimization if the layer is fragmented to avoid complexity
// about overflows in fragments. LayoutObject painters will do cull rect
// optimization later.
if (paint_layer_.EnclosingPaginationLayer() || fragments.size() > 1)
return true;
return paint_layer_.IntersectsDamageRect(fragments[0].layer_bounds,
fragments[0].background_rect.Rect(),
offset_from_root);
}
template <typename Function>
static void ForAllFragments(GraphicsContext& context,
const PaintLayerFragments& fragments,
const Function& function) {
for (wtf_size_t i = 0; i < fragments.size(); ++i) {
base::Optional<ScopedDisplayItemFragment> scoped_display_item_fragment;
if (i)
scoped_display_item_fragment.emplace(context, i);
function(fragments[i]);
}
}
PaintResult PaintLayerPainter::PaintChildren(
PaintLayerIteration children_to_visit,
GraphicsContext& context,
const PaintLayerPaintingInfo& painting_info,
PaintLayerFlags paint_flags) {
PaintResult result = kFullyPainted;
if (!paint_layer_.HasSelfPaintingLayerDescendant())
return result;
if (paint_layer_.GetLayoutObject().ChildPaintBlockedByDisplayLock())
return result;
PaintLayerPaintOrderIterator iterator(paint_layer_, children_to_visit);
while (PaintLayer* child = iterator.Next()) {
// If this Layer should paint into its own backing or a grouped backing,
// that will be done via CompositedLayerMapping::PaintContents() and
// CompositedLayerMapping::DoPaintTask().
if (child->PaintsIntoOwnOrGroupedBacking(
painting_info.GetGlobalPaintFlags()))
continue;
if (child->IsReplacedNormalFlowStacking())
continue;
if (PaintLayerPainter(*child).Paint(context, painting_info, paint_flags) ==
kMayBeClippedByCullRect)
result = kMayBeClippedByCullRect;
if (const auto* layers_painting_overlay_overflow_controls_after =
iterator.LayersPaintingOverlayOverflowControlsAfter(child)) {
for (auto* reparent_overflow_controls_layer :
*layers_painting_overlay_overflow_controls_after) {
DCHECK(reparent_overflow_controls_layer
->NeedsReorderOverlayOverflowControls());
if (PaintLayerPainter(*reparent_overflow_controls_layer)
.Paint(context, painting_info,
kPaintLayerPaintingOverlayOverflowControls) ==
kMayBeClippedByCullRect)
result = kMayBeClippedByCullRect;
}
}
}
return result;
}
void PaintLayerPainter::PaintOverlayOverflowControlsForFragments(
const PaintLayerFragments& layer_fragments,
GraphicsContext& context,
const PaintLayerPaintingInfo& painting_info,
PaintLayerFlags paint_flags) {
DCHECK(
paint_layer_.GetScrollableArea() &&
paint_layer_.GetScrollableArea()->ShouldOverflowControlsPaintAsOverlay());
// We don't need to paint composited overflow controls.
if (paint_layer_.GetScrollableArea()->HasLayerForHorizontalScrollbar() ||
paint_layer_.GetScrollableArea()->HasLayerForVerticalScrollbar() ||
paint_layer_.GetScrollableArea()->HasLayerForScrollCorner())
return;
PaintBackgroundForFragmentsWithPhase(PaintPhase::kOverlayOverflowControls,
layer_fragments, context, painting_info,
paint_flags);
}
void PaintLayerPainter::PaintFragmentWithPhase(
PaintPhase phase,
const PaintLayerFragment& fragment,
GraphicsContext& context,
const CullRect& cull_rect,
const PaintLayerPaintingInfo& painting_info,
PaintLayerFlags paint_flags) {
DCHECK(paint_layer_.IsSelfPaintingLayer());
auto chunk_properties = fragment.fragment_data->LocalBorderBoxProperties();
if (phase == PaintPhase::kMask) {
const auto* properties = fragment.fragment_data->PaintProperties();
DCHECK(properties);
DCHECK(properties->Mask());
chunk_properties.SetEffect(*properties->Mask());
}
ScopedPaintChunkProperties fragment_paint_chunk_properties(
context.GetPaintController(), chunk_properties, paint_layer_,
DisplayItem::PaintPhaseToDrawingType(phase));
PaintInfo paint_info(
context, cull_rect, phase, painting_info.GetGlobalPaintFlags(),
paint_flags, &painting_info.root_layer->GetLayoutObject(),
fragment.fragment_data ? fragment.fragment_data->LogicalTopInFlowThread()
: LayoutUnit());
if (paint_layer_.GetLayoutObject().ChildPaintBlockedByDisplayLock())
paint_info.SetDescendantPaintingBlocked(true);
if (fragment.physical_fragment)
NGBoxFragmentPainter(*fragment.physical_fragment).Paint(paint_info);
else
paint_layer_.GetLayoutObject().Paint(paint_info);
}
static CullRect LegacyCullRect(const PaintLayerFragment& fragment,
const ClipRect& clip_rect) {
DCHECK(!RuntimeEnabledFeatures::CullRectUpdateEnabled());
PhysicalRect new_cull_rect(clip_rect.Rect());
// Now |new_cull_rect| is in the pixel-snapped border box space of
// |fragment.root_fragment_data|. Adjust it to the containing transform node's
// space in which we will paint.
new_cull_rect.Move(PhysicalOffset(
RoundedIntPoint(fragment.root_fragment_data->PaintOffset())));
return CullRect(PixelSnappedIntRect(new_cull_rect));
}
void PaintLayerPainter::PaintBackgroundForFragmentsWithPhase(
PaintPhase phase,
const PaintLayerFragments& layer_fragments,
GraphicsContext& context,
const PaintLayerPaintingInfo& local_painting_info,
PaintLayerFlags paint_flags) {
ForAllFragments(
context, layer_fragments, [&](const PaintLayerFragment& fragment) {
CullRect cull_rect =
RuntimeEnabledFeatures::CullRectUpdateEnabled()
? fragment.fragment_data->GetCullRect()
: LegacyCullRect(fragment, fragment.background_rect);
if (!cull_rect.Rect().IsEmpty()) {
PaintFragmentWithPhase(phase, fragment, context, cull_rect,
local_painting_info, paint_flags);
}
});
}
void PaintLayerPainter::PaintForegroundForFragments(
const PaintLayerFragments& layer_fragments,
GraphicsContext& context,
const PaintLayerPaintingInfo& local_painting_info,
PaintLayerFlags paint_flags) {
PaintForegroundForFragmentsWithPhase(
PaintPhase::kDescendantBlockBackgroundsOnly, layer_fragments, context,
local_painting_info, paint_flags);
if (paint_layer_.GetLayoutObject().GetDocument().InForcedColorsMode()) {
PaintForegroundForFragmentsWithPhase(PaintPhase::kForcedColorsModeBackplate,
layer_fragments, context,
local_painting_info, paint_flags);
}
if (RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled() ||
paint_layer_.NeedsPaintPhaseFloat()) {
PaintForegroundForFragmentsWithPhase(PaintPhase::kFloat, layer_fragments,
context, local_painting_info,
paint_flags);
}
PaintForegroundForFragmentsWithPhase(PaintPhase::kForeground, layer_fragments,
context, local_painting_info,
paint_flags);
if (RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled() ||
paint_layer_.NeedsPaintPhaseDescendantOutlines()) {
PaintForegroundForFragmentsWithPhase(PaintPhase::kDescendantOutlinesOnly,
layer_fragments, context,
local_painting_info, paint_flags);
}
}
void PaintLayerPainter::PaintForegroundForFragmentsWithPhase(
PaintPhase phase,
const PaintLayerFragments& layer_fragments,
GraphicsContext& context,
const PaintLayerPaintingInfo& local_painting_info,
PaintLayerFlags paint_flags) {
ForAllFragments(
context, layer_fragments, [&](const PaintLayerFragment& fragment) {
CullRect cull_rect;
if (RuntimeEnabledFeatures::CullRectUpdateEnabled()) {
// In CullRectUpdate, this function is the same as
// PaintBackgroundForFragmentsWithPhase(). The contents cull rect will
// be applied by ScopedBoxContentsPaintState.
cull_rect = fragment.fragment_data->GetCullRect();
} else {
cull_rect = LegacyCullRect(fragment, fragment.foreground_rect);
}
if (!cull_rect.Rect().IsEmpty()) {
PaintFragmentWithPhase(phase, fragment, context, cull_rect,
local_painting_info, paint_flags);
}
});
}
void PaintLayerPainter::PaintOverlayOverflowControls(
GraphicsContext& context,
const CullRect& cull_rect,
const GlobalPaintFlags paint_flags) {
PaintLayerPaintingInfo painting_info(&paint_layer_, cull_rect, paint_flags,
PhysicalOffset());
Paint(context, painting_info, kPaintLayerPaintingOverlayOverflowControls);
}
} // namespace blink