OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "ui/gfx/render_text_harfbuzz.h" | 5 #include "ui/gfx/render_text_harfbuzz.h" |
6 | 6 |
7 #include <limits> | 7 #include <limits> |
8 #include <set> | 8 #include <set> |
9 | 9 |
10 #include "base/i18n/bidi_line_iterator.h" | 10 #include "base/i18n/bidi_line_iterator.h" |
(...skipping 627 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
638 if (is_rtl) { | 638 if (is_rtl) { |
639 GetClusterAtImpl(pos, range, glyph_to_char.rbegin(), glyph_to_char.rend(), | 639 GetClusterAtImpl(pos, range, glyph_to_char.rbegin(), glyph_to_char.rend(), |
640 true, chars, glyphs); | 640 true, chars, glyphs); |
641 return; | 641 return; |
642 } | 642 } |
643 | 643 |
644 GetClusterAtImpl(pos, range, glyph_to_char.begin(), glyph_to_char.end(), | 644 GetClusterAtImpl(pos, range, glyph_to_char.begin(), glyph_to_char.end(), |
645 false, chars, glyphs); | 645 false, chars, glyphs); |
646 } | 646 } |
647 | 647 |
648 RangeF TextRunHarfBuzz::GetGraphemeBounds( | 648 RangeF TextRunHarfBuzz::GetGraphemeBounds(RenderTextHarfBuzz* render_text, |
649 base::i18n::BreakIterator* grapheme_iterator, | 649 size_t text_index) { |
650 size_t text_index) { | |
651 DCHECK_LT(text_index, range.end()); | 650 DCHECK_LT(text_index, range.end()); |
652 if (glyph_count == 0) | 651 if (glyph_count == 0) |
653 return RangeF(preceding_run_widths, preceding_run_widths + width); | 652 return RangeF(preceding_run_widths, preceding_run_widths + width); |
654 | 653 |
655 Range chars; | 654 Range chars; |
656 Range glyphs; | 655 Range glyphs; |
657 GetClusterAt(text_index, &chars, &glyphs); | 656 GetClusterAt(text_index, &chars, &glyphs); |
658 const float cluster_begin_x = positions[glyphs.start()].x(); | 657 const float cluster_begin_x = positions[glyphs.start()].x(); |
659 const float cluster_end_x = glyphs.end() < glyph_count ? | 658 const float cluster_end_x = glyphs.end() < glyph_count ? |
660 positions[glyphs.end()].x() : SkFloatToScalar(width); | 659 positions[glyphs.end()].x() : SkFloatToScalar(width); |
661 | 660 |
662 // A cluster consists of a number of code points and corresponds to a number | 661 // A cluster consists of a number of code points and corresponds to a number |
663 // of glyphs that should be drawn together. A cluster can contain multiple | 662 // of glyphs that should be drawn together. A cluster can contain multiple |
664 // graphemes. In order to place the cursor at a grapheme boundary inside the | 663 // graphemes. In order to place the cursor at a grapheme boundary inside the |
665 // cluster, we simply divide the cluster width by the number of graphemes. | 664 // cluster, we simply divide the cluster width by the number of graphemes. |
666 if (chars.length() > 1 && grapheme_iterator) { | 665 // Note: The first call to GetGraphemeIterator() can be expensive, so avoid |
| 666 // doing it unless it's actually needed (when length > 1). |
| 667 if (chars.length() > 1 && render_text->GetGraphemeIterator()) { |
667 int before = 0; | 668 int before = 0; |
668 int total = 0; | 669 int total = 0; |
| 670 base::i18n::BreakIterator* grapheme_iterator = |
| 671 render_text->GetGraphemeIterator(); |
669 for (size_t i = chars.start(); i < chars.end(); ++i) { | 672 for (size_t i = chars.start(); i < chars.end(); ++i) { |
670 if (grapheme_iterator->IsGraphemeBoundary(i)) { | 673 if (grapheme_iterator->IsGraphemeBoundary(i)) { |
671 if (i < text_index) | 674 if (i < text_index) |
672 ++before; | 675 ++before; |
673 ++total; | 676 ++total; |
674 } | 677 } |
675 } | 678 } |
676 DCHECK_GT(total, 0); | 679 DCHECK_GT(total, 0); |
677 if (total > 1) { | 680 if (total > 1) { |
678 if (is_rtl) | 681 if (is_rtl) |
(...skipping 183 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
862 Range RenderTextHarfBuzz::GetGlyphBounds(size_t index) { | 865 Range RenderTextHarfBuzz::GetGlyphBounds(size_t index) { |
863 EnsureLayout(); | 866 EnsureLayout(); |
864 const size_t run_index = | 867 const size_t run_index = |
865 GetRunContainingCaret(SelectionModel(index, CURSOR_FORWARD)); | 868 GetRunContainingCaret(SelectionModel(index, CURSOR_FORWARD)); |
866 internal::TextRunList* run_list = GetRunList(); | 869 internal::TextRunList* run_list = GetRunList(); |
867 // Return edge bounds if the index is invalid or beyond the layout text size. | 870 // Return edge bounds if the index is invalid or beyond the layout text size. |
868 if (run_index >= run_list->size()) | 871 if (run_index >= run_list->size()) |
869 return Range(GetStringSize().width()); | 872 return Range(GetStringSize().width()); |
870 const size_t layout_index = TextIndexToDisplayIndex(index); | 873 const size_t layout_index = TextIndexToDisplayIndex(index); |
871 internal::TextRunHarfBuzz* run = run_list->runs()[run_index]; | 874 internal::TextRunHarfBuzz* run = run_list->runs()[run_index]; |
872 RangeF bounds = | 875 RangeF bounds = run->GetGraphemeBounds(this, layout_index); |
873 run->GetGraphemeBounds(GetGraphemeIterator(), layout_index); | |
874 // If cursor is enabled, extend the last glyph up to the rightmost cursor | 876 // If cursor is enabled, extend the last glyph up to the rightmost cursor |
875 // position since clients expect them to be contiguous. | 877 // position since clients expect them to be contiguous. |
876 if (cursor_enabled() && run_index == run_list->size() - 1 && | 878 if (cursor_enabled() && run_index == run_list->size() - 1 && |
877 index == (run->is_rtl ? run->range.start() : run->range.end() - 1)) | 879 index == (run->is_rtl ? run->range.start() : run->range.end() - 1)) |
878 bounds.set_end(std::ceil(bounds.end())); | 880 bounds.set_end(std::ceil(bounds.end())); |
879 return run->is_rtl ? RangeF(bounds.end(), bounds.start()).Round() | 881 return run->is_rtl ? RangeF(bounds.end(), bounds.start()).Round() |
880 : bounds.Round(); | 882 : bounds.Round(); |
881 } | 883 } |
882 | 884 |
| 885 base::i18n::BreakIterator* RenderTextHarfBuzz::GetGraphemeIterator() { |
| 886 if (update_grapheme_iterator_) { |
| 887 update_grapheme_iterator_ = false; |
| 888 grapheme_iterator_.reset(new base::i18n::BreakIterator( |
| 889 GetDisplayText(), base::i18n::BreakIterator::BREAK_CHARACTER)); |
| 890 if (!grapheme_iterator_->Init()) |
| 891 grapheme_iterator_.reset(); |
| 892 } |
| 893 return grapheme_iterator_.get(); |
| 894 } |
| 895 |
883 int RenderTextHarfBuzz::GetDisplayTextBaseline() { | 896 int RenderTextHarfBuzz::GetDisplayTextBaseline() { |
884 EnsureLayout(); | 897 EnsureLayout(); |
885 return lines()[0].baseline; | 898 return lines()[0].baseline; |
886 } | 899 } |
887 | 900 |
888 SelectionModel RenderTextHarfBuzz::AdjacentCharSelectionModel( | 901 SelectionModel RenderTextHarfBuzz::AdjacentCharSelectionModel( |
889 const SelectionModel& selection, | 902 const SelectionModel& selection, |
890 VisualCursorDirection direction) { | 903 VisualCursorDirection direction) { |
891 DCHECK(!update_display_run_list_); | 904 DCHECK(!update_display_run_list_); |
892 | 905 |
(...skipping 119 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1012 for (size_t i = 0; i < run_list->size(); ++i) { | 1025 for (size_t i = 0; i < run_list->size(); ++i) { |
1013 internal::TextRunHarfBuzz* run = | 1026 internal::TextRunHarfBuzz* run = |
1014 run_list->runs()[run_list->visual_to_logical(i)]; | 1027 run_list->runs()[run_list->visual_to_logical(i)]; |
1015 Range intersection = run->range.Intersect(layout_range); | 1028 Range intersection = run->range.Intersect(layout_range); |
1016 if (!intersection.IsValid()) | 1029 if (!intersection.IsValid()) |
1017 continue; | 1030 continue; |
1018 DCHECK(!intersection.is_reversed()); | 1031 DCHECK(!intersection.is_reversed()); |
1019 const size_t left_index = | 1032 const size_t left_index = |
1020 run->is_rtl ? intersection.end() - 1 : intersection.start(); | 1033 run->is_rtl ? intersection.end() - 1 : intersection.start(); |
1021 const Range leftmost_character_x = | 1034 const Range leftmost_character_x = |
1022 run->GetGraphemeBounds(GetGraphemeIterator(), left_index).Round(); | 1035 run->GetGraphemeBounds(this, left_index).Round(); |
1023 const size_t right_index = | 1036 const size_t right_index = |
1024 run->is_rtl ? intersection.start() : intersection.end() - 1; | 1037 run->is_rtl ? intersection.start() : intersection.end() - 1; |
1025 const Range rightmost_character_x = | 1038 const Range rightmost_character_x = |
1026 run->GetGraphemeBounds(GetGraphemeIterator(), right_index).Round(); | 1039 run->GetGraphemeBounds(this, right_index).Round(); |
1027 Range range_x(leftmost_character_x.start(), rightmost_character_x.end()); | 1040 Range range_x(leftmost_character_x.start(), rightmost_character_x.end()); |
1028 DCHECK(!range_x.is_reversed()); | 1041 DCHECK(!range_x.is_reversed()); |
1029 if (range_x.is_empty()) | 1042 if (range_x.is_empty()) |
1030 continue; | 1043 continue; |
1031 | 1044 |
1032 // Union this with the last range if they're adjacent. | 1045 // Union this with the last range if they're adjacent. |
1033 DCHECK(bounds.empty() || bounds.back().GetMax() <= range_x.GetMin()); | 1046 DCHECK(bounds.empty() || bounds.back().GetMax() <= range_x.GetMin()); |
1034 if (!bounds.empty() && bounds.back().GetMax() == range_x.GetMin()) { | 1047 if (!bounds.empty() && bounds.back().GetMax() == range_x.GetMin()) { |
1035 range_x = Range(bounds.back().GetMin(), range_x.GetMax()); | 1048 range_x = Range(bounds.back().GetMin(), range_x.GetMax()); |
1036 bounds.pop_back(); | 1049 bounds.pop_back(); |
(...skipping 524 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1561 update_display_text_ = true; | 1574 update_display_text_ = true; |
1562 update_layout_run_list_ = false; | 1575 update_layout_run_list_ = false; |
1563 } | 1576 } |
1564 if (update_display_text_) { | 1577 if (update_display_text_) { |
1565 UpdateDisplayText(multiline() ? 0 : layout_run_list_.width()); | 1578 UpdateDisplayText(multiline() ? 0 : layout_run_list_.width()); |
1566 update_display_text_ = false; | 1579 update_display_text_ = false; |
1567 update_display_run_list_ = text_elided(); | 1580 update_display_run_list_ = text_elided(); |
1568 } | 1581 } |
1569 } | 1582 } |
1570 | 1583 |
1571 base::i18n::BreakIterator* RenderTextHarfBuzz::GetGraphemeIterator() { | |
1572 if (update_grapheme_iterator_) { | |
1573 update_grapheme_iterator_ = false; | |
1574 grapheme_iterator_.reset(new base::i18n::BreakIterator( | |
1575 GetDisplayText(), | |
1576 base::i18n::BreakIterator::BREAK_CHARACTER)); | |
1577 if (!grapheme_iterator_->Init()) | |
1578 grapheme_iterator_.reset(); | |
1579 } | |
1580 return grapheme_iterator_.get(); | |
1581 } | |
1582 | |
1583 internal::TextRunList* RenderTextHarfBuzz::GetRunList() { | 1584 internal::TextRunList* RenderTextHarfBuzz::GetRunList() { |
1584 DCHECK(!update_layout_run_list_); | 1585 DCHECK(!update_layout_run_list_); |
1585 DCHECK(!update_display_run_list_); | 1586 DCHECK(!update_display_run_list_); |
1586 return text_elided() ? display_run_list_.get() : &layout_run_list_; | 1587 return text_elided() ? display_run_list_.get() : &layout_run_list_; |
1587 } | 1588 } |
1588 | 1589 |
1589 const internal::TextRunList* RenderTextHarfBuzz::GetRunList() const { | 1590 const internal::TextRunList* RenderTextHarfBuzz::GetRunList() const { |
1590 return const_cast<RenderTextHarfBuzz*>(this)->GetRunList(); | 1591 return const_cast<RenderTextHarfBuzz*>(this)->GetRunList(); |
1591 } | 1592 } |
1592 | 1593 |
1593 } // namespace gfx | 1594 } // namespace gfx |
OLD | NEW |