blob: 0672d751fc4bdcbcb82f61bbda6614446a670040 [file] [log] [blame]
// 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;
}
NGLineLayoutOpportunity NGLayoutOpportunity::ComputeLineLayoutOpportunity(
const NGConstraintSpace& space,
LayoutUnit line_block_size,
LayoutUnit block_delta) const {
return NGLineLayoutOpportunity(
ComputeLineLeftOffset(space, line_block_size, block_delta),
ComputeLineRightOffset(space, line_block_size, block_delta),
rect.LineStartOffset(), rect.LineEndOffset(),
rect.BlockStartOffset() + block_delta, line_block_size);
}
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