| Index: ui/gfx/render_text_harfbuzz.cc
|
| diff --git a/ui/gfx/render_text_harfbuzz.cc b/ui/gfx/render_text_harfbuzz.cc
|
| index e4469a6fdfa2500f117317a0b22e84bfac6deb4c..7e916ff3ae21142a633c45521e402b3b1b44ebc8 100644
|
| --- a/ui/gfx/render_text_harfbuzz.cc
|
| +++ b/ui/gfx/render_text_harfbuzz.cc
|
| @@ -10,6 +10,8 @@
|
| #include "base/i18n/break_iterator.h"
|
| #include "base/i18n/char_iterator.h"
|
| #include "base/profiler/scoped_tracker.h"
|
| +#include "base/strings/utf_string_conversions.h"
|
| +#include "base/trace_event/trace_event.h"
|
| #include "third_party/harfbuzz-ng/src/hb.h"
|
| #include "third_party/icu/source/common/unicode/ubidi.h"
|
| #include "third_party/skia/include/core/SkColor.h"
|
| @@ -574,11 +576,48 @@ RangeF TextRunHarfBuzz::GetGraphemeBounds(
|
| preceding_run_widths + cluster_end_x);
|
| }
|
|
|
| +TextRunList::TextRunList() : width_(0.0f) {}
|
| +
|
| +TextRunList::~TextRunList() {}
|
| +
|
| +void TextRunList::Reset() {
|
| + runs_.clear();
|
| + width_ = 0.0f;
|
| +}
|
| +
|
| +void TextRunList::InitIndexMap() {
|
| + if (runs_.size() == 1) {
|
| + visual_to_logical_ = logical_to_visual_ = std::vector<int32_t>(1, 0);
|
| + return;
|
| + }
|
| + const size_t num_runs = runs_.size();
|
| + std::vector<UBiDiLevel> levels(num_runs);
|
| + for (size_t i = 0; i < num_runs; ++i)
|
| + levels[i] = runs_[i]->level;
|
| + visual_to_logical_.resize(num_runs);
|
| + ubidi_reorderVisual(&levels[0], num_runs, &visual_to_logical_[0]);
|
| + logical_to_visual_.resize(num_runs);
|
| + ubidi_reorderLogical(&levels[0], num_runs, &logical_to_visual_[0]);
|
| +}
|
| +
|
| +void TextRunList::ComputePrecedingRunWidths() {
|
| + // Precalculate run width information.
|
| + width_ = 0.0f;
|
| + for (size_t i = 0; i < runs_.size(); ++i) {
|
| + TextRunHarfBuzz* run = runs_[visual_to_logical_[i]];
|
| + run->preceding_run_widths = width_;
|
| + width_ += run->width;
|
| + }
|
| +}
|
| +
|
| } // namespace internal
|
|
|
| RenderTextHarfBuzz::RenderTextHarfBuzz()
|
| : RenderText(),
|
| - needs_layout_(false),
|
| + update_layout_run_list_(false),
|
| + update_display_run_list_(false),
|
| + update_grapheme_iterator_(false),
|
| + update_display_text_(false),
|
| glyph_width_for_test_(0u) {
|
| set_truncate_length(kMaxTextLength);
|
| }
|
| @@ -589,6 +628,25 @@ scoped_ptr<RenderText> RenderTextHarfBuzz::CreateInstanceOfSameType() const {
|
| return scoped_ptr<RenderTextHarfBuzz>(new RenderTextHarfBuzz);
|
| }
|
|
|
| +const base::string16& RenderTextHarfBuzz::GetDisplayText() {
|
| + // TODO(oshima): Consider supporting eliding multi-line text.
|
| + // This requires max_line support first.
|
| + if (multiline() ||
|
| + elide_behavior() == NO_ELIDE ||
|
| + elide_behavior() == FADE_TAIL) {
|
| + // Call UpdateDisplayText to clear |display_text_| and |text_elided_|
|
| + // on the RenderText class.
|
| + UpdateDisplayText(0);
|
| + update_display_text_ = false;
|
| + display_run_list_.reset();
|
| + return layout_text();
|
| + }
|
| +
|
| + EnsureLayoutRunList();
|
| + DCHECK(!update_display_text_);
|
| + return text_elided() ? display_text() : layout_text();
|
| +}
|
| +
|
| Size RenderTextHarfBuzz::GetStringSize() {
|
| const SizeF size_f = GetStringSizeF();
|
| return Size(std::ceil(size_f.width()), size_f.height());
|
| @@ -605,22 +663,23 @@ SelectionModel RenderTextHarfBuzz::FindCursorPosition(const Point& point) {
|
| int x = ToTextPoint(point).x();
|
| float offset = 0;
|
| size_t run_index = GetRunContainingXCoord(x, &offset);
|
| - if (run_index >= runs_.size())
|
| - return EdgeSelectionModel((x < 0) ? CURSOR_LEFT : CURSOR_RIGHT);
|
| - const internal::TextRunHarfBuzz& run = *runs_[run_index];
|
|
|
| + internal::TextRunList* run_list = GetRunList();
|
| + if (run_index >= run_list->size())
|
| + return EdgeSelectionModel((x < 0) ? CURSOR_LEFT : CURSOR_RIGHT);
|
| + const internal::TextRunHarfBuzz& run = *run_list->runs()[run_index];
|
| for (size_t i = 0; i < run.glyph_count; ++i) {
|
| const SkScalar end =
|
| i + 1 == run.glyph_count ? run.width : run.positions[i + 1].x();
|
| const SkScalar middle = (end + run.positions[i].x()) / 2;
|
|
|
| if (offset < middle) {
|
| - return SelectionModel(LayoutIndexToTextIndex(
|
| + return SelectionModel(DisplayIndexToTextIndex(
|
| run.glyph_to_char[i] + (run.is_rtl ? 1 : 0)),
|
| (run.is_rtl ? CURSOR_BACKWARD : CURSOR_FORWARD));
|
| }
|
| if (offset < end) {
|
| - return SelectionModel(LayoutIndexToTextIndex(
|
| + return SelectionModel(DisplayIndexToTextIndex(
|
| run.glyph_to_char[i] + (run.is_rtl ? 0 : 1)),
|
| (run.is_rtl ? CURSOR_FORWARD : CURSOR_BACKWARD));
|
| }
|
| @@ -631,14 +690,16 @@ SelectionModel RenderTextHarfBuzz::FindCursorPosition(const Point& point) {
|
| std::vector<RenderText::FontSpan> RenderTextHarfBuzz::GetFontSpansForTesting() {
|
| EnsureLayout();
|
|
|
| + internal::TextRunList* run_list = GetRunList();
|
| std::vector<RenderText::FontSpan> spans;
|
| - for (size_t i = 0; i < runs_.size(); ++i) {
|
| + for (auto* run : run_list->runs()) {
|
| SkString family_name;
|
| - runs_[i]->skia_face->getFamilyName(&family_name);
|
| - Font font(family_name.c_str(), runs_[i]->font_size);
|
| - spans.push_back(RenderText::FontSpan(font,
|
| - Range(LayoutIndexToTextIndex(runs_[i]->range.start()),
|
| - LayoutIndexToTextIndex(runs_[i]->range.end()))));
|
| + run->skia_face->getFamilyName(&family_name);
|
| + Font font(family_name.c_str(), run->font_size);
|
| + spans.push_back(RenderText::FontSpan(
|
| + font,
|
| + Range(DisplayIndexToTextIndex(run->range.start()),
|
| + DisplayIndexToTextIndex(run->range.end()))));
|
| }
|
|
|
| return spans;
|
| @@ -648,23 +709,24 @@ Range RenderTextHarfBuzz::GetGlyphBounds(size_t index) {
|
| EnsureLayout();
|
| const size_t run_index =
|
| GetRunContainingCaret(SelectionModel(index, CURSOR_FORWARD));
|
| + internal::TextRunList* run_list = GetRunList();
|
| // Return edge bounds if the index is invalid or beyond the layout text size.
|
| - if (run_index >= runs_.size())
|
| + if (run_index >= run_list->size())
|
| return Range(GetStringSize().width());
|
| - const size_t layout_index = TextIndexToLayoutIndex(index);
|
| - internal::TextRunHarfBuzz* run = runs_[run_index];
|
| + const size_t layout_index = TextIndexToDisplayIndex(index);
|
| + internal::TextRunHarfBuzz* run = run_list->runs()[run_index];
|
| RangeF bounds =
|
| - run->GetGraphemeBounds(grapheme_iterator_.get(), layout_index);
|
| + run->GetGraphemeBounds(GetGraphemeIterator(), layout_index);
|
| // If cursor is enabled, extend the last glyph up to the rightmost cursor
|
| // position since clients expect them to be contiguous.
|
| - if (cursor_enabled() && run_index == runs_.size() - 1 &&
|
| + if (cursor_enabled() && run_index == run_list->size() - 1 &&
|
| index == (run->is_rtl ? run->range.start() : run->range.end() - 1))
|
| bounds.second = std::ceil(bounds.second);
|
| return RoundRangeF(run->is_rtl ?
|
| RangeF(bounds.second, bounds.first) : bounds);
|
| }
|
|
|
| -int RenderTextHarfBuzz::GetLayoutTextBaseline() {
|
| +int RenderTextHarfBuzz::GetDisplayTextBaseline() {
|
| EnsureLayout();
|
| return lines()[0].baseline;
|
| }
|
| @@ -672,39 +734,42 @@ int RenderTextHarfBuzz::GetLayoutTextBaseline() {
|
| SelectionModel RenderTextHarfBuzz::AdjacentCharSelectionModel(
|
| const SelectionModel& selection,
|
| VisualCursorDirection direction) {
|
| - DCHECK(!needs_layout_);
|
| + DCHECK(!update_display_run_list_);
|
| +
|
| + internal::TextRunList* run_list = GetRunList();
|
| internal::TextRunHarfBuzz* run;
|
| +
|
| size_t run_index = GetRunContainingCaret(selection);
|
| - if (run_index >= runs_.size()) {
|
| + if (run_index >= run_list->size()) {
|
| // The cursor is not in any run: we're at the visual and logical edge.
|
| SelectionModel edge = EdgeSelectionModel(direction);
|
| if (edge.caret_pos() == selection.caret_pos())
|
| return edge;
|
| - int visual_index = (direction == CURSOR_RIGHT) ? 0 : runs_.size() - 1;
|
| - run = runs_[visual_to_logical_[visual_index]];
|
| + int visual_index = (direction == CURSOR_RIGHT) ? 0 : run_list->size() - 1;
|
| + run = run_list->runs()[run_list->visual_to_logical(visual_index)];
|
| } else {
|
| // If the cursor is moving within the current run, just move it by one
|
| // grapheme in the appropriate direction.
|
| - run = runs_[run_index];
|
| + run = run_list->runs()[run_index];
|
| size_t caret = selection.caret_pos();
|
| bool forward_motion = run->is_rtl == (direction == CURSOR_LEFT);
|
| if (forward_motion) {
|
| - if (caret < LayoutIndexToTextIndex(run->range.end())) {
|
| + if (caret < DisplayIndexToTextIndex(run->range.end())) {
|
| caret = IndexOfAdjacentGrapheme(caret, CURSOR_FORWARD);
|
| return SelectionModel(caret, CURSOR_BACKWARD);
|
| }
|
| } else {
|
| - if (caret > LayoutIndexToTextIndex(run->range.start())) {
|
| + if (caret > DisplayIndexToTextIndex(run->range.start())) {
|
| caret = IndexOfAdjacentGrapheme(caret, CURSOR_BACKWARD);
|
| return SelectionModel(caret, CURSOR_FORWARD);
|
| }
|
| }
|
| // The cursor is at the edge of a run; move to the visually adjacent run.
|
| - int visual_index = logical_to_visual_[run_index];
|
| + int visual_index = run_list->logical_to_visual(run_index);
|
| visual_index += (direction == CURSOR_LEFT) ? -1 : 1;
|
| - if (visual_index < 0 || visual_index >= static_cast<int>(runs_.size()))
|
| + if (visual_index < 0 || visual_index >= static_cast<int>(run_list->size()))
|
| return EdgeSelectionModel(direction);
|
| - run = runs_[visual_to_logical_[visual_index]];
|
| + run = run_list->runs()[run_list->visual_to_logical(visual_index)];
|
| }
|
| bool forward_motion = run->is_rtl == (direction == CURSOR_LEFT);
|
| return forward_motion ? FirstSelectionModelInsideRun(run) :
|
| @@ -758,13 +823,15 @@ SelectionModel RenderTextHarfBuzz::AdjacentWordSelectionModel(
|
| }
|
| return SelectionModel(pos, CURSOR_FORWARD);
|
| #else
|
| + internal::TextRunList* run_list = GetRunList();
|
| SelectionModel cur(selection);
|
| for (;;) {
|
| cur = AdjacentCharSelectionModel(cur, direction);
|
| size_t run = GetRunContainingCaret(cur);
|
| - if (run == runs_.size())
|
| + if (run == run_list->size())
|
| break;
|
| - const bool is_forward = runs_[run]->is_rtl == (direction == CURSOR_LEFT);
|
| + const bool is_forward =
|
| + run_list->runs()[run]->is_rtl == (direction == CURSOR_LEFT);
|
| size_t cursor = cur.caret_pos();
|
| if (is_forward ? iter.IsEndOfWord(cursor) : iter.IsStartOfWord(cursor))
|
| break;
|
| @@ -774,29 +841,32 @@ SelectionModel RenderTextHarfBuzz::AdjacentWordSelectionModel(
|
| }
|
|
|
| std::vector<Rect> RenderTextHarfBuzz::GetSubstringBounds(const Range& range) {
|
| - DCHECK(!needs_layout_);
|
| + DCHECK(!update_display_run_list_);
|
| DCHECK(Range(0, text().length()).Contains(range));
|
| - Range layout_range(TextIndexToLayoutIndex(range.start()),
|
| - TextIndexToLayoutIndex(range.end()));
|
| - DCHECK(Range(0, GetLayoutText().length()).Contains(layout_range));
|
| + Range layout_range(TextIndexToDisplayIndex(range.start()),
|
| + TextIndexToDisplayIndex(range.end()));
|
| + DCHECK(Range(0, GetDisplayText().length()).Contains(layout_range));
|
|
|
| std::vector<Rect> rects;
|
| if (layout_range.is_empty())
|
| return rects;
|
| std::vector<Range> bounds;
|
|
|
| + internal::TextRunList* run_list = GetRunList();
|
| +
|
| // Add a Range for each run/selection intersection.
|
| - for (size_t i = 0; i < runs_.size(); ++i) {
|
| - internal::TextRunHarfBuzz* run = runs_[visual_to_logical_[i]];
|
| + for (size_t i = 0; i < run_list->size(); ++i) {
|
| + internal::TextRunHarfBuzz* run =
|
| + run_list->runs()[run_list->visual_to_logical(i)];
|
| Range intersection = run->range.Intersect(layout_range);
|
| if (!intersection.IsValid())
|
| continue;
|
| DCHECK(!intersection.is_reversed());
|
| const Range leftmost_character_x = RoundRangeF(run->GetGraphemeBounds(
|
| - grapheme_iterator_.get(),
|
| + GetGraphemeIterator(),
|
| run->is_rtl ? intersection.end() - 1 : intersection.start()));
|
| const Range rightmost_character_x = RoundRangeF(run->GetGraphemeBounds(
|
| - grapheme_iterator_.get(),
|
| + GetGraphemeIterator(),
|
| run->is_rtl ? intersection.start() : intersection.end() - 1));
|
| Range range_x(leftmost_character_x.start(), rightmost_character_x.end());
|
| DCHECK(!range_x.is_reversed());
|
| @@ -811,26 +881,20 @@ std::vector<Rect> RenderTextHarfBuzz::GetSubstringBounds(const Range& range) {
|
| }
|
| bounds.push_back(range_x);
|
| }
|
| - for (size_t i = 0; i < bounds.size(); ++i) {
|
| - std::vector<Rect> current_rects = TextBoundsToViewBounds(bounds[i]);
|
| + for (Range& bound : bounds) {
|
| + std::vector<Rect> current_rects = TextBoundsToViewBounds(bound);
|
| rects.insert(rects.end(), current_rects.begin(), current_rects.end());
|
| }
|
| return rects;
|
| }
|
|
|
| -size_t RenderTextHarfBuzz::TextIndexToLayoutIndex(size_t index) const {
|
| - DCHECK_LE(index, text().length());
|
| - ptrdiff_t i = obscured() ? UTF16IndexToOffset(text(), 0, index) : index;
|
| - CHECK_GE(i, 0);
|
| - // Clamp layout indices to the length of the text actually used for layout.
|
| - return std::min<size_t>(GetLayoutText().length(), i);
|
| +size_t RenderTextHarfBuzz::TextIndexToDisplayIndex(size_t index) {
|
| + return TextIndexToGivenTextIndex(GetDisplayText(), index);
|
| }
|
|
|
| -size_t RenderTextHarfBuzz::LayoutIndexToTextIndex(size_t index) const {
|
| +size_t RenderTextHarfBuzz::DisplayIndexToTextIndex(size_t index) {
|
| if (!obscured())
|
| return index;
|
| -
|
| - DCHECK_LE(index, GetLayoutText().length());
|
| const size_t text_index = UTF16OffsetToIndex(text(), 0, index);
|
| DCHECK_LE(text_index, text().length());
|
| return text_index;
|
| @@ -841,12 +905,18 @@ bool RenderTextHarfBuzz::IsValidCursorIndex(size_t index) {
|
| return true;
|
| if (!IsValidLogicalIndex(index))
|
| return false;
|
| - EnsureLayout();
|
| - return !grapheme_iterator_ || grapheme_iterator_->IsGraphemeBoundary(index);
|
| + base::i18n::BreakIterator* grapheme_iterator = GetGraphemeIterator();
|
| + return !grapheme_iterator || grapheme_iterator->IsGraphemeBoundary(index);
|
| +}
|
| +
|
| +void RenderTextHarfBuzz::OnLayoutTextAttributeChanged(bool text_changed) {
|
| + update_layout_run_list_ = true;
|
| + OnDisplayTextAttributeChanged();
|
| }
|
|
|
| -void RenderTextHarfBuzz::ResetLayout() {
|
| - needs_layout_ = true;
|
| +void RenderTextHarfBuzz::OnDisplayTextAttributeChanged() {
|
| + update_display_text_ = true;
|
| + update_grapheme_iterator_ = true;
|
| }
|
|
|
| void RenderTextHarfBuzz::EnsureLayout() {
|
| @@ -855,61 +925,37 @@ void RenderTextHarfBuzz::EnsureLayout() {
|
| FROM_HERE_WITH_EXPLICIT_FUNCTION(
|
| "431326 RenderTextHarfBuzz::EnsureLayout"));
|
|
|
| - if (needs_layout_) {
|
| - runs_.clear();
|
| - grapheme_iterator_.reset();
|
| + EnsureLayoutRunList();
|
|
|
| - if (!GetLayoutText().empty()) {
|
| - // TODO(vadimt): Remove ScopedTracker below once crbug.com/431326 is
|
| - // fixed.
|
| - tracked_objects::ScopedTracker tracking_profile1(
|
| - FROM_HERE_WITH_EXPLICIT_FUNCTION(
|
| - "431326 RenderTextHarfBuzz::EnsureLayout1"));
|
| + if (update_display_run_list_) {
|
| + DCHECK(text_elided());
|
| + const base::string16& display_text = GetDisplayText();
|
| + display_run_list_.reset(new internal::TextRunList);
|
|
|
| - grapheme_iterator_.reset(new base::i18n::BreakIterator(GetLayoutText(),
|
| - base::i18n::BreakIterator::BREAK_CHARACTER));
|
| - if (!grapheme_iterator_->Init())
|
| - grapheme_iterator_.reset();
|
| + if (!display_text.empty()) {
|
| + TRACE_EVENT0("ui", "RenderTextHarfBuzz:EnsureLayout1");
|
|
|
| // TODO(vadimt): Remove ScopedTracker below once crbug.com/431326 is
|
| // fixed.
|
| - tracked_objects::ScopedTracker tracking_profile11(
|
| + tracked_objects::ScopedTracker tracking_profile1(
|
| FROM_HERE_WITH_EXPLICIT_FUNCTION(
|
| - "431326 RenderTextHarfBuzz::EnsureLayout11"));
|
| -
|
| - ItemizeText();
|
| + "431326 RenderTextHarfBuzz::EnsureLayout1"));
|
| + ItemizeTextToRuns(display_text, display_run_list_.get());
|
|
|
| // TODO(vadimt): Remove ScopedTracker below once crbug.com/431326 is
|
| // fixed.
|
| - tracked_objects::ScopedTracker tracking_profile12(
|
| + tracked_objects::ScopedTracker tracking_profile2(
|
| FROM_HERE_WITH_EXPLICIT_FUNCTION(
|
| "431326 RenderTextHarfBuzz::EnsureLayout12"));
|
| -
|
| - for (size_t i = 0; i < runs_.size(); ++i)
|
| - ShapeRun(runs_[i]);
|
| -
|
| - // TODO(vadimt): Remove ScopedTracker below once crbug.com/431326 is
|
| - // fixed.
|
| - tracked_objects::ScopedTracker tracking_profile13(
|
| - FROM_HERE_WITH_EXPLICIT_FUNCTION(
|
| - "431326 RenderTextHarfBuzz::EnsureLayout13"));
|
| -
|
| - // Precalculate run width information.
|
| - float preceding_run_widths = 0.0f;
|
| - for (size_t i = 0; i < runs_.size(); ++i) {
|
| - internal::TextRunHarfBuzz* run = runs_[visual_to_logical_[i]];
|
| - run->preceding_run_widths = preceding_run_widths;
|
| - preceding_run_widths += run->width;
|
| - }
|
| + ShapeRunList(display_text, display_run_list_.get());
|
| }
|
| + update_display_run_list_ = false;
|
|
|
| // TODO(vadimt): Remove ScopedTracker below once crbug.com/431326 is
|
| // fixed.
|
| tracked_objects::ScopedTracker tracking_profile14(
|
| FROM_HERE_WITH_EXPLICIT_FUNCTION(
|
| "431326 RenderTextHarfBuzz::EnsureLayout14"));
|
| -
|
| - needs_layout_ = false;
|
| std::vector<internal::Line> empty_lines;
|
| set_lines(&empty_lines);
|
| }
|
| @@ -920,18 +966,21 @@ void RenderTextHarfBuzz::EnsureLayout() {
|
| FROM_HERE_WITH_EXPLICIT_FUNCTION(
|
| "431326 RenderTextHarfBuzz::EnsureLayout2"));
|
|
|
| + internal::TextRunList* run_list = GetRunList();
|
| HarfBuzzLineBreaker line_breaker(
|
| display_rect().width(), font_list().GetBaseline(),
|
| std::max(font_list().GetHeight(), min_line_height()), multiline(),
|
| - GetLayoutText(), multiline() ? &GetLineBreaks() : nullptr, runs_);
|
| + GetDisplayText(), multiline() ? &GetLineBreaks() : nullptr,
|
| + run_list->runs());
|
|
|
| // TODO(vadimt): Remove ScopedTracker below once crbug.com/431326 is fixed.
|
| tracked_objects::ScopedTracker tracking_profile3(
|
| FROM_HERE_WITH_EXPLICIT_FUNCTION(
|
| "431326 RenderTextHarfBuzz::EnsureLayout3"));
|
|
|
| - for (size_t i = 0; i < runs_.size(); ++i)
|
| - line_breaker.AddRun(visual_to_logical_[i]);
|
| + for (size_t i = 0; i < run_list->size(); ++i)
|
| + line_breaker.AddRun(run_list->visual_to_logical(i));
|
| +
|
| std::vector<internal::Line> lines;
|
| line_breaker.Finalize(&lines, &total_size_);
|
| set_lines(&lines);
|
| @@ -939,7 +988,9 @@ void RenderTextHarfBuzz::EnsureLayout() {
|
| }
|
|
|
| void RenderTextHarfBuzz::DrawVisualText(Canvas* canvas) {
|
| - DCHECK(!needs_layout_);
|
| + DCHECK(!update_layout_run_list_);
|
| + DCHECK(!update_display_run_list_);
|
| + DCHECK(!update_display_text_);
|
| if (lines().empty())
|
| return;
|
|
|
| @@ -948,12 +999,13 @@ void RenderTextHarfBuzz::DrawVisualText(Canvas* canvas) {
|
| ApplyTextShadows(&renderer);
|
| ApplyCompositionAndSelectionStyles();
|
|
|
| + internal::TextRunList* run_list = GetRunList();
|
| for (size_t i = 0; i < lines().size(); ++i) {
|
| const internal::Line& line = lines()[i];
|
| const Vector2d origin = GetLineOffset(i) + Vector2d(0, line.baseline);
|
| SkScalar preceding_segment_widths = 0;
|
| for (const internal::LineSegment& segment : line.segments) {
|
| - const internal::TextRunHarfBuzz& run = *runs_[segment.run];
|
| + const internal::TextRunHarfBuzz& run = *run_list->runs()[segment.run];
|
| renderer.SetTypeface(run.skia_face.get());
|
| renderer.SetTextSize(SkIntToScalar(run.font_size));
|
| renderer.SetFontRenderParams(run.render_params,
|
| @@ -1009,52 +1061,56 @@ void RenderTextHarfBuzz::DrawVisualText(Canvas* canvas) {
|
| }
|
|
|
| size_t RenderTextHarfBuzz::GetRunContainingCaret(
|
| - const SelectionModel& caret) const {
|
| - DCHECK(!needs_layout_);
|
| - size_t layout_position = TextIndexToLayoutIndex(caret.caret_pos());
|
| + const SelectionModel& caret) {
|
| + DCHECK(!update_display_run_list_);
|
| + size_t layout_position = TextIndexToDisplayIndex(caret.caret_pos());
|
| LogicalCursorDirection affinity = caret.caret_affinity();
|
| - for (size_t run = 0; run < runs_.size(); ++run) {
|
| - if (RangeContainsCaret(runs_[run]->range, layout_position, affinity))
|
| - return run;
|
| + internal::TextRunList* run_list = GetRunList();
|
| + for (size_t i = 0; i < run_list->size(); ++i) {
|
| + internal::TextRunHarfBuzz* run = run_list->runs()[i];
|
| + if (RangeContainsCaret(run->range, layout_position, affinity))
|
| + return i;
|
| }
|
| - return runs_.size();
|
| + return run_list->size();
|
| }
|
|
|
| size_t RenderTextHarfBuzz::GetRunContainingXCoord(float x,
|
| float* offset) const {
|
| - DCHECK(!needs_layout_);
|
| + DCHECK(!update_display_run_list_);
|
| + const internal::TextRunList* run_list = GetRunList();
|
| if (x < 0)
|
| - return runs_.size();
|
| + return run_list->size();
|
| // Find the text run containing the argument point (assumed already offset).
|
| float current_x = 0;
|
| - for (size_t i = 0; i < runs_.size(); ++i) {
|
| - size_t run = visual_to_logical_[i];
|
| - current_x += runs_[run]->width;
|
| + for (size_t i = 0; i < run_list->size(); ++i) {
|
| + size_t run = run_list->visual_to_logical(i);
|
| + current_x += run_list->runs()[run]->width;
|
| if (x < current_x) {
|
| - *offset = x - (current_x - runs_[run]->width);
|
| + *offset = x - (current_x - run_list->runs()[run]->width);
|
| return run;
|
| }
|
| }
|
| - return runs_.size();
|
| + return run_list->size();
|
| }
|
|
|
| SelectionModel RenderTextHarfBuzz::FirstSelectionModelInsideRun(
|
| const internal::TextRunHarfBuzz* run) {
|
| - size_t position = LayoutIndexToTextIndex(run->range.start());
|
| + size_t position = DisplayIndexToTextIndex(run->range.start());
|
| position = IndexOfAdjacentGrapheme(position, CURSOR_FORWARD);
|
| return SelectionModel(position, CURSOR_BACKWARD);
|
| }
|
|
|
| SelectionModel RenderTextHarfBuzz::LastSelectionModelInsideRun(
|
| const internal::TextRunHarfBuzz* run) {
|
| - size_t position = LayoutIndexToTextIndex(run->range.end());
|
| + size_t position = DisplayIndexToTextIndex(run->range.end());
|
| position = IndexOfAdjacentGrapheme(position, CURSOR_BACKWARD);
|
| return SelectionModel(position, CURSOR_FORWARD);
|
| }
|
|
|
| -void RenderTextHarfBuzz::ItemizeText() {
|
| - const base::string16& text = GetLayoutText();
|
| - const bool is_text_rtl = GetTextDirection() == base::i18n::RIGHT_TO_LEFT;
|
| +void RenderTextHarfBuzz::ItemizeTextToRuns(
|
| + const base::string16& text,
|
| + internal::TextRunList* run_list_out) {
|
| + const bool is_text_rtl = GetTextDirection(text) == base::i18n::RIGHT_TO_LEFT;
|
| DCHECK_NE(0U, text.length());
|
|
|
| // If ICU fails to itemize the text, we create a run that spans the entire
|
| @@ -1064,8 +1120,8 @@ void RenderTextHarfBuzz::ItemizeText() {
|
| if (!bidi_iterator.Open(text, is_text_rtl, false)) {
|
| internal::TextRunHarfBuzz* run = new internal::TextRunHarfBuzz;
|
| run->range = Range(0, text.length());
|
| - runs_.push_back(run);
|
| - visual_to_logical_ = logical_to_visual_ = std::vector<int32_t>(1, 0);
|
| + run_list_out->add(run);
|
| + run_list_out->InitIndexMap();
|
| return;
|
| }
|
|
|
| @@ -1086,7 +1142,6 @@ void RenderTextHarfBuzz::ItemizeText() {
|
| run->strike = style.style(STRIKE);
|
| run->diagonal_strike = style.style(DIAGONAL_STRIKE);
|
| run->underline = style.style(UNDERLINE);
|
| -
|
| int32 script_item_break = 0;
|
| bidi_iterator.GetLogicalRun(run_break, &script_item_break, &run->level);
|
| // Odd BiDi embedding levels correspond to RTL runs.
|
| @@ -1096,8 +1151,9 @@ void RenderTextHarfBuzz::ItemizeText() {
|
| script_item_break - run_break, &run->script) + run_break;
|
|
|
| // Find the next break and advance the iterators as needed.
|
| - run_break = std::min(static_cast<size_t>(script_item_break),
|
| - TextIndexToLayoutIndex(style.GetRange().end()));
|
| + run_break = std::min(
|
| + static_cast<size_t>(script_item_break),
|
| + TextIndexToGivenTextIndex(text, style.GetRange().end()));
|
|
|
| // Break runs at certain characters that need to be rendered separately to
|
| // prevent either an unusual character from forcing a fallback font on the
|
| @@ -1107,33 +1163,27 @@ void RenderTextHarfBuzz::ItemizeText() {
|
| run_break = FindRunBreakingCharacter(text, run->range.start(), run_break);
|
|
|
| DCHECK(IsValidCodePointIndex(text, run_break));
|
| - style.UpdatePosition(LayoutIndexToTextIndex(run_break));
|
| + style.UpdatePosition(DisplayIndexToTextIndex(run_break));
|
| run->range.set_end(run_break);
|
|
|
| - runs_.push_back(run);
|
| + run_list_out->add(run);
|
| }
|
|
|
| // Undo the temporarily applied composition underlines and selection colors.
|
| UndoCompositionAndSelectionStyles();
|
|
|
| - const size_t num_runs = runs_.size();
|
| - std::vector<UBiDiLevel> levels(num_runs);
|
| - for (size_t i = 0; i < num_runs; ++i)
|
| - levels[i] = runs_[i]->level;
|
| - visual_to_logical_.resize(num_runs);
|
| - ubidi_reorderVisual(&levels[0], num_runs, &visual_to_logical_[0]);
|
| - logical_to_visual_.resize(num_runs);
|
| - ubidi_reorderLogical(&levels[0], num_runs, &logical_to_visual_[0]);
|
| + run_list_out->InitIndexMap();
|
| }
|
|
|
| bool RenderTextHarfBuzz::CompareFamily(
|
| - internal::TextRunHarfBuzz* run,
|
| + const base::string16& text,
|
| const std::string& family,
|
| const gfx::FontRenderParams& render_params,
|
| + internal::TextRunHarfBuzz* run,
|
| std::string* best_family,
|
| gfx::FontRenderParams* best_render_params,
|
| size_t* best_missing_glyphs) {
|
| - if (!ShapeRunWithFont(run, family, render_params))
|
| + if (!ShapeRunWithFont(text, family, render_params, run))
|
| return false;
|
|
|
| const size_t missing_glyphs = run->CountMissingGlyphs();
|
| @@ -1145,7 +1195,15 @@ bool RenderTextHarfBuzz::CompareFamily(
|
| return missing_glyphs == 0;
|
| }
|
|
|
| -void RenderTextHarfBuzz::ShapeRun(internal::TextRunHarfBuzz* run) {
|
| +void RenderTextHarfBuzz::ShapeRunList(const base::string16& text,
|
| + internal::TextRunList* run_list) {
|
| + for (auto* run : run_list->runs())
|
| + ShapeRun(text, run);
|
| + run_list->ComputePrecedingRunWidths();
|
| +}
|
| +
|
| +void RenderTextHarfBuzz::ShapeRun(const base::string16& text,
|
| + internal::TextRunHarfBuzz* run) {
|
| const Font& primary_font = font_list().GetPrimaryFont();
|
| const std::string primary_family = primary_font.GetFontName();
|
| run->font_size = primary_font.GetFontSize();
|
| @@ -1155,20 +1213,21 @@ void RenderTextHarfBuzz::ShapeRun(internal::TextRunHarfBuzz* run) {
|
| size_t best_missing_glyphs = std::numeric_limits<size_t>::max();
|
|
|
| for (const Font& font : font_list().GetFonts()) {
|
| - if (CompareFamily(run, font.GetFontName(), font.GetFontRenderParams(),
|
| - &best_family, &best_render_params, &best_missing_glyphs))
|
| + if (CompareFamily(text, font.GetFontName(), font.GetFontRenderParams(),
|
| + run, &best_family, &best_render_params,
|
| + &best_missing_glyphs))
|
| return;
|
| }
|
|
|
| #if defined(OS_WIN)
|
| Font uniscribe_font;
|
| std::string uniscribe_family;
|
| - const base::char16* run_text = &(GetLayoutText()[run->range.start()]);
|
| + const base::char16* run_text = &(text[run->range.start()]);
|
| if (GetUniscribeFallbackFont(primary_font, run_text, run->range.length(),
|
| &uniscribe_font)) {
|
| uniscribe_family = uniscribe_font.GetFontName();
|
| - if (CompareFamily(run, uniscribe_family,
|
| - uniscribe_font.GetFontRenderParams(),
|
| + if (CompareFamily(text, uniscribe_family,
|
| + uniscribe_font.GetFontRenderParams(), run,
|
| &best_family, &best_render_params, &best_missing_glyphs))
|
| return;
|
| }
|
| @@ -1200,29 +1259,29 @@ void RenderTextHarfBuzz::ShapeRun(internal::TextRunHarfBuzz* run) {
|
| query.pixel_size = run->font_size;
|
| query.style = run->font_style;
|
| FontRenderParams fallback_render_params = GetFontRenderParams(query, NULL);
|
| - if (CompareFamily(run, family, fallback_render_params, &best_family,
|
| + if (CompareFamily(text, family, fallback_render_params, run, &best_family,
|
| &best_render_params, &best_missing_glyphs))
|
| return;
|
| }
|
|
|
| if (!best_family.empty() &&
|
| (best_family == run->family ||
|
| - ShapeRunWithFont(run, best_family, best_render_params)))
|
| + ShapeRunWithFont(text, best_family, best_render_params, run)))
|
| return;
|
|
|
| run->glyph_count = 0;
|
| run->width = 0.0f;
|
| }
|
|
|
| -bool RenderTextHarfBuzz::ShapeRunWithFont(internal::TextRunHarfBuzz* run,
|
| +bool RenderTextHarfBuzz::ShapeRunWithFont(const base::string16& text,
|
| const std::string& font_family,
|
| - const FontRenderParams& params) {
|
| + const FontRenderParams& params,
|
| + internal::TextRunHarfBuzz* run) {
|
| // TODO(vadimt): Remove ScopedTracker below once crbug.com/431326 is fixed.
|
| tracked_objects::ScopedTracker tracking_profile0(
|
| FROM_HERE_WITH_EXPLICIT_FUNCTION(
|
| "431326 RenderTextHarfBuzz::ShapeRunWithFont0"));
|
|
|
| - const base::string16& text = GetLayoutText();
|
| skia::RefPtr<SkTypeface> skia_face =
|
| internal::CreateSkiaTypeface(font_family, run->font_style);
|
| if (skia_face == NULL)
|
| @@ -1330,4 +1389,78 @@ bool RenderTextHarfBuzz::ShapeRunWithFont(internal::TextRunHarfBuzz* run,
|
| return true;
|
| }
|
|
|
| +void RenderTextHarfBuzz::EnsureLayoutRunList() {
|
| + if (update_layout_run_list_) {
|
| + layout_run_list_.Reset();
|
| +
|
| + const base::string16& text = layout_text();
|
| + if (!text.empty()) {
|
| + TRACE_EVENT0("ui", "RenderTextHarfBuzz:EnsureLayoutRunList");
|
| + // TODO(vadimt): Remove ScopedTracker below once crbug.com/431326 is
|
| + // fixed.
|
| + tracked_objects::ScopedTracker tracking_profile1(
|
| + FROM_HERE_WITH_EXPLICIT_FUNCTION(
|
| + "431326 RenderTextHarfBuzz::EnsureLayout1"));
|
| + ItemizeTextToRuns(text, &layout_run_list_);
|
| +
|
| + // TODO(vadimt): Remove ScopedTracker below once crbug.com/431326 is
|
| + // fixed.
|
| + tracked_objects::ScopedTracker tracking_profile2(
|
| + FROM_HERE_WITH_EXPLICIT_FUNCTION(
|
| + "431326 RenderTextHarfBuzz::EnsureLayout12"));
|
| + ShapeRunList(text, &layout_run_list_);
|
| + }
|
| +
|
| + // TODO(vadimt): Remove ScopedTracker below once crbug.com/431326 is
|
| + // fixed.
|
| + tracked_objects::ScopedTracker tracking_profile14(
|
| + FROM_HERE_WITH_EXPLICIT_FUNCTION(
|
| + "431326 RenderTextHarfBuzz::EnsureLayout14"));
|
| +
|
| + std::vector<internal::Line> empty_lines;
|
| + set_lines(&empty_lines);
|
| + display_run_list_.reset();
|
| + update_display_text_ = true;
|
| + update_layout_run_list_ = false;
|
| + }
|
| + if (update_display_text_) {
|
| + UpdateDisplayText(multiline() ? 0 : layout_run_list_.width());
|
| + update_display_text_ = false;
|
| + update_display_run_list_ = text_elided();
|
| + }
|
| +}
|
| +
|
| +base::i18n::BreakIterator* RenderTextHarfBuzz::GetGraphemeIterator() {
|
| + if (update_grapheme_iterator_) {
|
| + update_grapheme_iterator_ = false;
|
| + grapheme_iterator_.reset(new base::i18n::BreakIterator(
|
| + GetDisplayText(),
|
| + base::i18n::BreakIterator::BREAK_CHARACTER));
|
| + if (!grapheme_iterator_->Init())
|
| + grapheme_iterator_.reset();
|
| + }
|
| + return grapheme_iterator_.get();
|
| +}
|
| +
|
| +size_t RenderTextHarfBuzz::TextIndexToGivenTextIndex(
|
| + const base::string16& given_text,
|
| + size_t index) {
|
| + DCHECK(given_text == layout_text() || given_text == display_text());
|
| + DCHECK_LE(index, text().length());
|
| + ptrdiff_t i = obscured() ? UTF16IndexToOffset(text(), 0, index) : index;
|
| + CHECK_GE(i, 0);
|
| + // Clamp indices to the length of the given layout or display text.
|
| + return std::min<size_t>(given_text.length(), i);
|
| +}
|
| +
|
| +internal::TextRunList* RenderTextHarfBuzz::GetRunList() {
|
| + DCHECK(!update_layout_run_list_);
|
| + DCHECK(!update_display_run_list_);
|
| + return text_elided() ? display_run_list_.get() : &layout_run_list_;
|
| +}
|
| +
|
| +const internal::TextRunList* RenderTextHarfBuzz::GetRunList() const {
|
| + return const_cast<RenderTextHarfBuzz*>(this)->GetRunList();
|
| +}
|
| +
|
| } // namespace gfx
|
|
|