Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(607)

Unified Diff: third_party/WebKit/Source/core/paint/NGBoxFragmentPainter.cpp

Issue 2842983002: [LayoutNG] Paint inlines from the fragment tree
Patch Set: Rebase w/HEAD Created 3 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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

Powered by Google App Engine
This is Rietveld 408576698