Chromium Code Reviews| 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..2abf3ce070533ebf47512011aaf5ac580643fae0 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,59 @@ RangeF TextRunHarfBuzz::GetGraphemeBounds( |
| preceding_run_widths + cluster_end_x); |
| } |
| +TextRunList::TextRunList() {} |
| + |
| +TextRunList::~TextRunList() {} |
| + |
| +void TextRunList::Add(internal::TextRunHarfBuzz* run) { |
| + runs_.push_back(run); |
| +} |
| + |
| +void TextRunList::Reset() { |
| + runs_.clear(); |
| +} |
| + |
| +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. |
| + 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; |
| + } |
| +} |
| + |
| +int TextRunList::GetWidth() const { |
| + int width = 0; |
| + for (auto run : runs_) |
| + width += run->width; |
| + return width; |
| +} |
| + |
| } // namespace internal |
| RenderTextHarfBuzz::RenderTextHarfBuzz() |
| : RenderText(), |
| - needs_layout_(false), |
| + update_layout_run_list_(false), |
| + update_elided_run_list_(false), |
| + update_grapheme_iterator_(false), |
| + update_elided_text_(false), |
| glyph_width_for_test_(0u) { |
| set_truncate_length(kMaxTextLength); |
| } |
| @@ -589,6 +639,21 @@ scoped_ptr<RenderText> RenderTextHarfBuzz::CreateInstanceOfSameType() const { |
| return scoped_ptr<RenderTextHarfBuzz>(new RenderTextHarfBuzz); |
| } |
| +const base::string16& RenderTextHarfBuzz::GetLayoutText() { |
| + if (multiline() || |
| + elide_behavior() == NO_ELIDE || |
| + elide_behavior() == FADE_TAIL) { |
| + UpdateElidedText(0); |
| + update_elided_text_ = false; |
| + elided_run_list_.reset(); |
| + return layout_text(); |
| + } |
| + |
| + EnsureLayoutRunList(); |
| + |
| + return text_elided() ? elided_text() : layout_text(); |
| +} |
| + |
| Size RenderTextHarfBuzz::GetStringSize() { |
| const SizeF size_f = GetStringSizeF(); |
| return Size(std::ceil(size_f.width()), size_f.height()); |
| @@ -605,9 +670,12 @@ 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()) |
| + |
| + internal::TextRunList* run_list = GetRunList(); |
| + |
| + if (run_index >= run_list->size()) |
| return EdgeSelectionModel((x < 0) ? CURSOR_LEFT : CURSOR_RIGHT); |
| - const internal::TextRunHarfBuzz& run = *runs_[run_index]; |
| + const internal::TextRunHarfBuzz& run = *run_list->runs()[run_index]; |
| for (size_t i = 0; i < run.glyph_count; ++i) { |
| const SkScalar end = |
| @@ -631,14 +699,17 @@ 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(LayoutIndexToTextIndex(run->range.start()), |
| + LayoutIndexToTextIndex(run->range.end())))); |
| } |
| return spans; |
| @@ -648,16 +719,17 @@ 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]; |
| + 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 ? |
| @@ -672,20 +744,23 @@ int RenderTextHarfBuzz::GetLayoutTextBaseline() { |
| SelectionModel RenderTextHarfBuzz::AdjacentCharSelectionModel( |
| const SelectionModel& selection, |
| VisualCursorDirection direction) { |
| - DCHECK(!needs_layout_); |
| + DCHECK(!update_elided_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) { |
| @@ -700,11 +775,11 @@ SelectionModel RenderTextHarfBuzz::AdjacentCharSelectionModel( |
| } |
| } |
| // 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 +833,16 @@ 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,7 +852,7 @@ SelectionModel RenderTextHarfBuzz::AdjacentWordSelectionModel( |
| } |
| std::vector<Rect> RenderTextHarfBuzz::GetSubstringBounds(const Range& range) { |
| - DCHECK(!needs_layout_); |
| + DCHECK(!update_elided_run_list_); |
| DCHECK(Range(0, text().length()).Contains(range)); |
| Range layout_range(TextIndexToLayoutIndex(range.start()), |
| TextIndexToLayoutIndex(range.end())); |
| @@ -785,18 +863,21 @@ std::vector<Rect> RenderTextHarfBuzz::GetSubstringBounds(const Range& range) { |
| 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 +892,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::TextIndexToLayoutIndex(size_t index) { |
| + return TextIndexToGivenTextIndex(GetLayoutText(), index); |
| } |
| -size_t RenderTextHarfBuzz::LayoutIndexToTextIndex(size_t index) const { |
| +size_t RenderTextHarfBuzz::LayoutIndexToTextIndex(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 +916,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::OnLayoutTextShapeChanged(bool text_changed) { |
| + update_layout_run_list_ = true; |
| + OnElidedTextShapeChanged(); |
| } |
| -void RenderTextHarfBuzz::ResetLayout() { |
| - needs_layout_ = true; |
| +void RenderTextHarfBuzz::OnElidedTextShapeChanged() { |
| + update_elided_text_ = true; |
| + update_grapheme_iterator_ = true; |
| } |
| void RenderTextHarfBuzz::EnsureLayout() { |
| @@ -855,61 +936,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_elided_run_list_) { |
| + DCHECK(text_elided()); |
| + const base::string16& elided_text = GetLayoutText(); |
| + elided_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 (!elided_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(elided_text, elided_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(elided_text, elided_run_list_.get()); |
| } |
| + update_elided_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 +977,22 @@ 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_); |
| + GetLayoutText(), 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 +1000,8 @@ void RenderTextHarfBuzz::EnsureLayout() { |
| } |
| void RenderTextHarfBuzz::DrawVisualText(Canvas* canvas) { |
| - DCHECK(!needs_layout_); |
| + DCHECK(!update_layout_run_list_); |
| + DCHECK(!update_elided_run_list_); |
| if (lines().empty()) |
| return; |
| @@ -948,12 +1010,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,33 +1072,38 @@ void RenderTextHarfBuzz::DrawVisualText(Canvas* canvas) { |
| } |
| size_t RenderTextHarfBuzz::GetRunContainingCaret( |
| - const SelectionModel& caret) const { |
| - DCHECK(!needs_layout_); |
| + const SelectionModel& caret) { |
| + DCHECK(!update_elided_run_list_); |
| size_t layout_position = TextIndexToLayoutIndex(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_elided_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( |
| @@ -1052,8 +1120,9 @@ SelectionModel RenderTextHarfBuzz::LastSelectionModelInsideRun( |
| return SelectionModel(position, CURSOR_FORWARD); |
| } |
| -void RenderTextHarfBuzz::ItemizeText() { |
| - const base::string16& text = GetLayoutText(); |
| +void RenderTextHarfBuzz::ItemizeTextToRuns( |
| + const base::string16& text, |
| + internal::TextRunList* run_list_out) { |
| const bool is_text_rtl = GetTextDirection() == base::i18n::RIGHT_TO_LEFT; |
| DCHECK_NE(0U, text.length()); |
| @@ -1064,8 +1133,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 +1155,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 +1164,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 |
| @@ -1110,30 +1179,24 @@ void RenderTextHarfBuzz::ItemizeText() { |
| style.UpdatePosition(LayoutIndexToTextIndex(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 +1208,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 +1226,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 +1272,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 +1402,80 @@ bool RenderTextHarfBuzz::ShapeRunWithFont(internal::TextRunHarfBuzz* run, |
| return true; |
| } |
| +void RenderTextHarfBuzz::EnsureLayoutRunList() { |
| + if (update_layout_run_list_) { |
| + update_layout_run_list_ = false; |
| + 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")); |
|
oshima
2015/02/12 00:18:54
This is not a typo. I usede the same trace string
|
| + 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); |
| + elided_run_list_.reset(); |
| + update_elided_text_ = true; |
| + } |
| + if (update_elided_text_) { |
| + update_elided_text_ = false; |
| + UpdateElidedText(layout_run_list_.GetWidth()); |
| + update_elided_run_list_ = text_elided(); |
| + } |
| +} |
| + |
| +base::i18n::BreakIterator* RenderTextHarfBuzz::GetGraphemeIterator() { |
| + if (update_grapheme_iterator_) { |
| + update_grapheme_iterator_ = false; |
| + grapheme_iterator_.reset(new base::i18n::BreakIterator( |
| + GetLayoutText(), |
| + 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 == elided_text()); |
| + 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>(given_text.length(), i); |
| +} |
| + |
| +internal::TextRunList* RenderTextHarfBuzz::GetRunList() { |
| + DCHECK(!update_layout_run_list_); |
| + DCHECK(!update_elided_run_list_); |
| + return text_elided() ? elided_run_list_.get() : &layout_run_list_; |
| +} |
| + |
| +const internal::TextRunList* RenderTextHarfBuzz::GetRunList() const { |
| + DCHECK(!update_layout_run_list_); |
| + DCHECK(!update_elided_run_list_); |
| + return text_elided() ? elided_run_list_.get() : &layout_run_list_; |
| +} |
| + |
| } // namespace gfx |