 Chromium Code Reviews
 Chromium Code Reviews Issue 1070223004:
  Stop combining text runs which are connected by 'COMMON' blocks.  (Closed) 
  Base URL: https://chromium.googlesource.com/chromium/src.git@master
    
  
    Issue 1070223004:
  Stop combining text runs which are connected by 'COMMON' blocks.  (Closed) 
  Base URL: https://chromium.googlesource.com/chromium/src.git@master| Index: ui/gfx/render_text_harfbuzz.cc | 
| diff --git a/ui/gfx/render_text_harfbuzz.cc b/ui/gfx/render_text_harfbuzz.cc | 
| index 32a9e554f60b47127cef9d426aef71a9f410adfa..584d6aa81b96c8974a09c01f4f714c3a49051648 100644 | 
| --- a/ui/gfx/render_text_harfbuzz.cc | 
| +++ b/ui/gfx/render_text_harfbuzz.cc | 
| @@ -49,9 +49,7 @@ const size_t kMaxScripts = 5; | 
| // therefore it needs to be trigerred as fallbacks. See crbug.com/448909 | 
| bool IsUnusualBlockCode(UBlockCode block_code) { | 
| return block_code == UBLOCK_GEOMETRIC_SHAPES || | 
| - block_code == UBLOCK_MISCELLANEOUS_SYMBOLS || | 
| - block_code == UBLOCK_DINGBATS || | 
| - block_code == UBLOCK_EMOTICONS; | 
| + block_code == UBLOCK_MISCELLANEOUS_SYMBOLS; | 
| } | 
| bool IsBracket(UChar32 character) { | 
| @@ -90,15 +88,13 @@ size_t FindRunBreakingCharacter(const base::string16& text, | 
| return run_break; | 
| } | 
| -// If the given scripts match, returns the one that isn't USCRIPT_COMMON or | 
| -// USCRIPT_INHERITED, i.e. the more specific one. Otherwise returns | 
| +// If the given scripts match, returns the one that isn't USCRIPT_INHERITED, | 
| +// i.e. the more specific one. Otherwise returns | 
| // USCRIPT_INVALID_CODE. | 
| UScriptCode ScriptIntersect(UScriptCode first, UScriptCode second) { | 
| - if (first == second || | 
| - (second > USCRIPT_INVALID_CODE && second <= USCRIPT_INHERITED)) { | 
| + if (first == second || second == USCRIPT_INHERITED) | 
| return first; | 
| - } | 
| - if (first > USCRIPT_INVALID_CODE && first <= USCRIPT_INHERITED) | 
| + if (first == USCRIPT_INHERITED) | 
| return second; | 
| return USCRIPT_INVALID_CODE; | 
| } | 
| @@ -166,6 +162,9 @@ int ScriptInterval(const base::string16& text, | 
| *script = scripts[0]; | 
| while (char_iterator.Advance()) { | 
| + // Special handling to merge white space into the previous run. | 
| + if (u_isUWhiteSpace(char_iterator.get())) | 
| + continue; | 
| ScriptSetIntersect(char_iterator.get(), scripts, &scripts_size); | 
| if (scripts_size == 0U) | 
| return char_iterator.array_pos(); | 
| @@ -236,37 +235,16 @@ class HarfBuzzLineBreaker { | 
| text_(text), | 
| words_(words), | 
| run_list_(run_list), | 
| - text_x_(0), | 
| - line_x_(0), | 
| max_descent_(0), | 
| - max_ascent_(0) { | 
| + max_ascent_(0), | 
| + text_x_(0), | 
| + available_width_(max_width_) { | 
| DCHECK_EQ(multiline_, (words_ != nullptr)); | 
| - AdvanceLine(); | 
| + RunLineBreaking(); | 
| } | 
| - // Breaks the run at given |run_index| into Line structs. | 
| - void AddRun(int run_index) { | 
| - const internal::TextRunHarfBuzz* run = run_list_.runs()[run_index]; | 
| - base::char16 first_char = text_[run->range.start()]; | 
| - if (multiline_ && first_char == '\n') { | 
| - AdvanceLine(); | 
| - } else if (multiline_ && (line_x_ + SkFloatToScalar(run->width)) > | 
| - max_width_) { | 
| - BreakRun(run_index); | 
| - } else { | 
| - AddSegment(run_index, run->range, run->width); | 
| - } | 
| - } | 
| - | 
| - // Finishes line breaking and outputs the results. Can be called at most once. | 
| - void Finalize(std::vector<internal::Line>* lines, SizeF* size) { | 
| - DCHECK(!lines_.empty()); | 
| - // Add an empty line to finish the line size calculation and remove it. | 
| - AdvanceLine(); | 
| - lines_.pop_back(); | 
| - *size = total_size_; | 
| - lines->swap(lines_); | 
| - } | 
| + std::vector<internal::Line> GetLines() const { return lines_; } | 
| 
Jun Mukai
2015/05/15 20:16:01
Do not do this. This copies all of the line struct
 
xdai1
2015/05/15 23:41:17
Removed it and passed values through FinalizeLines
 | 
| + SizeF GetTotalSize() const { return total_size_; } | 
| private: | 
| // A (line index, segment index) pair that specifies a segment in |lines_|. | 
| @@ -276,130 +254,59 @@ class HarfBuzzLineBreaker { | 
| return &lines_[handle.first].segments[handle.second]; | 
| } | 
| - // Breaks a run into segments that fit in the last line in |lines_| and adds | 
| - // them. Adds a new Line to the back of |lines_| whenever a new segment can't | 
| - // be added without the Line's width exceeding |max_width_|. | 
| - void BreakRun(int run_index) { | 
| - const internal::TextRunHarfBuzz& run = *(run_list_.runs()[run_index]); | 
| - SkScalar width = 0; | 
| - size_t next_char = run.range.start(); | 
| - | 
| - // Break the run until it fits the current line. | 
| - while (next_char < run.range.end()) { | 
| - const size_t current_char = next_char; | 
| - size_t end_char = next_char; | 
| - const bool skip_line = | 
| - BreakRunAtWidth(run, current_char, &width, &end_char, &next_char); | 
| - AddSegment(run_index, Range(current_char, end_char), | 
| - SkScalarToFloat(width)); | 
| - if (skip_line) | 
| - AdvanceLine(); | 
| + // Runs line breaking algorithm and break |text_| into lines if applicable. | 
| + void RunLineBreaking() { | 
| 
Jun Mukai
2015/05/15 20:16:00
I don't think this method is necessary.
- revert
 
xdai1
2015/05/15 23:41:17
Done.
 | 
| + AdvanceLine(); | 
| + if (!multiline_) | 
| + ConstructSingleLine(); | 
| + else | 
| + ConstructMultiLines(); | 
| + // Finishes line breaking and calculates the line size. | 
| + FinalizeLines(); | 
| + } | 
| + | 
| + // Constructs a single line for |text_| using |run_list_|. | 
| + void ConstructSingleLine() { | 
| + for (auto iter = run_list_.runs().begin(); iter != run_list_.runs().end(); | 
| 
Jun Mukai
2015/05/15 20:16:00
It's better to use 'for (size_t i = 0; i < run_lis
 
xdai1
2015/05/15 23:41:17
Done. but I don't know why...
 | 
| + iter++) { | 
| + internal::LineSegment segment; | 
| + segment.run = std::distance(run_list_.runs().begin(), iter); | 
| + segment.char_range = (*iter)->range; | 
| + segment.x_range = Range(SkScalarCeilToInt(text_x_), | 
| + SkScalarCeilToInt(text_x_ + (*iter)->width)); | 
| + segment.width = (*iter)->width; | 
| + AddLineSegment(segment); | 
| } | 
| } | 
| - // Starting from |start_char|, finds a suitable line break position at or | 
| - // before available width using word break. If the current position is at the | 
| - // beginning of a line, this function will not roll back to |start_char| and | 
| - // |*next_char| will be greater than |start_char| (to avoid constructing empty | 
| - // lines). It stores the end of the segment range to |end_char|, which can be | 
| - // smaller than |*next_char| for certain word wrapping behavior. | 
| - // Returns whether to skip the line before |*next_char|. | 
| - // TODO(ckocagil): We might have to reshape after breaking at ligatures. | 
| - // See whether resolving the TODO above resolves this too. | 
| - // TODO(ckocagil): Do not reserve width for whitespace at the end of lines. | 
| - bool BreakRunAtWidth(const internal::TextRunHarfBuzz& run, | 
| - size_t start_char, | 
| - SkScalar* width, | 
| - size_t* end_char, | 
| - size_t* next_char) { | 
| - DCHECK(words_); | 
| - DCHECK(run.range.Contains(Range(start_char, start_char + 1))); | 
| - SkScalar available_width = max_width_ - line_x_; | 
| - BreakList<size_t>::const_iterator word = words_->GetBreak(start_char); | 
| - BreakList<size_t>::const_iterator next_word = word + 1; | 
| - // Width from |std::max(word->first, start_char)| to the current character. | 
| - SkScalar word_width = 0; | 
| - *width = 0; | 
| - | 
| - Range char_range; | 
| - SkScalar truncated_width = 0; | 
| - for (size_t i = start_char; i < run.range.end(); i += char_range.length()) { | 
| - // |word| holds the word boundary at or before |i|, and |next_word| holds | 
| - // the word boundary right after |i|. Advance both |word| and |next_word| | 
| - // when |i| reaches |next_word|. | 
| - if (next_word != words_->breaks().end() && i >= next_word->first) { | 
| - if (*width > available_width) { | 
| - DCHECK_NE(WRAP_LONG_WORDS, word_wrap_behavior_); | 
| - *next_char = i; | 
| - if (word_wrap_behavior_ != TRUNCATE_LONG_WORDS) | 
| - *end_char = *next_char; | 
| - else | 
| - *width = truncated_width; | 
| - return true; | 
| + // Constructs multiple lines for |text_| based on words iteration approach. | 
| + void ConstructMultiLines() { | 
| + for (auto iter = words_->breaks().begin(); iter != words_->breaks().end(); | 
| + iter++) { | 
| 
Jun Mukai
2015/05/15 20:16:00
for (const auto& word : words_->breaks()) { ... }
 
xdai1
2015/05/15 23:41:18
Done.
 | 
| + std::vector<internal::LineSegment> word_segments; | 
| + SkScalar word_width = GetWordWidth(iter, word_segments); | 
| + | 
| + bool new_line = false; | 
| + if (!word_segments.empty()) { | 
| + // If the word is end with '\n', we should advance a new line after | 
| + // adding the word to current line. | 
| + internal::LineSegment last_segment = word_segments.back(); | 
| 
Jun Mukai
2015/05/15 20:15:59
const internal::LineSegment&
 
xdai1
2015/05/15 23:41:17
Done.
 | 
| + base::char16 last_char = text_[last_segment.char_range.start()]; | 
| 
Jun Mukai
2015/05/15 20:16:00
const base::char16
 
xdai1
2015/05/15 23:41:18
Done.
 | 
| + if (last_char == '\n') { | 
| + new_line = true; | 
| + word_width -= last_segment.width; | 
| + word_segments.pop_back(); | 
| } | 
| - word = next_word++; | 
| - word_width = 0; | 
| } | 
| - Range glyph_range; | 
| - run.GetClusterAt(i, &char_range, &glyph_range); | 
| - DCHECK_LT(0U, char_range.length()); | 
| - | 
| - SkScalar char_width = ((glyph_range.end() >= run.glyph_count) | 
| - ? SkFloatToScalar(run.width) | 
| - : run.positions[glyph_range.end()].x()) - | 
| - run.positions[glyph_range.start()].x(); | 
| - | 
| - *width += char_width; | 
| - word_width += char_width; | 
| - | 
| - // TODO(mukai): implement ELIDE_LONG_WORDS. | 
| - if (*width > available_width) { | 
| - if (line_x_ != 0 || word_width < *width) { | 
| - // Roll back one word. | 
| - *width -= word_width; | 
| - *next_char = std::max(word->first, start_char); | 
| - *end_char = *next_char; | 
| - return true; | 
| - } else if (word_wrap_behavior_ == WRAP_LONG_WORDS) { | 
| - if (char_width < *width) { | 
| - // Roll back one character. | 
| - *width -= char_width; | 
| - *next_char = i; | 
| - } else { | 
| - // Continue from the next character. | 
| - *next_char = i + char_range.length(); | 
| - } | 
| - *end_char = *next_char; | 
| - return true; | 
| - } | 
| - } else { | 
| - *end_char = char_range.end(); | 
| - truncated_width = *width; | 
| - } | 
| - } | 
| - | 
| - if (word_wrap_behavior_ == TRUNCATE_LONG_WORDS) | 
| - *width = truncated_width; | 
| - *end_char = *next_char = run.range.end(); | 
| - return false; | 
| - } | 
| - | 
| - // RTL runs are broken in logical order but displayed in visual order. To find | 
| - // the text-space coordinate (where it would fall in a single-line text) | 
| - // |x_range| of RTL segments, segment widths are applied in reverse order. | 
| - // e.g. {[5, 10], [10, 40]} will become {[35, 40], [5, 35]}. | 
| - void UpdateRTLSegmentRanges() { | 
| - if (rtl_segments_.empty()) | 
| - return; | 
| - float x = SegmentFromHandle(rtl_segments_[0])->x_range.start(); | 
| - for (size_t i = rtl_segments_.size(); i > 0; --i) { | 
| - internal::LineSegment* segment = SegmentFromHandle(rtl_segments_[i - 1]); | 
| - const float segment_width = segment->width; | 
| - segment->x_range = Range(x, x + segment_width); | 
| - x += segment_width; | 
| + // If the word is not the first word in the line and it can't fit into | 
| + // the current line, advance a new line. | 
| + if (word_width > available_width_ && available_width_ != max_width_) | 
| + AdvanceLine(); | 
| 
Jun Mukai
2015/05/15 20:16:00
I think this is not necessary, and if behavior is
 
xdai1
2015/05/15 23:41:18
As we discussed offline, this logic is still neede
 | 
| + AddWordToLine(word_segments); | 
| + if (new_line) | 
| + AdvanceLine(); | 
| } | 
| - rtl_segments_.clear(); | 
| } | 
| // Finishes the size calculations of the last Line in |lines_|. Adds a new | 
| @@ -414,35 +321,100 @@ class HarfBuzzLineBreaker { | 
| run_list_.logical_to_visual(s2.run); | 
| }); | 
| line->size.set_height(std::max(min_height_, max_descent_ + max_ascent_)); | 
| - line->baseline = | 
| - std::max(min_baseline_, SkScalarRoundToInt(max_ascent_)); | 
| + line->baseline = std::max(min_baseline_, SkScalarRoundToInt(max_ascent_)); | 
| line->preceding_heights = std::ceil(total_size_.height()); | 
| total_size_.set_height(total_size_.height() + line->size.height()); | 
| total_size_.set_width(std::max(total_size_.width(), line->size.width())); | 
| } | 
| max_descent_ = 0; | 
| max_ascent_ = 0; | 
| - line_x_ = 0; | 
| + available_width_ = max_width_; | 
| lines_.push_back(internal::Line()); | 
| } | 
| - // Adds a new segment with the given properties to |lines_.back()|. | 
| - void AddSegment(int run_index, Range char_range, float width) { | 
| - if (char_range.is_empty()) { | 
| - DCHECK_EQ(0, width); | 
| + // Adds word to the current line. A word may contain multiple segments. If the | 
| + // word is the first word in line and its width exceeds |available_width_|, | 
| + // ignore/truncate/wrap it according to |word_wrap_behavior_|. | 
| + void AddWordToLine(std::vector<internal::LineSegment>& segments) { | 
| 
Jun Mukai
2015/05/15 20:16:00
const std::vector<internal::LineSegment>&
 
xdai1
2015/05/15 23:41:18
This can't be const since the element inside of th
 
Jun Mukai
2015/05/16 00:21:56
If you really need to modify this parameter, pass
 
xdai1
2015/05/18 17:41:26
Done.
 | 
| + if (segments.empty() || lines_.empty()) | 
| 
Jun Mukai
2015/05/15 20:16:00
lines_ shouldn't be empty, because AdvanceLine() m
 
xdai1
2015/05/15 23:41:17
Done.
 | 
| return; | 
| - } | 
| - const internal::TextRunHarfBuzz& run = *(run_list_.runs()[run_index]); | 
| - internal::LineSegment segment; | 
| - segment.run = run_index; | 
| - segment.char_range = char_range; | 
| - segment.x_range = Range( | 
| - SkScalarCeilToInt(text_x_), | 
| - SkScalarCeilToInt(text_x_ + SkFloatToScalar(width))); | 
| - segment.width = width; | 
| + bool has_truncated = false; | 
| + for (auto iter = segments.begin(); iter != segments.end(); iter++) { | 
| 
Jun Mukai
2015/05/15 20:16:00
for (const internal::LineSegment& segment : segmen
 
xdai1
2015/05/15 23:41:17
Same as previous one, segment can't be const since
 | 
| + if (has_truncated) | 
| + break; | 
| + internal::LineSegment segment = *iter; | 
| + if (segment.width <= available_width_ || | 
| + word_wrap_behavior_ == IGNORE_LONG_WORDS) { | 
| + AddLineSegment(segment); | 
| + } else { | 
| + DCHECK(word_wrap_behavior_ != IGNORE_LONG_WORDS); | 
| 
Jun Mukai
2015/05/15 20:16:01
DCHECK(word_wrap_behavior_ == TRUNCATE_LONG_WORDS
 
xdai1
2015/05/15 23:41:17
Done.
 | 
| + has_truncated = (word_wrap_behavior_ == TRUNCATE_LONG_WORDS); | 
| + | 
| + while (!segment.char_range.is_empty()) { | 
| + size_t cutoff_pos; | 
| 
Jun Mukai
2015/05/15 20:16:00
size_t cutoff_pos = 0;
 
xdai1
2015/05/15 23:41:17
Done.
 | 
| + SkScalar width = | 
| + GetCutoffWidth(segment, segment.char_range.start(), cutoff_pos); | 
| + if (width == 0 && available_width_ == max_width_) { | 
| + // |max_width_| might be smaller than a single character. In this | 
| + // case we need to put at least one character in the line. | 
| + // See RenderTextTest.Multiline_MinWidth for example. | 
| + cutoff_pos = segment.char_range.start() + 1; | 
| + const internal::TextRunHarfBuzz& run = | 
| + *(run_list_.runs()[segment.run]); | 
| + Range char_range = Range(segment.char_range.start(), cutoff_pos); | 
| + Range glyph_range = run.CharRangeToGlyphRange(char_range); | 
| + width = ((glyph_range.end() >= run.glyph_count) | 
| + ? SkFloatToScalar(run.width) | 
| + : run.positions[glyph_range.end()].x()) - | 
| + run.positions[glyph_range.start()].x(); | 
| 
Jun Mukai
2015/05/15 20:16:01
Looks like it's better to add a new method to Harf
 
xdai1
2015/05/15 23:41:17
Done. Also adjusted some functions' sequence to co
 | 
| + } | 
| + if (width > 0) { | 
| + internal::LineSegment cut_segment; | 
| + cut_segment.run = segment.run; | 
| + cut_segment.char_range = | 
| + Range(segment.char_range.start(), cutoff_pos); | 
| + cut_segment.width = width; | 
| + cut_segment.x_range = Range(segment.x_range.start(), | 
| + SkScalarCeilToInt(text_x_ + width)); | 
| + AddLineSegment(cut_segment); | 
| + // Updates old segment. | 
| + segment.char_range.set_start(cutoff_pos); | 
| + segment.x_range.set_start(SkScalarCeilToInt(text_x_)); | 
| + segment.width -= width; | 
| + } | 
| + if (has_truncated) | 
| + break; | 
| + if (!segment.char_range.is_empty()) | 
| + AdvanceLine(); | 
| + } | 
| + } | 
| + } | 
| + } | 
| + // Add line segment to current line. Note in order to keep the visual order | 
| + // correct for ltr and rtl language, we need to merge segments that belong to | 
| + // a same run. | 
| + void AddLineSegment(internal::LineSegment& segment) { | 
| 
Jun Mukai
2015/05/15 20:16:00
const internal::LineSegment&
 
xdai1
2015/05/15 23:41:18
It can't be const since it might be modified in li
 
Jun Mukai
2015/05/16 00:21:56
In that case please pass as the pointer.  Our codi
 | 
| + DCHECK(!lines_.empty()); | 
| internal::Line* line = &lines_.back(); | 
| + const internal::TextRunHarfBuzz& run = *(run_list_.runs()[segment.run]); | 
| + if (!line->segments.empty()) { | 
| + internal::LineSegment last_segment = line->segments.back(); | 
| 
Jun Mukai
2015/05/15 20:16:01
internal::LineSegment& last_segment = line_segment
 
xdai1
2015/05/15 23:41:17
Done. And also make last_segment const.
 
Jun Mukai
2015/05/16 00:21:56
No.  My intention was, if the last_segment and seg
 
xdai1
2015/05/18 17:41:26
You're right. We can modify last_segment and keep
 | 
| + // Merge segments that belong to the same run. | 
| + if (last_segment.run == segment.run) { | 
| + segment.char_range.set_start(last_segment.char_range.start()); | 
| + segment.width += last_segment.width; | 
| + segment.x_range.set_start( | 
| + SkScalarCeilToInt(text_x_ - last_segment.width)); | 
| + line->segments.pop_back(); | 
| + if (run.is_rtl) | 
| + rtl_segments_.pop_back(); | 
| + line->size.set_width(line->size.width() - last_segment.width); | 
| + text_x_ -= last_segment.width; | 
| + available_width_ += last_segment.width; | 
| + } | 
| + } | 
| line->segments.push_back(segment); | 
| SkPaint paint; | 
| @@ -452,7 +424,7 @@ class HarfBuzzLineBreaker { | 
| SkPaint::FontMetrics metrics; | 
| paint.getFontMetrics(&metrics); | 
| - line->size.set_width(line->size.width() + width); | 
| + line->size.set_width(line->size.width() + segment.width); | 
| // TODO(dschuyler): Account for stylized baselines in string sizing. | 
| max_descent_ = std::max(max_descent_, metrics.fDescent); | 
| // fAscent is always negative. | 
| @@ -463,11 +435,96 @@ class HarfBuzzLineBreaker { | 
| SegmentHandle(lines_.size() - 1, line->segments.size() - 1)); | 
| // If this is the last segment of an RTL run, reprocess the text-space x | 
| // ranges of all segments from the run. | 
| - if (char_range.end() == run.range.end()) | 
| + if (segment.char_range.end() == run.range.end()) | 
| UpdateRTLSegmentRanges(); | 
| } | 
| - text_x_ += SkFloatToScalar(width); | 
| - line_x_ += SkFloatToScalar(width); | 
| + text_x_ += segment.width; | 
| + available_width_ -= segment.width; | 
| + } | 
| + | 
| + // Starts from |st_pos|, find the end position |en_pos| that the preceding | 
| + // width is no larger than |available_width_|, and return the preceding width. | 
| + SkScalar GetCutoffWidth(internal::LineSegment segment, | 
| 
Jun Mukai
2015/05/15 20:16:00
const internal::LineSegment&
 
xdai1
2015/05/15 23:41:18
Done.
 | 
| + size_t st_pos, | 
| 
Jun Mukai
2015/05/15 20:16:00
Remove st_pos. It always starts from segment.char_
 
xdai1
2015/05/15 23:41:18
Done.
 | 
| + size_t& en_pos) { | 
| 
Jun Mukai
2015/05/15 20:16:00
Return value is passed by a pointer, do not use no
 
xdai1
2015/05/15 23:41:17
I see. Thanks! Done.
 | 
| + DCHECK(segment.char_range.Contains(Range(st_pos, st_pos + 1))); | 
| + const internal::TextRunHarfBuzz& run = *(run_list_.runs()[segment.run]); | 
| + en_pos = st_pos; | 
| + SkScalar width = 0; | 
| + while (en_pos < segment.char_range.end()) { | 
| + Range char_range; | 
| + Range glyph_range; | 
| + run.GetClusterAt(en_pos, &char_range, &glyph_range); | 
| + SkScalar char_width = ((glyph_range.end() >= run.glyph_count) | 
| 
Jun Mukai
2015/05/15 20:16:00
const SkScalar
 
xdai1
2015/05/15 23:41:17
Done.
 | 
| + ? SkFloatToScalar(run.width) | 
| + : run.positions[glyph_range.end()].x()) - | 
| + run.positions[glyph_range.start()].x(); | 
| + if (width + char_width > available_width_) | 
| + break; | 
| + width += char_width; | 
| + en_pos++; | 
| + } | 
| + return width; | 
| + } | 
| + | 
| + // Gets the glyph width for |word|, and splits the |word| into different | 
| + // segments based on its runs. | 
| + SkScalar GetWordWidth(BreakList<size_t>::const_iterator word, | 
| + std::vector<internal::LineSegment>& segments) { | 
| 
Jun Mukai
2015/05/15 20:16:00
std::vector<internal::LineSegment>*
Also make this
 
xdai1
2015/05/15 23:41:17
Done.
 | 
| + if (word == words_->breaks().end()) | 
| + return 0; | 
| + int word_st = word->first; | 
| + int word_en = (word + 1 == words_->breaks().end()) ? text_.size() - 1 | 
| + : (word + 1)->first - 1; | 
| + if (word_en < 0 || word_en < word_st) | 
| + return 0; | 
| + size_t run_st_idx = run_list_.GetRunIndexAt(word_st); | 
| + size_t run_en_idx = run_list_.GetRunIndexAt(word_en); | 
| + SkScalar width = 0; | 
| + for (size_t idx = run_st_idx; idx <= run_en_idx; idx++) { | 
| + const internal::TextRunHarfBuzz& run = *(run_list_.runs()[idx]); | 
| + Range char_range = run.range.Intersect(Range(word_st, word_en + 1)); | 
| + Range glyph_range = run.CharRangeToGlyphRange(char_range); | 
| + SkScalar char_width = ((glyph_range.end() >= run.glyph_count) | 
| + ? SkFloatToScalar(run.width) | 
| + : run.positions[glyph_range.end()].x()) - | 
| + run.positions[glyph_range.start()].x(); | 
| + width += char_width; | 
| + | 
| + internal::LineSegment segment; | 
| + segment.run = idx; | 
| + segment.char_range = char_range; | 
| + segment.width = char_width; | 
| + segment.x_range = Range(SkScalarCeilToInt(text_x_ + width - char_width), | 
| + SkScalarCeilToInt(text_x_ + width)); | 
| + segments.push_back(segment); | 
| + } | 
| + return width; | 
| + } | 
| + | 
| + // Finishes line breaking algorithm. | 
| + void FinalizeLines() { | 
| + DCHECK(!lines_.empty()); | 
| + // Add an empty line to finish the line size calculation and remove it. | 
| + AdvanceLine(); | 
| + lines_.pop_back(); | 
| + } | 
| + | 
| + // RTL runs are broken in logical order but displayed in visual order. To find | 
| + // the text-space coordinate (where it would fall in a single-line text) | 
| + // |x_range| of RTL segments, segment widths are applied in reverse order. | 
| + // e.g. {[5, 10], [10, 40]} will become {[35, 40], [5, 35]}. | 
| + void UpdateRTLSegmentRanges() { | 
| + if (rtl_segments_.empty()) | 
| + return; | 
| + float x = SegmentFromHandle(rtl_segments_[0])->x_range.start(); | 
| + for (size_t i = rtl_segments_.size(); i > 0; --i) { | 
| + internal::LineSegment* segment = SegmentFromHandle(rtl_segments_[i - 1]); | 
| + const float segment_width = segment->width; | 
| + segment->x_range = Range(x, x + segment_width); | 
| + x += segment_width; | 
| + } | 
| + rtl_segments_.clear(); | 
| } | 
| const SkScalar max_width_; | 
| @@ -482,13 +539,14 @@ class HarfBuzzLineBreaker { | 
| // Stores the resulting lines. | 
| std::vector<internal::Line> lines_; | 
| - // Text space and line space x coordinates of the next segment to be added. | 
| - SkScalar text_x_; | 
| - SkScalar line_x_; | 
| - | 
| float max_descent_; | 
| float max_ascent_; | 
| + // Text space x coordinates of the next segment to be added. | 
| + SkScalar text_x_; | 
| + // Stores available width in the current line. | 
| + SkScalar available_width_; | 
| + | 
| // Size of the multiline text, not including the currently processed line. | 
| SizeF total_size_; | 
| @@ -660,6 +718,14 @@ void TextRunList::ComputePrecedingRunWidths() { | 
| } | 
| } | 
| +size_t TextRunList::GetRunIndexAt(size_t position) const { | 
| + for (size_t i = 0; i < runs_.size(); ++i) { | 
| + if (runs_[i]->range.start() <= position && runs_[i]->range.end() > position) | 
| + return i; | 
| + } | 
| + return runs_.size(); | 
| +} | 
| + | 
| } // namespace internal | 
| RenderTextHarfBuzz::RenderTextHarfBuzz() | 
| @@ -1014,11 +1080,9 @@ void RenderTextHarfBuzz::EnsureLayout() { | 
| tracking_profile.reset(); | 
| - for (size_t i = 0; i < run_list->size(); ++i) | 
| - line_breaker.AddRun(i); | 
| - std::vector<internal::Line> lines; | 
| - line_breaker.Finalize(&lines, &total_size_); | 
| + std::vector<internal::Line> lines = line_breaker.GetLines(); | 
| set_lines(&lines); | 
| + total_size_ = line_breaker.GetTotalSize(); | 
| } | 
| } |