Commit 087318f1 authored by Aleks Totic's avatar Aleks Totic Committed by Commit Bot

LayoutNG inline outline initial implementation

I think that this patch is a good starting point for outline implementation.
It implements a basic outline painting algorithm:
- Outlines are painted by containing self painting fragment.
- Outlines are computed as:
   traverse fragment's descendants
   combine outlines for fragments with common layout objects
   paint combined outlines, using first fragment as DisplayItem

There are many problems:
- All descendant fragments are traversed for each outline paint, even if
  there are no outlines to be painted.
  Fix: detect outline existence while descendants are being painted,
  and only call this code if there are outlines.

There are 119 tests in our test suite that trigger inline outline paint code.
With this patch, we get 4 regressions:
fast/history/visited-link-hover-outline-color.html
paint/invalidation/overflow/inline-block-overflow-repaint.html
paint/invalidation/overflow/inline-box-overflow-repaint.html
  cause: improper invalidation
fast/inline/nested-text-descendants.html
  cause: inline text position

15 additional tests pass.

Bug: 714962
Cq-Include-Trybots: master.tryserver.blink:linux_trusty_blink_rel;master.tryserver.chromium.linux:linux_layout_tests_layout_ng;master.tryserver.chromium.linux:linux_layout_tests_slimming_paint_v2
Change-Id: Icccd225abac7e38fd9a871ee2914ff8d235474e3
Reviewed-on: https://chromium-review.googlesource.com/887633
Commit-Queue: Aleks Totic <atotic@chromium.org>
Reviewed-by: default avatarKoji Ishii <kojii@chromium.org>
Reviewed-by: default avatarEmil A Eklund <eae@chromium.org>
Cr-Commit-Position: refs/heads/master@{#532270}
parent 7ece806c
...@@ -453,6 +453,7 @@ crbug.com/714962 virtual/layout_ng/fast/inline/inline-borders-with-bidi-override ...@@ -453,6 +453,7 @@ crbug.com/714962 virtual/layout_ng/fast/inline/inline-borders-with-bidi-override
crbug.com/714962 virtual/layout_ng/fast/inline/inline-focus-ring-under-absolute-enclosing-relative-div.html [ Failure ] crbug.com/714962 virtual/layout_ng/fast/inline/inline-focus-ring-under-absolute-enclosing-relative-div.html [ Failure ]
crbug.com/714962 virtual/layout_ng/fast/inline/layout-after-inserting-nested-br.html [ Failure ] crbug.com/714962 virtual/layout_ng/fast/inline/layout-after-inserting-nested-br.html [ Failure ]
crbug.com/714962 virtual/layout_ng/fast/inline/left-right-center-inline-alignment-in-ltr-and-rtl-blocks.html [ Failure ] crbug.com/714962 virtual/layout_ng/fast/inline/left-right-center-inline-alignment-in-ltr-and-rtl-blocks.html [ Failure ]
crbug.com/714962 virtual/layout_ng/fast/inline/nested-text-descendants.html [ Failure ]
crbug.com/714962 virtual/layout_ng/fast/inline/out-of-flow-objects-and-whitespace-after-empty-inline.html [ Failure ] crbug.com/714962 virtual/layout_ng/fast/inline/out-of-flow-objects-and-whitespace-after-empty-inline.html [ Failure ]
crbug.com/714962 virtual/layout_ng/fast/inline/outline-continuations.html [ Failure ] crbug.com/714962 virtual/layout_ng/fast/inline/outline-continuations.html [ Failure ]
crbug.com/714962 virtual/layout_ng/fast/inline/styledEmptyInlinesWithBRs.html [ Failure ] crbug.com/714962 virtual/layout_ng/fast/inline/styledEmptyInlinesWithBRs.html [ Failure ]
......
...@@ -252,6 +252,8 @@ blink_core_sources("paint") { ...@@ -252,6 +252,8 @@ blink_core_sources("paint") {
"ng/ng_fragment_painter.h", "ng/ng_fragment_painter.h",
"ng/ng_paint_fragment.cc", "ng/ng_paint_fragment.cc",
"ng/ng_paint_fragment.h", "ng/ng_paint_fragment.h",
"ng/ng_paint_fragment_traversal.cc",
"ng/ng_paint_fragment_traversal.h",
"ng/ng_text_fragment_painter.cc", "ng/ng_text_fragment_painter.cc",
"ng/ng_text_fragment_painter.h", "ng/ng_text_fragment_painter.h",
"ng/ng_text_painter.cc", "ng/ng_text_painter.cc",
......
...@@ -223,7 +223,9 @@ void NGBoxFragmentPainter::PaintObject(const PaintInfo& paint_info, ...@@ -223,7 +223,9 @@ void NGBoxFragmentPainter::PaintObject(const PaintInfo& paint_info,
if (ShouldPaintSelfOutline(paint_phase)) if (ShouldPaintSelfOutline(paint_phase))
NGFragmentPainter(box_fragment_).PaintOutline(paint_info, paint_offset); NGFragmentPainter(box_fragment_).PaintOutline(paint_info, paint_offset);
if (ShouldPaintDescendantOutlines(paint_phase))
NGFragmentPainter(box_fragment_)
.PaintDescendantOutlines(paint_info, paint_offset);
// TODO(layout-dev): Implement once we have selections in LayoutNG. // TODO(layout-dev): Implement once we have selections in LayoutNG.
// If the caret's node's layout object's containing block is this block, and // If the caret's node's layout object's containing block is this block, and
// the paint action is PaintPhaseForeground, then paint the caret. // the paint action is PaintPhaseForeground, then paint the caret.
......
...@@ -32,7 +32,8 @@ class NGBoxFragmentPainter : public BoxPainterBase { ...@@ -32,7 +32,8 @@ class NGBoxFragmentPainter : public BoxPainterBase {
public: public:
NGBoxFragmentPainter(const NGPaintFragment&); NGBoxFragmentPainter(const NGPaintFragment&);
void Paint(const PaintInfo&, const LayoutPoint&); // paint_offset is relative to the parent fragment
void Paint(const PaintInfo&, const LayoutPoint& paint_offset);
void PaintInlineBox(const PaintInfo&, void PaintInlineBox(const PaintInfo&,
const LayoutPoint&, const LayoutPoint&,
const LayoutPoint& block_paint_offset); const LayoutPoint& block_paint_offset);
......
...@@ -5,9 +5,12 @@ ...@@ -5,9 +5,12 @@
#include "core/paint/ng/ng_fragment_painter.h" #include "core/paint/ng/ng_fragment_painter.h"
#include "core/layout/LayoutTheme.h" #include "core/layout/LayoutTheme.h"
#include "core/layout/ng/inline/ng_inline_fragment_traversal.h"
#include "core/layout/ng/inline/ng_physical_line_box_fragment.h"
#include "core/layout/ng/ng_physical_fragment.h" #include "core/layout/ng/ng_physical_fragment.h"
#include "core/paint/PaintInfo.h" #include "core/paint/PaintInfo.h"
#include "core/paint/ng/ng_paint_fragment.h" #include "core/paint/ng/ng_paint_fragment.h"
#include "core/paint/ng/ng_paint_fragment_traversal.h"
#include "core/style/BorderEdge.h" #include "core/style/BorderEdge.h"
#include "core/style/ComputedStyle.h" #include "core/style/ComputedStyle.h"
#include "platform/geometry/LayoutPoint.h" #include "platform/geometry/LayoutPoint.h"
...@@ -34,7 +37,8 @@ void NGFragmentPainter::PaintOutline(const PaintInfo& paint_info, ...@@ -34,7 +37,8 @@ void NGFragmentPainter::PaintOutline(const PaintInfo& paint_info,
} }
Vector<LayoutRect> outline_rects; Vector<LayoutRect> outline_rects;
paint_fragment_.AddOutlineRects(&outline_rects, paint_offset); //
paint_fragment_.AddSelfOutlineRect(&outline_rects, paint_offset);
if (outline_rects.IsEmpty()) if (outline_rects.IsEmpty())
return; return;
...@@ -45,4 +49,73 @@ void NGFragmentPainter::PaintOutline(const PaintInfo& paint_info, ...@@ -45,4 +49,73 @@ void NGFragmentPainter::PaintOutline(const PaintInfo& paint_info,
PaintOutlineRects(paint_info, outline_rects, style, paint_fragment_); PaintOutlineRects(paint_info, outline_rects, style, paint_fragment_);
} }
void NGFragmentPainter::CollectDescendantOutlines(
const LayoutPoint& paint_offset,
HashMap<const LayoutObject*, NGPaintFragment*>* anchor_fragment_map,
HashMap<const LayoutObject*, Vector<LayoutRect>>* outline_rect_map) {
/*
TODO(atotic): Optimize.
We should not traverse all children for every paint, because
in most cases there are no descendants with outlines.
Possible solution is to only call this function if there are
children with outlines.
*/
// Collect and group outline rects.
for (auto& paint_descendant :
NGPaintFragmentTraversal::DescendantsOf(paint_fragment_)) {
const ComputedStyle& descendant_style = paint_descendant.fragment->Style();
if (!paint_descendant.fragment->PhysicalFragment().IsBox() ||
paint_descendant.fragment->PhysicalFragment().IsInlineBlock())
continue;
if (!descendant_style.HasOutline() ||
descendant_style.Visibility() != EVisibility::kVisible)
continue;
if (descendant_style.OutlineStyleIsAuto() &&
!LayoutTheme::GetTheme().ShouldDrawDefaultFocusRing(
paint_descendant.fragment->GetNode(), descendant_style))
continue;
const LayoutObject* layout_object =
paint_descendant.fragment->GetLayoutObject();
Vector<LayoutRect>* outline_rects;
auto iter = outline_rect_map->find(layout_object);
if (iter == outline_rect_map->end()) {
anchor_fragment_map->insert(layout_object, paint_descendant.fragment);
outline_rects =
&outline_rect_map->insert(layout_object, Vector<LayoutRect>())
.stored_value->value;
} else {
outline_rects = &iter->value;
}
paint_descendant.fragment->AddSelfOutlineRect(
outline_rects,
paint_offset + paint_descendant.container_offset.ToLayoutPoint());
}
}
void NGFragmentPainter::PaintDescendantOutlines(
const PaintInfo& paint_info,
const LayoutPoint& paint_offset) {
DCHECK(ShouldPaintDescendantOutlines(paint_info.phase));
HashMap<const LayoutObject*, NGPaintFragment*> anchor_fragment_map;
HashMap<const LayoutObject*, Vector<LayoutRect>> outline_rect_map;
CollectDescendantOutlines(paint_offset, &anchor_fragment_map,
&outline_rect_map);
// Paint all outlines
for (auto& anchor_iter : anchor_fragment_map) {
NGPaintFragment* fragment = anchor_iter.value;
Vector<LayoutRect>* outline_rects =
&outline_rect_map.find(anchor_iter.key)->value;
if (DrawingRecorder::UseCachedDrawingIfPossible(
paint_info.context, *fragment, paint_info.phase))
continue;
PaintOutlineRects(paint_info, *outline_rects, fragment->Style(), *fragment);
}
}
} // namespace blink } // namespace blink
...@@ -6,10 +6,11 @@ ...@@ -6,10 +6,11 @@
#define ng_fragment_painter_h #define ng_fragment_painter_h
#include "core/paint/ObjectPainterBase.h" #include "core/paint/ObjectPainterBase.h"
#include "platform/wtf/Allocator.h" #include "platform/wtf/HashMap.h"
namespace blink { namespace blink {
class LayoutObject;
class LayoutPoint; class LayoutPoint;
class NGPaintFragment; class NGPaintFragment;
struct PaintInfo; struct PaintInfo;
...@@ -25,7 +26,15 @@ class NGFragmentPainter : public ObjectPainterBase { ...@@ -25,7 +26,15 @@ class NGFragmentPainter : public ObjectPainterBase {
void PaintOutline(const PaintInfo&, const LayoutPoint& paint_offset); void PaintOutline(const PaintInfo&, const LayoutPoint& paint_offset);
void PaintDescendantOutlines(const PaintInfo&,
const LayoutPoint& paint_offset);
private: private:
void CollectDescendantOutlines(
const LayoutPoint& paint_offset,
HashMap<const LayoutObject*, NGPaintFragment*>* anchor_fragment_map,
HashMap<const LayoutObject*, Vector<LayoutRect>>* outline_rect_map);
const NGPaintFragment& paint_fragment_; const NGPaintFragment& paint_fragment_;
}; };
......
...@@ -120,18 +120,18 @@ void NGPaintFragment::UpdateVisualRectFromLayoutObject( ...@@ -120,18 +120,18 @@ void NGPaintFragment::UpdateVisualRectFromLayoutObject(
} }
} }
void NGPaintFragment::AddOutlineRects( void NGPaintFragment::AddSelfOutlineRect(
Vector<LayoutRect>* outline_rects, Vector<LayoutRect>* outline_rects,
const LayoutPoint& additional_offset) const { const LayoutPoint& additional_offset) const {
DCHECK(outline_rects); DCHECK(outline_rects);
//
// TODO(layout-dev): This isn't correct but is close enough until we have LayoutRect outline_rect(additional_offset, Size().ToLayoutSize());
// the right set of rects for outlines. // LayoutRect outline_rect = VisualRect();
for (const auto& child : children_) { // outline_rect.MoveBy(additional_offset);
LayoutRect outline_rect = child->VisualRect(); // outline_rect.Inflate(-Style().OutlineOffset());
outline_rect.MoveBy(additional_offset); // outline_rect.Inflate(-Style().OutlineWidth());
outline_rects->push_back(outline_rect);
} outline_rects->push_back(outline_rect);
} }
void NGPaintFragment::PaintInlineBoxForDescendants( void NGPaintFragment::PaintInlineBoxForDescendants(
......
...@@ -48,10 +48,7 @@ class NGPaintFragment : public DisplayItemClient, public ImageResourceObserver { ...@@ -48,10 +48,7 @@ class NGPaintFragment : public DisplayItemClient, public ImageResourceObserver {
// set beforehand. // set beforehand.
void UpdateVisualRectFromLayoutObject(); void UpdateVisualRectFromLayoutObject();
// Collects rectangles that the outline of this object would be drawing along void AddSelfOutlineRect(Vector<LayoutRect>*, const LayoutPoint& offset) const;
// the outside of, even if the object isn't styled with a outline for now. The
// rects also cover continuations.
void AddOutlineRects(Vector<LayoutRect>*, const LayoutPoint& offset) const;
// TODO(layout-dev): Implement when we have oveflow support. // TODO(layout-dev): Implement when we have oveflow support.
// TODO(eae): Switch to using NG geometry types. // TODO(eae): Switch to using NG geometry types.
......
// 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/ng/ng_paint_fragment_traversal.h"
#include "core/layout/LayoutObject.h"
#include "core/layout/ng/ng_physical_box_fragment.h"
#include "core/paint/ng/ng_paint_fragment.h"
namespace blink {
namespace {
// Preorder traverse |container|, and collect the fragments satisfying
// |filter| into |results|.
// |filter|.IsTraverse(NGPaintFragment) returns true to traverse children.
// |filter|.IsCollectible(NGPaintFragment) returns true to collect fragment.
template <typename Filter>
void CollectPaintFragments(const NGPaintFragment& container,
NGPhysicalOffset offset_to_container_box,
Filter& filter,
Vector<NGPaintFragmentWithContainerOffset>* result) {
for (const auto& child : container.Children()) {
NGPaintFragmentWithContainerOffset fragment_with_offset{
child.get(), child->Offset() + offset_to_container_box};
if (filter.IsCollectible(child.get())) {
result->push_back(fragment_with_offset);
}
if (filter.IsTraverse(child.get())) {
CollectPaintFragments(*child.get(), fragment_with_offset.container_offset,
filter, result);
}
}
}
// Does not collect fragments with SelfPaintingLayer or their descendants.
class NotSelfPaintingFilter {
public:
bool IsCollectible(const NGPaintFragment* fragment) const {
return !fragment->HasSelfPaintingLayer();
}
bool IsTraverse(const NGPaintFragment* fragment) const {
return !fragment->HasSelfPaintingLayer();
}
};
// Collect only fragments that belong to this LayoutObject.
class LayoutObjectFilter {
public:
explicit LayoutObjectFilter(const LayoutObject* layout_object)
: layout_object_(layout_object) {
DCHECK(layout_object);
};
bool IsCollectible(const NGPaintFragment* fragment) const {
return fragment->GetLayoutObject() == layout_object_;
}
bool IsTraverse(const NGPaintFragment*) const { return true; }
private:
const LayoutObject* layout_object_;
};
} // namespace
Vector<NGPaintFragmentWithContainerOffset>
NGPaintFragmentTraversal::DescendantsOf(const NGPaintFragment& container) {
Vector<NGPaintFragmentWithContainerOffset> result;
NotSelfPaintingFilter filter;
CollectPaintFragments(container, NGPhysicalOffset(), filter, &result);
return result;
}
Vector<NGPaintFragmentWithContainerOffset>
NGPaintFragmentTraversal::SelfFragmentsOf(const NGPaintFragment& container,
const LayoutObject* target) {
Vector<NGPaintFragmentWithContainerOffset> result;
LayoutObjectFilter filter(target);
CollectPaintFragments(container, NGPhysicalOffset(), filter, &result);
return result;
}
} // namespace blink
// 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.
#ifndef NGPaintFragmentTraversal_h
#define NGPaintFragmentTraversal_h
#include "core/CoreExport.h"
#include "core/layout/ng/geometry/ng_physical_offset.h"
#include "platform/wtf/Vector.h"
namespace blink {
class LayoutObject;
class NGPaintFragment;
// Used for return value of traversing fragment tree.
struct CORE_EXPORT NGPaintFragmentWithContainerOffset {
DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
NGPaintFragment* fragment;
// Offset relative to container fragment
NGPhysicalOffset container_offset;
};
// Utility class for traversing the paint fragment tree.
class CORE_EXPORT NGPaintFragmentTraversal {
STATIC_ONLY(NGPaintFragmentTraversal);
public:
// Return descendants without paint layer in preorder.
static Vector<NGPaintFragmentWithContainerOffset> DescendantsOf(
const NGPaintFragment&);
static Vector<NGPaintFragmentWithContainerOffset> SelfFragmentsOf(
const NGPaintFragment&,
const LayoutObject* target);
};
} // namespace blink
#endif // NGPaintFragmentTraversal_h
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment