| Index: third_party/WebKit/Source/core/paint/NGBoxFragmentPainter.cpp
|
| diff --git a/third_party/WebKit/Source/core/paint/NGBoxFragmentPainter.cpp b/third_party/WebKit/Source/core/paint/NGBoxFragmentPainter.cpp
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..0ed2a1de7963bfd85dfceeeef83490547b07a882
|
| --- /dev/null
|
| +++ b/third_party/WebKit/Source/core/paint/NGBoxFragmentPainter.cpp
|
| @@ -0,0 +1,485 @@
|
| +// Copyright 2017 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/NGBoxFragmentPainter.h"
|
| +
|
| +#include "core/layout/BackgroundBleedAvoidance.h"
|
| +#include "core/layout/ng/inline/ng_physical_line_box_fragment.h"
|
| +#include "core/layout/ng/inline/ng_physical_text_fragment.h"
|
| +#include "core/layout/ng/ng_physical_box_fragment.h"
|
| +#include "core/paint/BoxBorderPainter.h"
|
| +#include "core/paint/BoxDecorationData.h"
|
| +#include "core/paint/NGTextFragmentPainter.h"
|
| +#include "core/paint/PaintInfo.h"
|
| +#include "core/paint/PaintLayer.h"
|
| +#include "core/paint/RoundedInnerRectClipper.h"
|
| +#include "core/style/FillLayer.h"
|
| +#include "platform/geometry/LayoutRectOutsets.h"
|
| +#include "platform/graphics/GraphicsContextStateSaver.h"
|
| +#include "platform/graphics/paint/DrawingRecorder.h"
|
| +
|
| +namespace blink {
|
| +
|
| +void NGBoxFragmentPainter::Paint(const PaintInfo& paint_info,
|
| + const LayoutPoint& paint_offset) {
|
| + PaintChildren(paint_info, paint_offset);
|
| +}
|
| +
|
| +void NGBoxFragmentPainter::PaintChildren(const PaintInfo& paint_info,
|
| + const LayoutPoint& paint_offset) {
|
| + PaintInfo child_info(paint_info);
|
| + for (const auto& child : box_fragment_->Children()) {
|
| + if (child->Type() == NGPhysicalBoxFragment::kFragmentLineBox) {
|
| + const NGPhysicalLineBoxFragment* line_box_fragment =
|
| + ToNGPhysicalLineBoxFragment(child.Get());
|
| + PaintLineBox(line_box_fragment, child_info, paint_offset);
|
| + }
|
| + }
|
| +}
|
| +
|
| +void NGBoxFragmentPainter::PaintLineBox(
|
| + const NGPhysicalLineBoxFragment* fragment,
|
| + const PaintInfo& paint_info,
|
| + const LayoutPoint& paint_offset) {
|
| + // TODO: Should this check if the line boxes intersects with the dirty rect
|
| + // like legacy layout or do we want to change the invalidation logic?
|
| +
|
| + LayoutRect overflow_rect(box_fragment_->VisualOverflowRect());
|
| + overflow_rect.MoveBy(paint_offset);
|
| +
|
| + // TODO(eae): Should this go here, in ::Paint or in the NGTextFragmentPainter
|
| + // class?
|
| + DrawingRecorder recorder(
|
| + paint_info.context, *fragment,
|
| + DisplayItem::PaintPhaseToDrawingType(paint_info.phase),
|
| + PixelSnappedIntRect(overflow_rect));
|
| +
|
| + for (const auto& child : fragment->Children()) {
|
| + if (child->Type() == NGPhysicalBoxFragment::kFragmentLineBox) {
|
| + CHECK(false);
|
| + } else if (child->Type() == NGPhysicalBoxFragment::kFragmentText) {
|
| + const NGPhysicalTextFragment* text_fragment =
|
| + ToNGPhysicalTextFragment(child.Get());
|
| + NGTextFragmentPainter(text_fragment)
|
| + .Paint(GetDocument(), paint_info, paint_offset);
|
| + }
|
| + }
|
| +}
|
| +
|
| +bool IsPaintingBackgroundOfPaintContainerIntoScrollingContentsLayer(
|
| + const NGPhysicalFragment* fragment,
|
| + const PaintInfo& paint_info) {
|
| + // TODO(eae): Implement. Will require changing the type of
|
| + // PaintInfo::paint_container_ or using the NGBoxFragment container object.
|
| + return false;
|
| +}
|
| +
|
| +void NGBoxFragmentPainter::PaintBoxDecorationBackground(
|
| + const PaintInfo& paint_info,
|
| + const LayoutPoint& paint_offset) {
|
| + LayoutRect paint_rect;
|
| + if (IsPaintingBackgroundOfPaintContainerIntoScrollingContentsLayer(
|
| + box_fragment_, 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.
|
| +
|
| + // 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.
|
| + } else {
|
| + // TODO(eae): We need better converters for ng geometry types. Long term we
|
| + // probably want to change the paint code to take NGPhysical* but that is a
|
| + // much bigger change.
|
| + NGPhysicalSize size = box_fragment_->Size();
|
| + paint_rect = LayoutRect(LayoutPoint(), LayoutSize(size.width, size.height));
|
| + }
|
| +
|
| + paint_rect.MoveBy(paint_offset);
|
| + PaintBoxDecorationBackgroundWithRect(paint_info, paint_offset, paint_rect);
|
| +}
|
| +
|
| +void NGBoxFragmentPainter::PaintBoxDecorationBackgroundWithRect(
|
| + const PaintInfo& paint_info,
|
| + const LayoutPoint& paint_offset,
|
| + const LayoutRect& paint_rect) {
|
| + bool painting_overflow_contents =
|
| + IsPaintingBackgroundOfPaintContainerIntoScrollingContentsLayer(
|
| + box_fragment_, paint_info);
|
| + const ComputedStyle& style = box_fragment_->Style();
|
| +
|
| + // TODO(eae): Implement support for painting overflow contents.
|
| + const DisplayItemClient& display_item_client = *box_fragment_;
|
| + if (DrawingRecorder::UseCachedDrawingIfPossible(
|
| + paint_info.context, display_item_client,
|
| + DisplayItem::kBoxDecorationBackground))
|
| + return;
|
| +
|
| + DrawingRecorder recorder(
|
| + paint_info.context, display_item_client,
|
| + DisplayItem::kBoxDecorationBackground,
|
| + FloatRect(BoundsForDrawingRecorder(paint_info, paint_offset)));
|
| + BoxDecorationData box_decoration_data(box_fragment_);
|
| + GraphicsContextStateSaver state_saver(paint_info.context, false);
|
| +
|
| + 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.
|
| + 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();
|
| + }
|
| + }
|
| +
|
| + // TODO(layout-dev): Support theme painting.
|
| +
|
| + // TODO(eae): Support SkipRootBackground painting.
|
| + bool should_paint_background = true;
|
| + if (should_paint_background) {
|
| + PaintBackground(paint_info, paint_rect,
|
| + box_decoration_data.background_color,
|
| + box_decoration_data.bleed_avoidance);
|
| + }
|
| +
|
| + if (!painting_overflow_contents) {
|
| + PaintInsetBoxShadow(paint_info, paint_rect, style);
|
| +
|
| + if (box_decoration_data.has_border_decoration) {
|
| + PaintBorder(box_fragment_, paint_info, paint_rect, style,
|
| + box_decoration_data.bleed_avoidance);
|
| + }
|
| + }
|
| +
|
| + if (box_decoration_data.bleed_avoidance == kBackgroundBleedClipLayer)
|
| + paint_info.context.EndLayer();
|
| +}
|
| +
|
| +void NGBoxFragmentPainter::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;
|
| + PaintFillLayers(paint_info, background_color,
|
| + box_fragment_->Style().BackgroundLayers(), paint_rect,
|
| + bleed_avoidance);
|
| +}
|
| +
|
| +void NGBoxFragmentPainter::PaintFillLayers(
|
| + const PaintInfo& paint_info,
|
| + const Color& c,
|
| + const FillLayer& fill_layer,
|
| + const LayoutRect& rect,
|
| + BackgroundBleedAvoidance avoidance,
|
| + SkBlendMode op,
|
| + const NGPhysicalFragment* bg_fragment) {
|
| + FillLayerOcclusionOutputList reversed_paint_list;
|
| + bool should_draw_background_in_separate_buffer =
|
| + CalculateFillLayerOcclusionCulling(reversed_paint_list, fill_layer,
|
| + GetDocument(), box_fragment_->Style());
|
| +
|
| + // TODO(trchen): We can optimize out isolation group if we have a
|
| + // non-transparent background color and the bottom layer encloses all other
|
| + // layers.
|
| +
|
| + GraphicsContext& context = paint_info.context;
|
| +
|
| + if (should_draw_background_in_separate_buffer)
|
| + context.BeginLayer();
|
| +
|
| + for (auto it = reversed_paint_list.rbegin(); it != reversed_paint_list.rend();
|
| + ++it) {
|
| + PaintFillLayer(box_fragment_, paint_info, c, **it, rect, avoidance, 0,
|
| + LayoutSize(), op, bg_fragment);
|
| + }
|
| +
|
| + if (should_draw_background_in_separate_buffer)
|
| + context.EndLayer();
|
| +}
|
| +
|
| +namespace {
|
| +
|
| +inline bool PaintFastBottomLayer(
|
| + const NGPhysicalBoxFragment* fragment,
|
| + const PaintInfo& paint_info,
|
| + const BoxPainterBase::FillLayerInfo& info,
|
| + const FillLayer& layer,
|
| + const LayoutRect& rect,
|
| + BackgroundBleedAvoidance bleed_avoidance,
|
| + bool has_line_box_sibling,
|
| + const LayoutSize& box_size,
|
| + SkBlendMode op,
|
| + const NGPhysicalFragment* background_fragment
|
| + /*Optional<BackgroundImageGeometry>& geometry*/) {
|
| + // Painting a background image from an ancestor onto a cell is a complex case.
|
| + // if (obj.IsTableCell() && bg_fragment &&
|
| + // !bg_fragment->IsTableCell())
|
| + // return false;
|
| + // Complex cases not handled on the fast path.
|
| + // if (!info.is_bottom_layer || !info.is_border_fill ||
|
| + // info.is_clipped_with_local_scrolling)
|
| + // return false;
|
| +
|
| + // Transparent layer, nothing to paint.
|
| + if (!info.should_paint_color && !info.should_paint_image)
|
| + return true;
|
| +
|
| + // When the layer has an image, figure out whether it is covered by a single
|
| + // tile.
|
| + FloatRect image_tile;
|
| + if (info.should_paint_image) {
|
| + // TODO(layout-dev): Add support for background images.
|
| + return false;
|
| + }
|
| +
|
| + // At this point we're committed to the fast path: the destination (r)rect
|
| + // fits within a single tile, and we can paint it using direct draw(R)Rect()
|
| + // calls.
|
| + GraphicsContext& context = paint_info.context;
|
| + FloatRoundedRect border =
|
| + info.is_rounded_fill
|
| + ? BoxPainterBase::BackgroundRoundedRectAdjustedForBleedAvoidance(
|
| + fragment->Style(), rect, bleed_avoidance, has_line_box_sibling,
|
| + box_size, info.include_left_edge, info.include_right_edge)
|
| + : FloatRoundedRect(PixelSnappedIntRect(rect));
|
| +
|
| + // Optional<RoundedInnerRectClipper> clipper;
|
| + // if (info.is_rounded_fill && !border.IsRenderable()) {
|
| + // When the rrect is not renderable, we resort to clipping.
|
| + // RoundedInnerRectClipper handles this case via discrete, corner-wise
|
| + // clipping.
|
| + // clipper.emplace(obj, paint_info, rect, border, kApplyToContext);
|
| + // border.SetRadii(FloatRoundedRect::Radii());
|
| + //}
|
| +
|
| + // Paint the color if needed.
|
| + if (info.should_paint_color)
|
| + context.FillRoundedRect(border, info.color);
|
| +
|
| + return true;
|
| +}
|
| +
|
| +} // anonymous namespace
|
| +
|
| +void NGBoxFragmentPainter::PaintFillLayer(
|
| + const NGPhysicalBoxFragment* fragment,
|
| + const PaintInfo& paint_info,
|
| + const Color& color,
|
| + const FillLayer& bg_layer,
|
| + const LayoutRect& rect,
|
| + BackgroundBleedAvoidance avoidance,
|
| + const NGPhysicalFragment* box,
|
| + const LayoutSize& box_size,
|
| + SkBlendMode op,
|
| + const NGPhysicalFragment* bg_fragment) {
|
| + GraphicsContext& context = paint_info.context;
|
| + const ComputedStyle& style = fragment->Style();
|
| + if (rect.IsEmpty())
|
| + return;
|
| +
|
| + const BoxPainterBase::FillLayerInfo info(
|
| + fragment->GetLayoutObject()->GetDocument(), style,
|
| + fragment->HasOverflowClip(), color, bg_layer, avoidance,
|
| + box ? (box->BorderEdges() & NGPhysicalTextFragment::kLeftBorder) : true,
|
| + box ? (box->BorderEdges() & NGPhysicalTextFragment::kRightBorder) : true);
|
| +
|
| + // Optional<BackgroundImageGeometry> geometry;
|
| + // bool has_line_box_sibling = box && (box->NextLineBox() ||
|
| + // box->PrevLineBox());
|
| + bool has_line_box_sibling = false;
|
| +
|
| + // Fast path for drawing simple color backgrounds.
|
| + if (PaintFastBottomLayer(fragment, paint_info, info, bg_layer, rect,
|
| + avoidance, has_line_box_sibling, box_size, op,
|
| + bg_fragment)) {
|
| + return;
|
| + }
|
| +
|
| + NGPixelSnappedPhysicalBoxStrut borders = fragment->BorderWidths();
|
| + NGPhysicalBoxStrut paddings; // TODO(layout-dev): Implement
|
| +
|
| + Optional<RoundedInnerRectClipper> clip_to_border;
|
| + if (info.is_rounded_fill) {
|
| + FloatRoundedRect border =
|
| + info.is_border_fill
|
| + ? BackgroundRoundedRectAdjustedForBleedAvoidance(
|
| + style, rect, avoidance, has_line_box_sibling, box_size,
|
| + info.include_left_edge, info.include_right_edge)
|
| + : GetBackgroundRoundedRect(style, rect, has_line_box_sibling,
|
| + box_size, info.include_left_edge,
|
| + info.include_right_edge);
|
| +
|
| + // Clip to the padding or content boxes as necessary.
|
| + if (bg_layer.Clip() == kContentFillBox) {
|
| + // TODO(layout-dev): Do we need to include the padding here?
|
| + LayoutRectOutsets border_inset(
|
| + LayoutUnit(borders.top), LayoutUnit(borders.right),
|
| + LayoutUnit(borders.bottom), LayoutUnit(borders.left));
|
| + border = style.GetRoundedInnerBorderFor(
|
| + LayoutRect(border.Rect()), border_inset, info.include_left_edge,
|
| + info.include_right_edge);
|
| + } else if (bg_layer.Clip() == kPaddingFillBox) {
|
| + border = style.GetRoundedInnerBorderFor(LayoutRect(border.Rect()),
|
| + info.include_left_edge,
|
| + info.include_right_edge);
|
| + }
|
| +
|
| + clip_to_border.emplace(*fragment, paint_info, rect, border,
|
| + kApplyToContext);
|
| + }
|
| +
|
| + GraphicsContextStateSaver clip_with_scrolling_state_saver(
|
| + context, info.is_clipped_with_local_scrolling);
|
| + LayoutRect scrolled_paint_rect = rect;
|
| + if (info.is_clipped_with_local_scrolling &&
|
| + !IsPaintingBackgroundOfPaintContainerIntoScrollingContentsLayer(
|
| + fragment, paint_info)) {
|
| + // Clip to the overflow area.
|
| + // TODO(chrishtr): this should be pixel-snapped.
|
| + // TODO(layout-dev): We don't know the location...
|
| + // context.Clip(FloatRect(this_box.OverflowClipRect(rect.Location())));
|
| +
|
| + // Adjust the paint rect to reflect a scrolled content box with borders at
|
| + // the ends.
|
| + // TODO(layout_dev): Support scrolling
|
| + // IntSize offset = this_box.ScrolledContentOffset();
|
| + // scrolled_paint_rect.Move(-offset);
|
| + // scrolled_paint_rect.SetWidth(borders.left + this_box.ScrollWidth() +
|
| + // borders.right); scrolled_paint_rect.SetHeight(this_box.BorderTop() +
|
| + // this_box.ScrollHeight() +
|
| + // this_box.BorderBottom());
|
| + }
|
| +
|
| + GraphicsContextStateSaver background_clip_state_saver(context, false);
|
| + IntRect mask_rect;
|
| +
|
| + switch (bg_layer.Clip()) {
|
| + case kPaddingFillBox:
|
| + case kContentFillBox: {
|
| + if (info.is_rounded_fill)
|
| + break;
|
| +
|
| + // Clip to the padding or content boxes as necessary.
|
| + bool include_padding = bg_layer.Clip() == kContentFillBox;
|
| + LayoutRect clip_rect(
|
| + scrolled_paint_rect.X() + borders.left,
|
| + scrolled_paint_rect.Y() + borders.top,
|
| + scrolled_paint_rect.Width() - borders.left - borders.right,
|
| + scrolled_paint_rect.Height() - borders.top - borders.bottom);
|
| +
|
| + if (include_padding) {
|
| + clip_rect.ContractEdges(paddings.top, paddings.right, paddings.bottom,
|
| + paddings.left);
|
| + }
|
| +
|
| + background_clip_state_saver.Save();
|
| + // TODO(chrishtr): this should be pixel-snapped.
|
| + context.Clip(FloatRect(clip_rect));
|
| +
|
| + break;
|
| + }
|
| + case kTextFillBox: {
|
| + // First figure out how big the mask has to be. It should be no bigger
|
| + // than what we need to actually render, so we should intersect the dirty
|
| + // rect with the border box of the background.
|
| + mask_rect = PixelSnappedIntRect(rect);
|
| +
|
| + // We draw the background into a separate layer, to be later masked with
|
| + // yet another layer holding the text content.
|
| + background_clip_state_saver.Save();
|
| + context.Clip(mask_rect);
|
| + context.BeginLayer();
|
| +
|
| + break;
|
| + }
|
| + case kBorderFillBox:
|
| + break;
|
| + default:
|
| + NOTREACHED();
|
| + break;
|
| + }
|
| +
|
| + // Paint the color first underneath all images, culled if background image
|
| + // occludes it.
|
| + // TODO(trchen): In the !bgLayer.hasRepeatXY() case, we could improve the
|
| + // culling test by verifying whether the background image covers the entire
|
| + // painting area.
|
| + if (info.is_bottom_layer && info.color.Alpha() && info.should_paint_color) {
|
| + IntRect background_rect(PixelSnappedIntRect(scrolled_paint_rect));
|
| + context.FillRect(background_rect, info.color);
|
| + }
|
| +
|
| + // No progressive loading of the background image.
|
| + if (info.should_paint_image) {
|
| + // TODO(layout-dev): Add support for image painting.
|
| + DCHECK(false);
|
| + }
|
| +
|
| + if (bg_layer.Clip() == kTextFillBox) {
|
| + // Create the text mask layer.
|
| + context.BeginLayer(1, SkBlendMode::kDstIn);
|
| +
|
| + // Now draw the text into the mask. We do this by painting using a special
|
| + // paint phase that signals to
|
| + // InlineTextBoxes that they should just add their contents to the clip.
|
| + PaintInfo info(context, mask_rect, kPaintPhaseTextClip,
|
| + kGlobalPaintNormalPhase, 0);
|
| + // if (box) {
|
| + // const RootInlineBox& root = box->Root();
|
| + // box->Paint(info,
|
| + // LayoutPoint(scrolled_paint_rect.X() - box->X(),
|
| + // scrolled_paint_rect.Y() - box->Y()),
|
| + // root.LineTop(), root.LineBottom());
|
| + //} else {
|
| + // // FIXME: this should only have an effect for the line box list within
|
| + // // |obj|. Change this to create a LineBoxListPainter directly.
|
| + // LayoutSize local_offset =
|
| + // obj.IsBox() ? ToLayoutBox(&obj)->LocationOffset() : LayoutSize();
|
| + // obj.Paint(info, scrolled_paint_rect.Location() - local_offset);
|
| + //}
|
| +
|
| + context.EndLayer();
|
| + context.EndLayer();
|
| + }
|
| +}
|
| +
|
| +LayoutRect NGBoxFragmentPainter::BoundsForDrawingRecorder(
|
| + const PaintInfo& paint_info,
|
| + const LayoutPoint& adjusted_paint_offset) {
|
| + LayoutRect bounds = box_fragment_->VisualOverflowRect();
|
| + bounds.MoveBy(adjusted_paint_offset);
|
| + return bounds;
|
| +}
|
| +
|
| +void NGBoxFragmentPainter::PaintBorder(const NGPhysicalBoxFragment* fragment,
|
| + const PaintInfo& info,
|
| + const LayoutRect& rect,
|
| + const ComputedStyle& style,
|
| + BackgroundBleedAvoidance bleed_avoidance,
|
| + bool include_logical_left_edge,
|
| + bool include_logical_right_edge) {
|
| + // TODO(layout-dev): Add support for image border painting.
|
| + const BoxBorderPainter border_painter(rect, style, bleed_avoidance,
|
| + include_logical_left_edge,
|
| + include_logical_right_edge);
|
| + border_painter.PaintBorder(info, rect);
|
| +}
|
| +
|
| +const Document& NGBoxFragmentPainter::GetDocument() const {
|
| + return box_fragment_->GetLayoutObject()->GetDocument();
|
| +}
|
| +
|
| +} // namespace blink
|
|
|