summaryrefslogtreecommitdiff
path: root/chromium/third_party/blink/renderer/core/layout/ng/exclusions/ng_layout_opportunity.cc
blob: 250278fdfba71d930b0d86be8792d4309edca0b8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
// Copyright 2018 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/layout/ng/exclusions/ng_layout_opportunity.h"

#include "third_party/blink/renderer/core/layout/layout_box.h"
#include "third_party/blink/renderer/core/layout/ng/ng_constraint_space.h"
#include "third_party/blink/renderer/core/layout/shapes/shape_outside_info.h"

namespace blink {

namespace {

// Returns how far a line can "fit" into a given exclusion based on its shape
// area. If the exclusion does not obstruct the line, then the returned
// LineSegment will be "invalid".
LineSegment ExcludedSegment(const NGExclusion& exclusion,
                            LayoutUnit bfc_block_offset,
                            LayoutUnit line_block_size) {
  DCHECK(exclusion.shape_data);
  const NGExclusionShapeData& shape_data = *exclusion.shape_data;
  const Shape& shape =
      shape_data.layout_box->GetShapeOutsideInfo()->ComputedShape();

  // Determine the block offset (relative to the shape) at which we need to
  // test for.
  LayoutUnit shape_relative_block_offset =
      bfc_block_offset -
      (exclusion.rect.BlockStartOffset() + shape_data.margins.block_start +
       shape_data.shape_insets.block_start);

  // At the block-start/end of shapes it is possible for a line to just touch,
  // and GetExcludedInterval will return a valid segment.
  // This check skips the shape when this happens.
  if (!shape.LineOverlapsShapeMarginBounds(shape_relative_block_offset,
                                           line_block_size))
    return LineSegment();

  // Clamp the line size to the size of the shape.
  LayoutUnit clamped_line_block_size =
      std::min(line_block_size, exclusion.rect.BlockSize() -
                                    shape_data.shape_insets.BlockSum() -
                                    shape_data.margins.BlockSum());

  LineSegment segment = shape.GetExcludedInterval(shape_relative_block_offset,
                                                  clamped_line_block_size);

  // Adjust the segment offsets to be relative to the line-left margin edge.
  LayoutUnit margin_delta =
      shape_data.margins.LineLeft(TextDirection::kLtr) +
      shape_data.shape_insets.LineLeft(TextDirection::kLtr);
  segment.logical_left += margin_delta;
  segment.logical_right += margin_delta;

  // Clamp the segment offsets to the size of the exclusion.
  segment.logical_left = clampTo<LayoutUnit>(segment.logical_left, LayoutUnit(),
                                             exclusion.rect.InlineSize());
  segment.logical_right = clampTo<LayoutUnit>(
      segment.logical_right, LayoutUnit(), exclusion.rect.InlineSize());

  // Make the segment offsets relative to the BFC coordinate space.
  segment.logical_left += exclusion.rect.LineStartOffset();
  segment.logical_right += exclusion.rect.LineStartOffset();

  return segment;
}

// Returns if the given line block-size and offset intersects with the given
// exclusion.
bool IntersectsExclusion(const NGExclusion& exclusion,
                         LayoutUnit bfc_block_offset,
                         LayoutUnit line_block_size) {
  return bfc_block_offset < exclusion.rect.BlockEndOffset() &&
         bfc_block_offset + line_block_size > exclusion.rect.BlockStartOffset();
}

}  // namespace

bool NGLayoutOpportunity::IsBlockDeltaBelowShapes(
    LayoutUnit block_delta) const {
  DCHECK(shape_exclusions);

  for (const auto& exclusion : shape_exclusions->line_left_shapes) {
    if (rect.BlockStartOffset() + block_delta <
        exclusion->rect.BlockEndOffset())
      return false;
  }

  for (const auto& exclusion : shape_exclusions->line_right_shapes) {
    if (rect.BlockStartOffset() + block_delta <
        exclusion->rect.BlockEndOffset())
      return false;
  }

  return true;
}

LayoutUnit NGLayoutOpportunity::ComputeLineLeftOffset(
    const NGConstraintSpace& space,
    LayoutUnit line_block_size,
    LayoutUnit block_delta) const {
  if (!shape_exclusions || shape_exclusions->line_left_shapes.IsEmpty())
    return rect.LineStartOffset();

  LayoutUnit bfc_block_offset = rect.BlockStartOffset() + block_delta;

  // Step through each exclusion and re-build the line_left_offset. Without
  // shapes this would be the same as the opportunity offset.
  //
  // We rebuild this offset from the line-left end, checking each exclusion and
  // increasing the line_left when an exclusion intersects.
  LayoutUnit line_left = space.BfcOffset().line_offset;
  for (auto& exclusion : shape_exclusions->line_left_shapes) {
    if (!IntersectsExclusion(*exclusion, bfc_block_offset, line_block_size))
      continue;

    if (exclusion->shape_data) {
      LineSegment segment =
          ExcludedSegment(*exclusion, bfc_block_offset, line_block_size);
      if (segment.is_valid)
        line_left = std::max(line_left, segment.logical_right);
    } else {
      line_left = std::max(line_left, exclusion->rect.LineEndOffset());
    }
  }

  return std::min(line_left, rect.LineEndOffset());
}

LayoutUnit NGLayoutOpportunity::ComputeLineRightOffset(
    const NGConstraintSpace& space,
    LayoutUnit line_block_size,
    LayoutUnit block_delta) const {
  if (!shape_exclusions || shape_exclusions->line_right_shapes.IsEmpty())
    return rect.LineEndOffset();

  LayoutUnit bfc_block_offset = rect.BlockStartOffset() + block_delta;

  LayoutUnit line_right =
      space.BfcOffset().line_offset + space.AvailableSize().inline_size;

  // Step through each exclusion and re-build the line_right_offset. Without
  // shapes this would be the same as the opportunity offset.
  //
  // We rebuild this offset from the line-right end, checking each exclusion and
  // reducing the line_right when an exclusion intersects.
  for (auto& exclusion : shape_exclusions->line_right_shapes) {
    if (!IntersectsExclusion(*exclusion, bfc_block_offset, line_block_size))
      continue;

    if (exclusion->shape_data) {
      LineSegment segment =
          ExcludedSegment(*exclusion, bfc_block_offset, line_block_size);
      if (segment.is_valid)
        line_right = std::min(line_right, segment.logical_left);
    } else {
      line_right = std::min(line_right, exclusion->rect.LineStartOffset());
    }
  }

  return std::max(line_right, rect.LineStartOffset());
}

}  // namespace blink