Commit 6d0e3050 authored by Koji Ishii's avatar Koji Ishii Committed by Commit Bot

[LayoutNG] Avoid CreateShapeResult when `text-overflow: ellipsis`

This patch changes `text-overflow: ellipsis` to create the
truncated ShapeResultView from ShapeResultView without
creating intermediate ShapeResult, using the mechanism added
in r633612 (CL:1477141). This saves memory by not creating
actual sub RunInfo.

This change also fixes an issue in offset computation in
ShapeResultView::ForEachGraphemeClusters. This is discovered
by this change because before this change, RunInfoPart always
points to the full range of RunInfo.

Some utility functions to compute character index are added.
I hope they can be better structured to abstract differences
between ShapeResult and ShapeResultView, but doing so is
deferred to future patches as I understand it better.

Bug: 636993
Change-Id: Ib2e501829051ca44f7aaccac4856ffce5618ba23
Reviewed-on: https://chromium-review.googlesource.com/c/1478319
Commit-Queue: Emil A Eklund <eae@chromium.org>
Reviewed-by: default avatarEmil A Eklund <eae@chromium.org>
Cr-Commit-Position: refs/heads/master@{#635758}
parent b8614c05
...@@ -216,6 +216,7 @@ bool NGLineTruncator::TruncateChild(LayoutUnit space_for_child, ...@@ -216,6 +216,7 @@ bool NGLineTruncator::TruncateChild(LayoutUnit space_for_child,
IsLtr(line_direction_) ? space_for_child IsLtr(line_direction_) ? space_for_child
: shape_result->Width() - space_for_child, : shape_result->Width() - space_for_child,
line_direction_); line_direction_);
DCHECK_LE(new_length, fragment.Length());
if (!new_length || new_length == fragment.Length()) { if (!new_length || new_length == fragment.Length()) {
if (!is_first_child) if (!is_first_child)
return false; return false;
......
...@@ -288,14 +288,10 @@ scoped_refptr<const NGPhysicalFragment> NGPhysicalTextFragment::TrimText( ...@@ -288,14 +288,10 @@ scoped_refptr<const NGPhysicalFragment> NGPhysicalTextFragment::TrimText(
DCHECK_GE(new_start_offset, StartOffset()); DCHECK_GE(new_start_offset, StartOffset());
DCHECK_GT(new_end_offset, new_start_offset); DCHECK_GT(new_end_offset, new_start_offset);
DCHECK_LE(new_end_offset, EndOffset()); DCHECK_LE(new_end_offset, EndOffset());
// TODO(layout-dev): Add sub-range version of CreateShapeResult to avoid scoped_refptr<ShapeResultView> new_shape_result = ShapeResultView::Create(
// this double copy. shape_result_.get(), new_start_offset, new_end_offset);
scoped_refptr<ShapeResult> new_shape_result =
shape_result_->CreateShapeResult()->SubRange(new_start_offset,
new_end_offset);
return base::AdoptRef(new NGPhysicalTextFragment( return base::AdoptRef(new NGPhysicalTextFragment(
*this, new_start_offset, new_end_offset, *this, new_start_offset, new_end_offset, std::move(new_shape_result)));
ShapeResultView::Create(new_shape_result.get())));
} }
unsigned NGPhysicalTextFragment::TextOffsetForPoint( unsigned NGPhysicalTextFragment::TextOffsetForPoint(
......
...@@ -42,6 +42,13 @@ struct ShapeResultView::RunInfoPart { ...@@ -42,6 +42,13 @@ struct ShapeResultView::RunInfoPart {
const HarfBuzzRunGlyphData& GlyphAt(unsigned index) const { const HarfBuzzRunGlyphData& GlyphAt(unsigned index) const {
return *(range_.begin + index); return *(range_.begin + index);
} }
// The end character index of |this| without considering offsets in
// |ShapeResultView|. This is analogous to:
// GlyphAt(Rtl() ? -1 : NumGlyphs()).character_index
// if such |HarfBuzzRunGlyphData| is available.
unsigned CharacterIndexOfEndGlyph() const {
return num_characters_ + offset_;
}
bool Rtl() const { return run_->Rtl(); } bool Rtl() const { return run_->Rtl(); }
bool IsHorizontal() const { return run_->IsHorizontal(); } bool IsHorizontal() const { return run_->IsHorizontal(); }
...@@ -50,9 +57,6 @@ struct ShapeResultView::RunInfoPart { ...@@ -50,9 +57,6 @@ struct ShapeResultView::RunInfoPart {
float Width() const { return width_; } float Width() const { return width_; }
unsigned PreviousSafeToBreakOffset(unsigned offset) const; unsigned PreviousSafeToBreakOffset(unsigned offset) const;
size_t GlyphToCharacterIndex(size_t i) const {
return run_->GlyphToCharacterIndex(i);
}
// Common signatures with RunInfo, to templatize algorithms. // Common signatures with RunInfo, to templatize algorithms.
const ShapeResult::RunInfo* GetRunInfo() const { return run_.get(); } const ShapeResult::RunInfo* GetRunInfo() const { return run_.get(); }
...@@ -100,6 +104,13 @@ unsigned ShapeResultView::RunInfoPart::PreviousSafeToBreakOffset( ...@@ -100,6 +104,13 @@ unsigned ShapeResultView::RunInfoPart::PreviousSafeToBreakOffset(
return 0; return 0;
} }
// The offset to add to |HarfBuzzRunGlyphData.character_index| to compute the
// character index of the source string.
unsigned ShapeResultView::CharacterIndexOffsetForGlyphData(
const RunInfoPart& part) const {
return part.start_index_ + char_index_offset_ - part.offset_;
}
template <class ShapeResultType> template <class ShapeResultType>
ShapeResultView::ShapeResultView(const ShapeResultType* other) ShapeResultView::ShapeResultView(const ShapeResultType* other)
: primary_font_(other->primary_font_), : primary_font_(other->primary_font_),
...@@ -378,10 +389,11 @@ float ShapeResultView::ForEachGlyph(float initial_advance, ...@@ -378,10 +389,11 @@ float ShapeResultView::ForEachGlyph(float initial_advance,
const auto& run = part->run_; const auto& run = part->run_;
bool is_horizontal = HB_DIRECTION_IS_HORIZONTAL(run->direction_); bool is_horizontal = HB_DIRECTION_IS_HORIZONTAL(run->direction_);
const SimpleFontData* font_data = run->font_data_.get(); const SimpleFontData* font_data = run->font_data_.get();
const unsigned character_index_offset_for_glyph_data =
CharacterIndexOffsetForGlyphData(*part);
for (const auto& glyph_data : *part) { for (const auto& glyph_data : *part) {
unsigned character_index = glyph_data.character_index + unsigned character_index =
part->start_index_ + char_index_offset_ - glyph_data.character_index + character_index_offset_for_glyph_data;
part->offset_;
glyph_callback(context, character_index, glyph_data.glyph, glyph_callback(context, character_index, glyph_data.glyph,
glyph_data.offset, total_advance, is_horizontal, glyph_data.offset, total_advance, is_horizontal,
run->canvas_rotation_, font_data); run->canvas_rotation_, font_data);
...@@ -404,12 +416,13 @@ float ShapeResultView::ForEachGlyph(float initial_advance, ...@@ -404,12 +416,13 @@ float ShapeResultView::ForEachGlyph(float initial_advance,
const auto& run = part->run_; const auto& run = part->run_;
bool is_horizontal = HB_DIRECTION_IS_HORIZONTAL(run->direction_); bool is_horizontal = HB_DIRECTION_IS_HORIZONTAL(run->direction_);
const SimpleFontData* font_data = run->font_data_.get(); const SimpleFontData* font_data = run->font_data_.get();
const unsigned character_index_offset_for_glyph_data =
CharacterIndexOffsetForGlyphData(*part);
if (!run->Rtl()) { // Left-to-right if (!run->Rtl()) { // Left-to-right
for (const auto& glyph_data : *part) { for (const auto& glyph_data : *part) {
unsigned character_index = glyph_data.character_index + unsigned character_index =
part->start_index_ + char_index_offset_ - glyph_data.character_index + character_index_offset_for_glyph_data;
part->offset_;
if (character_index >= to) if (character_index >= to)
break; break;
if (character_index >= from) { if (character_index >= from) {
...@@ -422,9 +435,8 @@ float ShapeResultView::ForEachGlyph(float initial_advance, ...@@ -422,9 +435,8 @@ float ShapeResultView::ForEachGlyph(float initial_advance,
} else { // Right-to-left } else { // Right-to-left
for (const auto& glyph_data : *part) { for (const auto& glyph_data : *part) {
unsigned character_index = glyph_data.character_index + unsigned character_index =
part->start_index_ + char_index_offset_ - glyph_data.character_index + character_index_offset_for_glyph_data;
part->offset_;
if (character_index < from) if (character_index < from)
break; break;
if (character_index < to) { if (character_index < to) {
...@@ -460,20 +472,24 @@ float ShapeResultView::ForEachGraphemeClusters(const StringView& text, ...@@ -460,20 +472,24 @@ float ShapeResultView::ForEachGraphemeClusters(const StringView& text,
// broken down further from a text shaping point of view. A cluster can // broken down further from a text shaping point of view. A cluster can
// contain multiple glyphs and grapheme clusters, with mutually overlapping // contain multiple glyphs and grapheme clusters, with mutually overlapping
// boundaries. // boundaries.
uint16_t cluster_start = static_cast<uint16_t>( const unsigned character_index_offset_for_glyph_data =
rtl ? run->start_index_ + run->num_characters_ + run_offset CharacterIndexOffsetForGlyphData(*part) + run_offset;
: run->GlyphToCharacterIndex(0) + run_offset); uint16_t cluster_start =
static_cast<uint16_t>(rtl ? part->CharacterIndexOfEndGlyph() +
character_index_offset_for_glyph_data
: part->GlyphAt(0).character_index +
character_index_offset_for_glyph_data);
const unsigned num_glyphs = part->NumGlyphs(); const unsigned num_glyphs = part->NumGlyphs();
for (unsigned i = 0; i < num_glyphs; ++i) { for (unsigned i = 0; i < num_glyphs; ++i) {
const HarfBuzzRunGlyphData& glyph_data = part->GlyphAt(i); const HarfBuzzRunGlyphData& glyph_data = part->GlyphAt(i);
uint16_t current_character_index = glyph_data.character_index + uint16_t current_character_index =
part->start_index_ + glyph_data.character_index + character_index_offset_for_glyph_data;
char_index_offset_ - part->offset_;
bool is_run_end = (i + 1 == num_glyphs); bool is_run_end = (i + 1 == num_glyphs);
bool is_cluster_end = bool is_cluster_end =
is_run_end || (run->GlyphToCharacterIndex(i + 1) + run_offset != is_run_end || (part->GlyphAt(i + 1).character_index +
character_index_offset_for_glyph_data !=
current_character_index); current_character_index);
if ((rtl && current_character_index >= to) || if ((rtl && current_character_index >= to) ||
...@@ -496,8 +512,10 @@ float ShapeResultView::ForEachGraphemeClusters(const StringView& text, ...@@ -496,8 +512,10 @@ float ShapeResultView::ForEachGraphemeClusters(const StringView& text,
cluster_end = current_character_index; cluster_end = current_character_index;
} else { } else {
cluster_end = static_cast<uint16_t>( cluster_end = static_cast<uint16_t>(
is_run_end ? run->start_index_ + run->num_characters_ + run_offset is_run_end ? part->CharacterIndexOfEndGlyph() +
: run->GlyphToCharacterIndex(i + 1) + run_offset); character_index_offset_for_glyph_data
: part->GlyphAt(i + 1).character_index +
character_index_offset_for_glyph_data);
} }
graphemes_in_cluster = ShapeResult::CountGraphemesInCluster( graphemes_in_cluster = ShapeResult::CountGraphemesInCluster(
text.Characters16(), text.length(), cluster_start, cluster_end); text.Characters16(), text.length(), cluster_start, cluster_end);
......
...@@ -155,6 +155,8 @@ class PLATFORM_EXPORT ShapeResultView final ...@@ -155,6 +155,8 @@ class PLATFORM_EXPORT ShapeResultView final
template <bool is_horizontal_run> template <bool is_horizontal_run>
void ComputeBoundsForPart(const RunInfoPart&, float origin); void ComputeBoundsForPart(const RunInfoPart&, float origin);
unsigned CharacterIndexOffsetForGlyphData(const RunInfoPart&) const;
// Common signatures with ShapeResult, to templatize algorithms. // Common signatures with ShapeResult, to templatize algorithms.
const Vector<std::unique_ptr<RunInfoPart>, 4>& RunsOrParts() const { const Vector<std::unique_ptr<RunInfoPart>, 4>& RunsOrParts() const {
return parts_; return parts_;
......
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