blob: fe86c723d1598d1d3ae82013acfe5219534e7351 [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 "core/paint/BoxPainter.h"
#include "core/layout/BackgroundBleedAvoidance.h"
#include "core/layout/LayoutBox.h"
#include "core/layout/LayoutObject.h"
#include "core/layout/LayoutTable.h"
#include "core/layout/LayoutTheme.h"
#include "core/paint/AdjustPaintOffsetScope.h"
#include "core/paint/BackgroundImageGeometry.h"
#include "core/paint/BoxDecorationData.h"
#include "core/paint/BoxModelObjectPainter.h"
#include "core/paint/BoxPainterBase.h"
#include "core/paint/NinePieceImagePainter.h"
#include "core/paint/ObjectPainter.h"
#include "core/paint/PaintInfo.h"
#include "core/paint/ScrollRecorder.h"
#include "core/paint/ThemePainter.h"
#include "core/paint/compositing/CompositedLayerMapping.h"
#include "platform/LengthFunctions.h"
#include "platform/geometry/LayoutPoint.h"
#include "platform/graphics/GraphicsContextStateSaver.h"
#include "platform/graphics/paint/DisplayItemCacheSkipper.h"
#include "platform/graphics/paint/DrawingRecorder.h"
#include "platform/graphics/paint/ScopedPaintChunkProperties.h"
#include "platform/wtf/Optional.h"
namespace blink {
void BoxPainter::Paint(const PaintInfo& paint_info,
const LayoutPoint& paint_offset) {
// Default implementation. Just pass paint through to the children.
AdjustPaintOffsetScope adjustment(layout_box_, paint_info, paint_offset);
PaintChildren(adjustment.GetPaintInfo(), adjustment.AdjustedPaintOffset());
}
void BoxPainter::PaintChildren(const PaintInfo& paint_info,
const LayoutPoint& paint_offset) {
PaintInfo child_info(paint_info);
for (LayoutObject* child = layout_box_.SlowFirstChild(); child;
child = child->NextSibling())
child->Paint(child_info, paint_offset);
}
void BoxPainter::PaintBoxDecorationBackground(const PaintInfo& paint_info,
const LayoutPoint& paint_offset) {
LayoutRect paint_rect;
Optional<ScrollRecorder> scroll_recorder;
Optional<ScopedPaintChunkProperties> scoped_scroll_property;
if (BoxModelObjectPainter::
IsPaintingBackgroundOfPaintContainerIntoScrollingContentsLayer(
&layout_box_, paint_info)) {
// For the case where we are painting the background into the scrolling
// contents layer of a composited scroller we need to include the entire
// overflow rect.
paint_rect = layout_box_.LayoutOverflowRect();
scroll_recorder.emplace(paint_info.context, layout_box_, paint_info.phase,
layout_box_.ScrolledContentOffset());
if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
DCHECK_EQ(layout_box_.HasHiddenBackface(),
paint_info.context.GetPaintController()
.CurrentPaintChunkProperties()
.backface_hidden);
if (const auto* fragment = paint_info.FragmentToPaint(layout_box_)) {
scoped_scroll_property.emplace(
paint_info.context.GetPaintController(),
fragment->ContentsProperties(), layout_box_,
DisplayItem::PaintPhaseToScrollType(paint_info.phase));
}
}
// The background painting code assumes that the borders are part of the
// paintRect so we expand the paintRect by the border size when painting the
// background into the scrolling contents layer.
paint_rect.Expand(layout_box_.BorderBoxOutsets());
} else {
paint_rect = layout_box_.BorderBoxRect();
}
paint_rect.MoveBy(paint_offset);
PaintBoxDecorationBackgroundWithRect(paint_info, paint_offset, paint_rect);
}
LayoutRect BoxPainter::BoundsForDrawingRecorder(
const PaintInfo& paint_info,
const LayoutPoint& adjusted_paint_offset) {
LayoutRect bounds =
BoxModelObjectPainter::
IsPaintingBackgroundOfPaintContainerIntoScrollingContentsLayer(
&layout_box_, paint_info)
? layout_box_.LayoutOverflowRect()
: layout_box_.SelfVisualOverflowRect();
bounds.MoveBy(adjusted_paint_offset);
return bounds;
}
void BoxPainter::PaintBoxDecorationBackgroundWithRect(
const PaintInfo& paint_info,
const LayoutPoint& paint_offset,
const LayoutRect& paint_rect) {
bool painting_overflow_contents = BoxModelObjectPainter::
IsPaintingBackgroundOfPaintContainerIntoScrollingContentsLayer(
&layout_box_, paint_info);
const ComputedStyle& style = layout_box_.StyleRef();
Optional<DisplayItemCacheSkipper> cache_skipper;
// Disable cache in under-invalidation checking mode for MediaSliderPart
// because we always paint using the latest data (buffered ranges, current
// time and duration) which may be different from the cached data, and for
// delayed-invalidation object because it may change before it's actually
// invalidated.
if (RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled() &&
(style.Appearance() == kMediaSliderPart ||
layout_box_.FullPaintInvalidationReason() ==
PaintInvalidationReason::kDelayedFull)) {
cache_skipper.emplace(paint_info.context);
}
const DisplayItemClient& display_item_client =
painting_overflow_contents ? static_cast<const DisplayItemClient&>(
*layout_box_.Layer()
->GetCompositedLayerMapping()
->ScrollingContentsLayer())
: layout_box_;
if (DrawingRecorder::UseCachedDrawingIfPossible(
paint_info.context, display_item_client,
DisplayItem::kBoxDecorationBackground))
return;
DrawingRecorder recorder(paint_info.context, display_item_client,
DisplayItem::kBoxDecorationBackground);
BoxDecorationData box_decoration_data(layout_box_);
GraphicsContextStateSaver state_saver(paint_info.context, false);
if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled() &&
LayoutRect(EnclosingIntRect(paint_rect)) == paint_rect &&
// TODO(wkorman): Rename BoundsForDrawingRecorder as it's used
// here for another purpose.
layout_box_.BackgroundIsKnownToBeOpaqueInRect(
BoundsForDrawingRecorder(paint_info, LayoutPoint())))
recorder.SetKnownToBeOpaque();
if (!painting_overflow_contents) {
// FIXME: Should eventually give the theme control over whether the box
// shadow should paint, since controls could have custom shadows of their
// own.
BoxPainterBase::PaintNormalBoxShadow(paint_info, paint_rect, style);
if (BleedAvoidanceIsClipping(box_decoration_data.bleed_avoidance)) {
state_saver.Save();
FloatRoundedRect border = style.GetRoundedBorderFor(paint_rect);
paint_info.context.ClipRoundedRect(border);
if (box_decoration_data.bleed_avoidance == kBackgroundBleedClipLayer)
paint_info.context.BeginLayer();
}
}
// If we have a native theme appearance, paint that before painting our
// background. The theme will tell us whether or not we should also paint the
// CSS background.
IntRect snapped_paint_rect(PixelSnappedIntRect(paint_rect));
ThemePainter& theme_painter = LayoutTheme::GetTheme().Painter();
bool theme_painted =
box_decoration_data.has_appearance &&
!theme_painter.Paint(layout_box_, paint_info, snapped_paint_rect);
bool should_paint_background =
!theme_painted && (!paint_info.SkipRootBackground() ||
paint_info.PaintContainer() != &layout_box_);
if (should_paint_background) {
PaintBackground(paint_info, paint_rect,
box_decoration_data.background_color,
box_decoration_data.bleed_avoidance);
if (box_decoration_data.has_appearance) {
theme_painter.PaintDecorations(layout_box_.GetNode(),
layout_box_.GetDocument(), style,
paint_info, snapped_paint_rect);
}
}
if (!painting_overflow_contents) {
BoxPainterBase::PaintInsetBoxShadowWithBorderRect(paint_info, paint_rect,
style);
// The theme will tell us whether or not we should also paint the CSS
// border.
if (box_decoration_data.has_border_decoration &&
(!box_decoration_data.has_appearance ||
(!theme_painted &&
LayoutTheme::GetTheme().Painter().PaintBorderOnly(
layout_box_.GetNode(), style, paint_info, snapped_paint_rect))) &&
!(layout_box_.IsTable() &&
ToLayoutTable(&layout_box_)->ShouldCollapseBorders())) {
BoxPainterBase::PaintBorder(
layout_box_, layout_box_.GetDocument(), layout_box_.GeneratingNode(),
paint_info, paint_rect, style, box_decoration_data.bleed_avoidance);
}
}
if (box_decoration_data.bleed_avoidance == kBackgroundBleedClipLayer)
paint_info.context.EndLayer();
}
void BoxPainter::PaintBackground(const PaintInfo& paint_info,
const LayoutRect& paint_rect,
const Color& background_color,
BackgroundBleedAvoidance bleed_avoidance) {
if (layout_box_.IsDocumentElement())
return;
if (layout_box_.BackgroundStolenForBeingBody())
return;
if (layout_box_.BackgroundIsKnownToBeObscured())
return;
BackgroundImageGeometry geometry(layout_box_);
BoxModelObjectPainter box_model_painter(layout_box_);
box_model_painter.PaintFillLayers(paint_info, background_color,
layout_box_.Style()->BackgroundLayers(),
paint_rect, geometry, bleed_avoidance);
}
void BoxPainter::PaintMask(const PaintInfo& paint_info,
const LayoutPoint& paint_offset) {
DCHECK_EQ(PaintPhase::kMask, paint_info.phase);
DCHECK(layout_box_.HasMask());
if (layout_box_.Style()->Visibility() != EVisibility::kVisible)
return;
if (DrawingRecorder::UseCachedDrawingIfPossible(
paint_info.context, layout_box_, paint_info.phase))
return;
DrawingRecorder recorder(paint_info.context, layout_box_, paint_info.phase);
LayoutRect paint_rect = LayoutRect(paint_offset, layout_box_.Size());
PaintMaskImages(paint_info, paint_rect);
}
void BoxPainter::PaintMaskImages(const PaintInfo& paint_info,
const LayoutRect& paint_rect) {
// Figure out if we need to push a transparency layer to render our mask.
bool push_transparency_layer = false;
bool all_mask_images_loaded = true;
DCHECK(layout_box_.HasLayer());
if (!layout_box_.Layer()->MaskBlendingAppliedByCompositor(paint_info)) {
push_transparency_layer = true;
StyleImage* mask_box_image = layout_box_.Style()->MaskBoxImage().GetImage();
const FillLayer& mask_layers = layout_box_.Style()->MaskLayers();
// Don't render a masked element until all the mask images have loaded, to
// prevent a flash of unmasked content.
if (mask_box_image)
all_mask_images_loaded &= mask_box_image->IsLoaded();
all_mask_images_loaded &= mask_layers.ImagesAreLoaded();
paint_info.context.BeginLayer(1, SkBlendMode::kDstIn);
}
if (all_mask_images_loaded) {
BackgroundImageGeometry geometry(layout_box_);
BoxModelObjectPainter box_model_painter(layout_box_);
box_model_painter.PaintFillLayers(paint_info, Color::kTransparent,
layout_box_.Style()->MaskLayers(),
paint_rect, geometry);
NinePieceImagePainter::Paint(
paint_info.context, layout_box_, layout_box_.GetDocument(),
layout_box_.GeneratingNode(), paint_rect, layout_box_.StyleRef(),
layout_box_.StyleRef().MaskBoxImage());
}
if (push_transparency_layer)
paint_info.context.EndLayer();
}
void BoxPainter::PaintClippingMask(const PaintInfo& paint_info,
const LayoutPoint& paint_offset) {
// SPv175 always paints clipping mask in PaintLayerPainter.
DCHECK(!RuntimeEnabledFeatures::SlimmingPaintV175Enabled());
DCHECK(paint_info.phase == PaintPhase::kClippingMask);
if (layout_box_.Style()->Visibility() != EVisibility::kVisible)
return;
if (!layout_box_.Layer() ||
layout_box_.Layer()->GetCompositingState() != kPaintsIntoOwnBacking)
return;
if (DrawingRecorder::UseCachedDrawingIfPossible(
paint_info.context, layout_box_, paint_info.phase))
return;
IntRect paint_rect =
PixelSnappedIntRect(LayoutRect(paint_offset, layout_box_.Size()));
DrawingRecorder recorder(paint_info.context, layout_box_, paint_info.phase);
paint_info.context.FillRect(paint_rect, Color::kBlack);
}
} // namespace blink