| 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" | 
| 11 #include "base/i18n/break_iterator.h" | 11 #include "base/i18n/break_iterator.h" | 
| 12 #include "base/i18n/char_iterator.h" | 12 #include "base/i18n/char_iterator.h" | 
| 13 #include "base/profiler/scoped_tracker.h" | 13 #include "base/profiler/scoped_tracker.h" | 
| 14 #include "base/strings/string_util.h" | 14 #include "base/strings/string_util.h" | 
| 15 #include "base/strings/utf_string_conversions.h" | 15 #include "base/strings/utf_string_conversions.h" | 
| 16 #include "base/trace_event/trace_event.h" | 16 #include "base/trace_event/trace_event.h" | 
| 17 #include "third_party/harfbuzz-ng/src/hb.h" | 17 #include "third_party/harfbuzz-ng/src/hb.h" | 
| 18 #include "third_party/icu/source/common/unicode/ubidi.h" | 18 #include "third_party/icu/source/common/unicode/ubidi.h" | 
|  | 19 #include "third_party/icu/source/common/unicode/utf16.h" | 
| 19 #include "third_party/skia/include/core/SkColor.h" | 20 #include "third_party/skia/include/core/SkColor.h" | 
| 20 #include "third_party/skia/include/core/SkTypeface.h" | 21 #include "third_party/skia/include/core/SkTypeface.h" | 
| 21 #include "ui/gfx/canvas.h" | 22 #include "ui/gfx/canvas.h" | 
| 22 #include "ui/gfx/font_fallback.h" | 23 #include "ui/gfx/font_fallback.h" | 
| 23 #include "ui/gfx/font_render_params.h" | 24 #include "ui/gfx/font_render_params.h" | 
| 24 #include "ui/gfx/geometry/safe_integer_conversions.h" | 25 #include "ui/gfx/geometry/safe_integer_conversions.h" | 
| 25 #include "ui/gfx/harfbuzz_font_skia.h" | 26 #include "ui/gfx/harfbuzz_font_skia.h" | 
| 26 #include "ui/gfx/range/range_f.h" | 27 #include "ui/gfx/range/range_f.h" | 
|  | 28 #include "ui/gfx/text_utils.h" | 
| 27 #include "ui/gfx/utf16_indexing.h" | 29 #include "ui/gfx/utf16_indexing.h" | 
| 28 | 30 | 
| 29 #if defined(OS_WIN) | 31 #if defined(OS_WIN) | 
| 30 #include "ui/gfx/font_fallback_win.h" | 32 #include "ui/gfx/font_fallback_win.h" | 
| 31 #endif | 33 #endif | 
| 32 | 34 | 
| 33 using gfx::internal::RoundRangeF; | 35 using gfx::internal::RoundRangeF; | 
| 34 | 36 | 
| 35 namespace gfx { | 37 namespace gfx { | 
| 36 | 38 | 
| 37 namespace { | 39 namespace { | 
| 38 | 40 | 
| 39 // Text length limit. Longer strings are slow and not fully tested. | 41 // Text length limit. Longer strings are slow and not fully tested. | 
| 40 const size_t kMaxTextLength = 10000; | 42 const size_t kMaxTextLength = 10000; | 
| 41 | 43 | 
| 42 // The maximum number of scripts a Unicode character can belong to. This value | 44 // The maximum number of scripts a Unicode character can belong to. This value | 
| 43 // is arbitrarily chosen to be a good limit because it is unlikely for a single | 45 // is arbitrarily chosen to be a good limit because it is unlikely for a single | 
| 44 // character to belong to more scripts. | 46 // character to belong to more scripts. | 
| 45 const size_t kMaxScripts = 5; | 47 const size_t kMaxScripts = 5; | 
| 46 | 48 | 
| 47 // Returns true if characters of |block_code| may trigger font fallback. | 49 // Returns true if characters of |block_code| may trigger font fallback. | 
| 48 // Dingbats and emoticons can be rendered through the color emoji font file, | 50 // Dingbats and emoticons can be rendered through the color emoji font file, | 
| 49 // therefore it needs to be trigerred as fallbacks. See crbug.com/448909 | 51 // therefore it needs to be trigerred as fallbacks. See crbug.com/448909 | 
| 50 bool IsUnusualBlockCode(UBlockCode block_code) { | 52 bool IsUnusualBlockCode(UBlockCode block_code) { | 
| 51   return block_code == UBLOCK_GEOMETRIC_SHAPES || | 53   return block_code == UBLOCK_GEOMETRIC_SHAPES || | 
| 52          block_code == UBLOCK_MISCELLANEOUS_SYMBOLS || | 54          block_code == UBLOCK_MISCELLANEOUS_SYMBOLS; | 
| 53          block_code == UBLOCK_DINGBATS || |  | 
| 54          block_code == UBLOCK_EMOTICONS; |  | 
| 55 } | 55 } | 
| 56 | 56 | 
| 57 bool IsBracket(UChar32 character) { | 57 bool IsBracket(UChar32 character) { | 
| 58   static const char kBrackets[] = { '(', ')', '{', '}', '<', '>', }; | 58   static const char kBrackets[] = { '(', ')', '{', '}', '<', '>', }; | 
| 59   static const char* kBracketsEnd = kBrackets + arraysize(kBrackets); | 59   static const char* kBracketsEnd = kBrackets + arraysize(kBrackets); | 
| 60   return std::find(kBrackets, kBracketsEnd, character) != kBracketsEnd; | 60   return std::find(kBrackets, kBracketsEnd, character) != kBracketsEnd; | 
| 61 } | 61 } | 
| 62 | 62 | 
| 63 // Returns the boundary between a special and a regular character. Special | 63 // Returns the boundary between a special and a regular character. Special | 
| 64 // characters are brackets or characters that satisfy |IsUnusualBlockCode|. | 64 // characters are brackets or characters that satisfy |IsUnusualBlockCode|. | 
| (...skipping 18 matching lines...) Expand all  Loading... | 
| 83     const bool block_break = current_block != first_block && | 83     const bool block_break = current_block != first_block && | 
| 84         (first_block_unusual || IsUnusualBlockCode(current_block)); | 84         (first_block_unusual || IsUnusualBlockCode(current_block)); | 
| 85     if (block_break || current_char == '\n' || | 85     if (block_break || current_char == '\n' || | 
| 86         first_bracket != IsBracket(current_char)) { | 86         first_bracket != IsBracket(current_char)) { | 
| 87       return run_start + iter.array_pos(); | 87       return run_start + iter.array_pos(); | 
| 88     } | 88     } | 
| 89   } | 89   } | 
| 90   return run_break; | 90   return run_break; | 
| 91 } | 91 } | 
| 92 | 92 | 
| 93 // If the given scripts match, returns the one that isn't USCRIPT_COMMON or | 93 // If the given scripts match, returns the one that isn't USCRIPT_INHERITED, | 
| 94 // USCRIPT_INHERITED, i.e. the more specific one. Otherwise returns | 94 // i.e. the more specific one. Otherwise returns USCRIPT_INVALID_CODE. | 
| 95 // USCRIPT_INVALID_CODE. |  | 
| 96 UScriptCode ScriptIntersect(UScriptCode first, UScriptCode second) { | 95 UScriptCode ScriptIntersect(UScriptCode first, UScriptCode second) { | 
| 97   if (first == second || | 96   if (first == second || second == USCRIPT_INHERITED) | 
| 98       (second > USCRIPT_INVALID_CODE && second <= USCRIPT_INHERITED)) { |  | 
| 99     return first; | 97     return first; | 
| 100   } | 98   if (first == USCRIPT_INHERITED) | 
| 101   if (first > USCRIPT_INVALID_CODE && first <= USCRIPT_INHERITED) |  | 
| 102     return second; | 99     return second; | 
| 103   return USCRIPT_INVALID_CODE; | 100   return USCRIPT_INVALID_CODE; | 
| 104 } | 101 } | 
| 105 | 102 | 
| 106 // Writes the script and the script extensions of the character with the | 103 // Writes the script and the script extensions of the character with the | 
| 107 // Unicode |codepoint|. Returns the number of written scripts. | 104 // Unicode |codepoint|. Returns the number of written scripts. | 
| 108 int GetScriptExtensions(UChar32 codepoint, UScriptCode* scripts) { | 105 int GetScriptExtensions(UChar32 codepoint, UScriptCode* scripts) { | 
| 109   UErrorCode icu_error = U_ZERO_ERROR; | 106   UErrorCode icu_error = U_ZERO_ERROR; | 
| 110   // ICU documentation incorrectly states that the result of | 107   // ICU documentation incorrectly states that the result of | 
| 111   // |uscript_getScriptExtensions| will contain the regular script property. | 108   // |uscript_getScriptExtensions| will contain the regular script property. | 
| (...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 159                    UScriptCode* script) { | 156                    UScriptCode* script) { | 
| 160   DCHECK_GT(length, 0U); | 157   DCHECK_GT(length, 0U); | 
| 161 | 158 | 
| 162   UScriptCode scripts[kMaxScripts] = { USCRIPT_INVALID_CODE }; | 159   UScriptCode scripts[kMaxScripts] = { USCRIPT_INVALID_CODE }; | 
| 163 | 160 | 
| 164   base::i18n::UTF16CharIterator char_iterator(text.c_str() + start, length); | 161   base::i18n::UTF16CharIterator char_iterator(text.c_str() + start, length); | 
| 165   size_t scripts_size = GetScriptExtensions(char_iterator.get(), scripts); | 162   size_t scripts_size = GetScriptExtensions(char_iterator.get(), scripts); | 
| 166   *script = scripts[0]; | 163   *script = scripts[0]; | 
| 167 | 164 | 
| 168   while (char_iterator.Advance()) { | 165   while (char_iterator.Advance()) { | 
|  | 166     // Special handling to merge white space into the previous run. | 
|  | 167     if (u_isUWhiteSpace(char_iterator.get())) | 
|  | 168       continue; | 
| 169     ScriptSetIntersect(char_iterator.get(), scripts, &scripts_size); | 169     ScriptSetIntersect(char_iterator.get(), scripts, &scripts_size); | 
| 170     if (scripts_size == 0U) | 170     if (scripts_size == 0U) | 
| 171       return char_iterator.array_pos(); | 171       return char_iterator.array_pos(); | 
| 172     *script = scripts[0]; | 172     *script = scripts[0]; | 
| 173   } | 173   } | 
| 174 | 174 | 
| 175   return length; | 175   return length; | 
| 176 } | 176 } | 
| 177 | 177 | 
| 178 // A port of hb_icu_script_to_script because harfbuzz on CrOS is built without | 178 // A port of hb_icu_script_to_script because harfbuzz on CrOS is built without | 
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 216 // than |max_width|. If |multiline| is false, only outputs a single Line from | 216 // than |max_width|. If |multiline| is false, only outputs a single Line from | 
| 217 // the given runs. |min_baseline| and |min_height| are the minimum baseline and | 217 // the given runs. |min_baseline| and |min_height| are the minimum baseline and | 
| 218 // height for each line. | 218 // height for each line. | 
| 219 // TODO(ckocagil): Expose the interface of this class in the header and test | 219 // TODO(ckocagil): Expose the interface of this class in the header and test | 
| 220 //                 this class directly. | 220 //                 this class directly. | 
| 221 class HarfBuzzLineBreaker { | 221 class HarfBuzzLineBreaker { | 
| 222  public: | 222  public: | 
| 223   HarfBuzzLineBreaker(size_t max_width, | 223   HarfBuzzLineBreaker(size_t max_width, | 
| 224                       int min_baseline, | 224                       int min_baseline, | 
| 225                       float min_height, | 225                       float min_height, | 
| 226                       bool multiline, |  | 
| 227                       WordWrapBehavior word_wrap_behavior, | 226                       WordWrapBehavior word_wrap_behavior, | 
| 228                       const base::string16& text, | 227                       const base::string16& text, | 
| 229                       const BreakList<size_t>* words, | 228                       const BreakList<size_t>* words, | 
| 230                       const internal::TextRunList& run_list) | 229                       const internal::TextRunList& run_list) | 
| 231       : max_width_((max_width == 0) ? SK_ScalarMax : SkIntToScalar(max_width)), | 230       : max_width_((max_width == 0) ? SK_ScalarMax : SkIntToScalar(max_width)), | 
| 232         min_baseline_(min_baseline), | 231         min_baseline_(min_baseline), | 
| 233         min_height_(min_height), | 232         min_height_(min_height), | 
| 234         multiline_(multiline), |  | 
| 235         word_wrap_behavior_(word_wrap_behavior), | 233         word_wrap_behavior_(word_wrap_behavior), | 
| 236         text_(text), | 234         text_(text), | 
| 237         words_(words), | 235         words_(words), | 
| 238         run_list_(run_list), | 236         run_list_(run_list), | 
|  | 237         max_descent_(0), | 
|  | 238         max_ascent_(0), | 
| 239         text_x_(0), | 239         text_x_(0), | 
| 240         line_x_(0), | 240         available_width_(max_width_) { | 
| 241         max_descent_(0), |  | 
| 242         max_ascent_(0) { |  | 
| 243     DCHECK_EQ(multiline_, (words_ != nullptr)); |  | 
| 244     AdvanceLine(); | 241     AdvanceLine(); | 
| 245   } | 242   } | 
| 246 | 243 | 
| 247   // Breaks the run at given |run_index| into Line structs. | 244   // Constructs a single line for |text_| using |run_list_|. | 
| 248   void AddRun(int run_index) { | 245   void ConstructSingleLine() { | 
| 249     const internal::TextRunHarfBuzz* run = run_list_.runs()[run_index]; | 246     for (size_t i = 0; i < run_list_.size(); i++) { | 
| 250     base::char16 first_char = text_[run->range.start()]; | 247       const internal::TextRunHarfBuzz& run = *(run_list_.runs()[i]); | 
| 251     if (multiline_ && first_char == '\n') { | 248       internal::LineSegment segment; | 
| 252       AdvanceLine(); | 249       segment.run = i; | 
| 253     } else if (multiline_ && (line_x_ + SkFloatToScalar(run->width)) > | 250       segment.char_range = run.range; | 
| 254                max_width_) { | 251       segment.x_range = Range(SkScalarCeilToInt(text_x_), | 
| 255       BreakRun(run_index); | 252                               SkScalarCeilToInt(text_x_ + run.width)); | 
| 256     } else { | 253       segment.width = run.width; | 
| 257       AddSegment(run_index, run->range, run->width); | 254       AddLineSegment(segment); | 
|  | 255     } | 
|  | 256   } | 
|  | 257 | 
|  | 258   // Constructs multiple lines for |text_| based on words iteration approach. | 
|  | 259   void ConstructMultiLines() { | 
|  | 260     DCHECK(words_); | 
|  | 261     for (auto iter = words_->breaks().begin(); iter != words_->breaks().end(); | 
|  | 262          iter++) { | 
|  | 263       const Range word_range = words_->GetRange(iter); | 
|  | 264       std::vector<internal::LineSegment> word_segments; | 
|  | 265       SkScalar word_width = GetWordWidth(word_range, &word_segments); | 
|  | 266 | 
|  | 267       // If the last word is '\n', we should advance a new line after adding | 
|  | 268       // the word to the current line. | 
|  | 269       bool new_line = false; | 
|  | 270       if (!word_segments.empty() && | 
|  | 271           text_[word_segments.back().char_range.start()] == '\n') { | 
|  | 272         new_line = true; | 
|  | 273         word_width -= word_segments.back().width; | 
|  | 274         word_segments.pop_back(); | 
|  | 275       } | 
|  | 276 | 
|  | 277       // If the word is not the first word in the line and it can't fit into | 
|  | 278       // the current line, advance a new line. | 
|  | 279       if (word_width > available_width_ && available_width_ != max_width_) | 
|  | 280         AdvanceLine(); | 
|  | 281       if (!word_segments.empty()) | 
|  | 282         AddWordToLine(word_segments); | 
|  | 283       if (new_line) | 
|  | 284         AdvanceLine(); | 
| 258     } | 285     } | 
| 259   } | 286   } | 
| 260 | 287 | 
| 261   // Finishes line breaking and outputs the results. Can be called at most once. | 288   // Finishes line breaking and outputs the results. Can be called at most once. | 
| 262   void Finalize(std::vector<internal::Line>* lines, SizeF* size) { | 289   void FinalizeLines(std::vector<internal::Line>* lines, SizeF* size) { | 
| 263     DCHECK(!lines_.empty()); | 290     DCHECK(!lines_.empty()); | 
| 264     // Add an empty line to finish the line size calculation and remove it. | 291     // Add an empty line to finish the line size calculation and remove it. | 
| 265     AdvanceLine(); | 292     AdvanceLine(); | 
| 266     lines_.pop_back(); | 293     lines_.pop_back(); | 
| 267     *size = total_size_; | 294     *size = total_size_; | 
| 268     lines->swap(lines_); | 295     lines->swap(lines_); | 
| 269   } | 296   } | 
| 270 | 297 | 
| 271  private: | 298  private: | 
| 272   // A (line index, segment index) pair that specifies a segment in |lines_|. | 299   // A (line index, segment index) pair that specifies a segment in |lines_|. | 
| 273   typedef std::pair<size_t, size_t> SegmentHandle; | 300   typedef std::pair<size_t, size_t> SegmentHandle; | 
| 274 | 301 | 
| 275   internal::LineSegment* SegmentFromHandle(const SegmentHandle& handle) { | 302   internal::LineSegment* SegmentFromHandle(const SegmentHandle& handle) { | 
| 276     return &lines_[handle.first].segments[handle.second]; | 303     return &lines_[handle.first].segments[handle.second]; | 
| 277   } | 304   } | 
| 278 | 305 | 
| 279   // Breaks a run into segments that fit in the last line in |lines_| and adds |  | 
| 280   // them. Adds a new Line to the back of |lines_| whenever a new segment can't |  | 
| 281   // be added without the Line's width exceeding |max_width_|. |  | 
| 282   void BreakRun(int run_index) { |  | 
| 283     const internal::TextRunHarfBuzz& run = *(run_list_.runs()[run_index]); |  | 
| 284     SkScalar width = 0; |  | 
| 285     size_t next_char = run.range.start(); |  | 
| 286 |  | 
| 287     // Break the run until it fits the current line. |  | 
| 288     while (next_char < run.range.end()) { |  | 
| 289       const size_t current_char = next_char; |  | 
| 290       size_t end_char = next_char; |  | 
| 291       const bool skip_line = |  | 
| 292           BreakRunAtWidth(run, current_char, &width, &end_char, &next_char); |  | 
| 293       AddSegment(run_index, Range(current_char, end_char), |  | 
| 294                  SkScalarToFloat(width)); |  | 
| 295       if (skip_line) |  | 
| 296         AdvanceLine(); |  | 
| 297     } |  | 
| 298   } |  | 
| 299 |  | 
| 300   // Starting from |start_char|, finds a suitable line break position at or |  | 
| 301   // before available width using word break. If the current position is at the |  | 
| 302   // beginning of a line, this function will not roll back to |start_char| and |  | 
| 303   // |*next_char| will be greater than |start_char| (to avoid constructing empty |  | 
| 304   // lines). It stores the end of the segment range to |end_char|, which can be |  | 
| 305   // smaller than |*next_char| for certain word wrapping behavior. |  | 
| 306   // Returns whether to skip the line before |*next_char|. |  | 
| 307   // TODO(ckocagil): We might have to reshape after breaking at ligatures. |  | 
| 308   //                 See whether resolving the TODO above resolves this too. |  | 
| 309   // TODO(ckocagil): Do not reserve width for whitespace at the end of lines. |  | 
| 310   bool BreakRunAtWidth(const internal::TextRunHarfBuzz& run, |  | 
| 311                        size_t start_char, |  | 
| 312                        SkScalar* width, |  | 
| 313                        size_t* end_char, |  | 
| 314                        size_t* next_char) { |  | 
| 315     DCHECK(words_); |  | 
| 316     DCHECK(run.range.Contains(Range(start_char, start_char + 1))); |  | 
| 317     SkScalar available_width = max_width_ - line_x_; |  | 
| 318     BreakList<size_t>::const_iterator word = words_->GetBreak(start_char); |  | 
| 319     BreakList<size_t>::const_iterator next_word = word + 1; |  | 
| 320     // Width from |std::max(word->first, start_char)| to the current character. |  | 
| 321     SkScalar word_width = 0; |  | 
| 322     *width = 0; |  | 
| 323 |  | 
| 324     Range char_range; |  | 
| 325     SkScalar truncated_width = 0; |  | 
| 326     for (size_t i = start_char; i < run.range.end(); i += char_range.length()) { |  | 
| 327       // |word| holds the word boundary at or before |i|, and |next_word| holds |  | 
| 328       // the word boundary right after |i|. Advance both |word| and |next_word| |  | 
| 329       // when |i| reaches |next_word|. |  | 
| 330       if (next_word != words_->breaks().end() && i >= next_word->first) { |  | 
| 331         if (*width > available_width) { |  | 
| 332           DCHECK_NE(WRAP_LONG_WORDS, word_wrap_behavior_); |  | 
| 333           *next_char = i; |  | 
| 334           if (word_wrap_behavior_ != TRUNCATE_LONG_WORDS) |  | 
| 335             *end_char = *next_char; |  | 
| 336           else |  | 
| 337             *width = truncated_width; |  | 
| 338           return true; |  | 
| 339         } |  | 
| 340         word = next_word++; |  | 
| 341         word_width = 0; |  | 
| 342       } |  | 
| 343 |  | 
| 344       Range glyph_range; |  | 
| 345       run.GetClusterAt(i, &char_range, &glyph_range); |  | 
| 346       DCHECK_LT(0U, char_range.length()); |  | 
| 347 |  | 
| 348       SkScalar char_width = ((glyph_range.end() >= run.glyph_count) |  | 
| 349                                  ? SkFloatToScalar(run.width) |  | 
| 350                                  : run.positions[glyph_range.end()].x()) - |  | 
| 351                             run.positions[glyph_range.start()].x(); |  | 
| 352 |  | 
| 353       *width += char_width; |  | 
| 354       word_width += char_width; |  | 
| 355 |  | 
| 356       // TODO(mukai): implement ELIDE_LONG_WORDS. |  | 
| 357       if (*width > available_width) { |  | 
| 358         if (line_x_ != 0 || word_width < *width) { |  | 
| 359           // Roll back one word. |  | 
| 360           *width -= word_width; |  | 
| 361           *next_char = std::max(word->first, start_char); |  | 
| 362           *end_char = *next_char; |  | 
| 363           return true; |  | 
| 364         } else if (word_wrap_behavior_ == WRAP_LONG_WORDS) { |  | 
| 365           if (char_width < *width) { |  | 
| 366             // Roll back one character. |  | 
| 367             *width -= char_width; |  | 
| 368             *next_char = i; |  | 
| 369           } else { |  | 
| 370             // Continue from the next character. |  | 
| 371             *next_char = i + char_range.length(); |  | 
| 372           } |  | 
| 373           *end_char = *next_char; |  | 
| 374           return true; |  | 
| 375         } |  | 
| 376       } else { |  | 
| 377         *end_char = char_range.end(); |  | 
| 378         truncated_width = *width; |  | 
| 379       } |  | 
| 380     } |  | 
| 381 |  | 
| 382     if (word_wrap_behavior_ == TRUNCATE_LONG_WORDS) |  | 
| 383       *width = truncated_width; |  | 
| 384     *end_char = *next_char = run.range.end(); |  | 
| 385     return false; |  | 
| 386   } |  | 
| 387 |  | 
| 388   // RTL runs are broken in logical order but displayed in visual order. To find |  | 
| 389   // the text-space coordinate (where it would fall in a single-line text) |  | 
| 390   // |x_range| of RTL segments, segment widths are applied in reverse order. |  | 
| 391   // e.g. {[5, 10], [10, 40]} will become {[35, 40], [5, 35]}. |  | 
| 392   void UpdateRTLSegmentRanges() { |  | 
| 393     if (rtl_segments_.empty()) |  | 
| 394       return; |  | 
| 395     float x = SegmentFromHandle(rtl_segments_[0])->x_range.start(); |  | 
| 396     for (size_t i = rtl_segments_.size(); i > 0; --i) { |  | 
| 397       internal::LineSegment* segment = SegmentFromHandle(rtl_segments_[i - 1]); |  | 
| 398       const float segment_width = segment->width; |  | 
| 399       segment->x_range = Range(x, x + segment_width); |  | 
| 400       x += segment_width; |  | 
| 401     } |  | 
| 402     rtl_segments_.clear(); |  | 
| 403   } |  | 
| 404 |  | 
| 405   // Finishes the size calculations of the last Line in |lines_|. Adds a new | 306   // Finishes the size calculations of the last Line in |lines_|. Adds a new | 
| 406   // Line to the back of |lines_|. | 307   // Line to the back of |lines_|. | 
| 407   void AdvanceLine() { | 308   void AdvanceLine() { | 
| 408     if (!lines_.empty()) { | 309     if (!lines_.empty()) { | 
| 409       internal::Line* line = &lines_.back(); | 310       internal::Line* line = &lines_.back(); | 
| 410       std::sort(line->segments.begin(), line->segments.end(), | 311       std::sort(line->segments.begin(), line->segments.end(), | 
| 411                 [this](const internal::LineSegment& s1, | 312                 [this](const internal::LineSegment& s1, | 
| 412                        const internal::LineSegment& s2) -> bool { | 313                        const internal::LineSegment& s2) -> bool { | 
| 413                   return run_list_.logical_to_visual(s1.run) < | 314                   return run_list_.logical_to_visual(s1.run) < | 
| 414                          run_list_.logical_to_visual(s2.run); | 315                          run_list_.logical_to_visual(s2.run); | 
| 415                 }); | 316                 }); | 
| 416       line->size.set_height(std::max(min_height_, max_descent_ + max_ascent_)); | 317       line->size.set_height(std::max(min_height_, max_descent_ + max_ascent_)); | 
| 417       line->baseline = | 318       line->baseline = std::max(min_baseline_, SkScalarRoundToInt(max_ascent_)); | 
| 418           std::max(min_baseline_, SkScalarRoundToInt(max_ascent_)); |  | 
| 419       line->preceding_heights = std::ceil(total_size_.height()); | 319       line->preceding_heights = std::ceil(total_size_.height()); | 
| 420       total_size_.set_height(total_size_.height() + line->size.height()); | 320       total_size_.set_height(total_size_.height() + line->size.height()); | 
| 421       total_size_.set_width(std::max(total_size_.width(), line->size.width())); | 321       total_size_.set_width(std::max(total_size_.width(), line->size.width())); | 
| 422     } | 322     } | 
| 423     max_descent_ = 0; | 323     max_descent_ = 0; | 
| 424     max_ascent_ = 0; | 324     max_ascent_ = 0; | 
| 425     line_x_ = 0; | 325     available_width_ = max_width_; | 
| 426     lines_.push_back(internal::Line()); | 326     lines_.push_back(internal::Line()); | 
| 427   } | 327   } | 
| 428 | 328 | 
| 429   // Adds a new segment with the given properties to |lines_.back()|. | 329   // Adds word to the current line. A word may contain multiple segments. If the | 
| 430   void AddSegment(int run_index, Range char_range, float width) { | 330   // word is the first word in line and its width exceeds |available_width_|, | 
| 431     if (char_range.is_empty()) { | 331   // ignore/truncate/wrap it according to |word_wrap_behavior_|. | 
| 432       DCHECK_EQ(0, width); | 332   void AddWordToLine(const std::vector<internal::LineSegment>& word_segments) { | 
| 433       return; | 333     DCHECK(!lines_.empty()); | 
|  | 334     DCHECK(!word_segments.empty()); | 
|  | 335 | 
|  | 336     bool has_truncated = false; | 
|  | 337     for (const internal::LineSegment& segment : word_segments) { | 
|  | 338       if (has_truncated) | 
|  | 339         break; | 
|  | 340       if (segment.width <= available_width_ || | 
|  | 341           word_wrap_behavior_ == IGNORE_LONG_WORDS) { | 
|  | 342         AddLineSegment(segment); | 
|  | 343       } else { | 
|  | 344         DCHECK(word_wrap_behavior_ == TRUNCATE_LONG_WORDS || | 
|  | 345                word_wrap_behavior_ == WRAP_LONG_WORDS); | 
|  | 346         has_truncated = (word_wrap_behavior_ == TRUNCATE_LONG_WORDS); | 
|  | 347 | 
|  | 348         const internal::TextRunHarfBuzz& run = *(run_list_.runs()[segment.run]); | 
|  | 349         internal::LineSegment remaining_segment = segment; | 
|  | 350         while (!remaining_segment.char_range.is_empty()) { | 
|  | 351           size_t cutoff_pos = GetCutoffPos(remaining_segment); | 
|  | 352           SkScalar width = run.GetGlyphWidthForCharRange( | 
|  | 353               Range(remaining_segment.char_range.start(), cutoff_pos)); | 
|  | 354           if (width > 0) { | 
|  | 355             internal::LineSegment cut_segment; | 
|  | 356             cut_segment.run = remaining_segment.run; | 
|  | 357             cut_segment.char_range = | 
|  | 358                 Range(remaining_segment.char_range.start(), cutoff_pos); | 
|  | 359             cut_segment.width = width; | 
|  | 360             cut_segment.x_range = Range(remaining_segment.x_range.start(), | 
|  | 361                                         SkScalarCeilToInt(text_x_ + width)); | 
|  | 362             AddLineSegment(cut_segment); | 
|  | 363             // Updates old segment range. | 
|  | 364             remaining_segment.char_range.set_start(cutoff_pos); | 
|  | 365             remaining_segment.x_range.set_start(SkScalarCeilToInt(text_x_)); | 
|  | 366             remaining_segment.width -= width; | 
|  | 367           } | 
|  | 368           if (has_truncated) | 
|  | 369             break; | 
|  | 370           if (!remaining_segment.char_range.is_empty()) | 
|  | 371             AdvanceLine(); | 
|  | 372         } | 
|  | 373       } | 
| 434     } | 374     } | 
| 435     const internal::TextRunHarfBuzz& run = *(run_list_.runs()[run_index]); | 375   } | 
| 436 | 376 | 
| 437     internal::LineSegment segment; | 377   // Add a line segment to the current line. Note that, in order to keep the | 
| 438     segment.run = run_index; | 378   // visual order correct for ltr and rtl language, we need to merge segments | 
| 439     segment.char_range = char_range; | 379   // that belong to the same run. | 
| 440     segment.x_range = Range( | 380   void AddLineSegment(const internal::LineSegment& segment) { | 
| 441         SkScalarCeilToInt(text_x_), | 381     DCHECK(!lines_.empty()); | 
| 442         SkScalarCeilToInt(text_x_ + SkFloatToScalar(width))); |  | 
| 443     segment.width = width; |  | 
| 444 |  | 
| 445     internal::Line* line = &lines_.back(); | 382     internal::Line* line = &lines_.back(); | 
|  | 383     const internal::TextRunHarfBuzz& run = *(run_list_.runs()[segment.run]); | 
|  | 384     if (!line->segments.empty()) { | 
|  | 385       internal::LineSegment& last_segment = line->segments.back(); | 
|  | 386       // Merge segments that belong to the same run. | 
|  | 387       if (last_segment.run == segment.run) { | 
|  | 388         DCHECK_EQ(last_segment.char_range.end(), segment.char_range.start()); | 
|  | 389         DCHECK_EQ(last_segment.x_range.end(), segment.x_range.start()); | 
|  | 390         last_segment.char_range.set_end(segment.char_range.end()); | 
|  | 391         last_segment.width += segment.width; | 
|  | 392         last_segment.x_range.set_end( | 
|  | 393             SkScalarCeilToInt(text_x_ + segment.width)); | 
|  | 394         if (run.is_rtl && last_segment.char_range.end() == run.range.end()) | 
|  | 395           UpdateRTLSegmentRanges(); | 
|  | 396         line->size.set_width(line->size.width() + segment.width); | 
|  | 397         text_x_ += segment.width; | 
|  | 398         available_width_ -= segment.width; | 
|  | 399         return; | 
|  | 400       } | 
|  | 401     } | 
| 446     line->segments.push_back(segment); | 402     line->segments.push_back(segment); | 
| 447 | 403 | 
| 448     SkPaint paint; | 404     SkPaint paint; | 
| 449     paint.setTypeface(run.skia_face.get()); | 405     paint.setTypeface(run.skia_face.get()); | 
| 450     paint.setTextSize(SkIntToScalar(run.font_size)); | 406     paint.setTextSize(SkIntToScalar(run.font_size)); | 
| 451     paint.setAntiAlias(run.render_params.antialiasing); | 407     paint.setAntiAlias(run.render_params.antialiasing); | 
| 452     SkPaint::FontMetrics metrics; | 408     SkPaint::FontMetrics metrics; | 
| 453     paint.getFontMetrics(&metrics); | 409     paint.getFontMetrics(&metrics); | 
| 454 | 410 | 
| 455     line->size.set_width(line->size.width() + width); | 411     line->size.set_width(line->size.width() + segment.width); | 
| 456     // TODO(dschuyler): Account for stylized baselines in string sizing. | 412     // TODO(dschuyler): Account for stylized baselines in string sizing. | 
| 457     max_descent_ = std::max(max_descent_, metrics.fDescent); | 413     max_descent_ = std::max(max_descent_, metrics.fDescent); | 
| 458     // fAscent is always negative. | 414     // fAscent is always negative. | 
| 459     max_ascent_ = std::max(max_ascent_, -metrics.fAscent); | 415     max_ascent_ = std::max(max_ascent_, -metrics.fAscent); | 
| 460 | 416 | 
| 461     if (run.is_rtl) { | 417     if (run.is_rtl) { | 
| 462       rtl_segments_.push_back( | 418       rtl_segments_.push_back( | 
| 463           SegmentHandle(lines_.size() - 1, line->segments.size() - 1)); | 419           SegmentHandle(lines_.size() - 1, line->segments.size() - 1)); | 
| 464       // If this is the last segment of an RTL run, reprocess the text-space x | 420       // If this is the last segment of an RTL run, reprocess the text-space x | 
| 465       // ranges of all segments from the run. | 421       // ranges of all segments from the run. | 
| 466       if (char_range.end() == run.range.end()) | 422       if (segment.char_range.end() == run.range.end()) | 
| 467         UpdateRTLSegmentRanges(); | 423         UpdateRTLSegmentRanges(); | 
| 468     } | 424     } | 
| 469     text_x_ += SkFloatToScalar(width); | 425     text_x_ += segment.width; | 
| 470     line_x_ += SkFloatToScalar(width); | 426     available_width_ -= segment.width; | 
|  | 427   } | 
|  | 428 | 
|  | 429   // Finds the end position |end_pos| in |segment| where the preceding width is | 
|  | 430   // no larger than |available_width_|. | 
|  | 431   size_t GetCutoffPos(const internal::LineSegment& segment) const { | 
|  | 432     DCHECK(!segment.char_range.is_empty()); | 
|  | 433     const internal::TextRunHarfBuzz& run = *(run_list_.runs()[segment.run]); | 
|  | 434     size_t end_pos = segment.char_range.start(); | 
|  | 435     SkScalar width = 0; | 
|  | 436     while (end_pos < segment.char_range.end()) { | 
|  | 437       const SkScalar char_width = | 
|  | 438           run.GetGlyphWidthForCharRange(Range(end_pos, end_pos + 1)); | 
|  | 439       if (width + char_width > available_width_) | 
|  | 440         break; | 
|  | 441       width += char_width; | 
|  | 442       end_pos++; | 
|  | 443     } | 
|  | 444 | 
|  | 445     const size_t valid_end_pos = FindValidBoundaryBefore(text_, end_pos); | 
|  | 446     if (end_pos != valid_end_pos) { | 
|  | 447       end_pos = valid_end_pos; | 
|  | 448       width = run.GetGlyphWidthForCharRange( | 
|  | 449           Range(segment.char_range.start(), end_pos)); | 
|  | 450     } | 
|  | 451 | 
|  | 452     // |max_width_| might be smaller than a single character. In this case we | 
|  | 453     // need to put at least one character in the line. Note that, we should | 
|  | 454     // not separate surrogate pair or combining characters. | 
|  | 455     // See RenderTextTest.Multiline_MinWidth for an example. | 
|  | 456     if (width == 0 && available_width_ == max_width_) | 
|  | 457       end_pos = FindValidBoundaryAfter(text_, end_pos + 1); | 
|  | 458 | 
|  | 459     return end_pos; | 
|  | 460   } | 
|  | 461 | 
|  | 462   // Gets the glyph width for |word_range|, and splits the |word| into different | 
|  | 463   // segments based on its runs. | 
|  | 464   SkScalar GetWordWidth(const Range& word_range, | 
|  | 465                         std::vector<internal::LineSegment>* segments) const { | 
|  | 466     DCHECK(words_); | 
|  | 467     if (word_range.is_empty() || segments == nullptr) | 
|  | 468       return 0; | 
|  | 469     size_t run_start_index = run_list_.GetRunIndexAt(word_range.start()); | 
|  | 470     size_t run_end_index = run_list_.GetRunIndexAt(word_range.end() - 1); | 
|  | 471     SkScalar width = 0; | 
|  | 472     for (size_t i = run_start_index; i <= run_end_index; i++) { | 
|  | 473       const internal::TextRunHarfBuzz& run = *(run_list_.runs()[i]); | 
|  | 474       const Range char_range = run.range.Intersect(word_range); | 
|  | 475       DCHECK(!char_range.is_empty()); | 
|  | 476       const SkScalar char_width = run.GetGlyphWidthForCharRange(char_range); | 
|  | 477       width += char_width; | 
|  | 478 | 
|  | 479       internal::LineSegment segment; | 
|  | 480       segment.run = i; | 
|  | 481       segment.char_range = char_range; | 
|  | 482       segment.width = char_width; | 
|  | 483       segment.x_range = Range(SkScalarCeilToInt(text_x_ + width - char_width), | 
|  | 484                               SkScalarCeilToInt(text_x_ + width)); | 
|  | 485       segments->push_back(segment); | 
|  | 486     } | 
|  | 487     return width; | 
|  | 488   } | 
|  | 489 | 
|  | 490   // RTL runs are broken in logical order but displayed in visual order. To find | 
|  | 491   // the text-space coordinate (where it would fall in a single-line text) | 
|  | 492   // |x_range| of RTL segments, segment widths are applied in reverse order. | 
|  | 493   // e.g. {[5, 10], [10, 40]} will become {[35, 40], [5, 35]}. | 
|  | 494   void UpdateRTLSegmentRanges() { | 
|  | 495     if (rtl_segments_.empty()) | 
|  | 496       return; | 
|  | 497     float x = SegmentFromHandle(rtl_segments_[0])->x_range.start(); | 
|  | 498     for (size_t i = rtl_segments_.size(); i > 0; --i) { | 
|  | 499       internal::LineSegment* segment = SegmentFromHandle(rtl_segments_[i - 1]); | 
|  | 500       const float segment_width = segment->width; | 
|  | 501       segment->x_range = Range(x, x + segment_width); | 
|  | 502       x += segment_width; | 
|  | 503     } | 
|  | 504     rtl_segments_.clear(); | 
| 471   } | 505   } | 
| 472 | 506 | 
| 473   const SkScalar max_width_; | 507   const SkScalar max_width_; | 
| 474   const int min_baseline_; | 508   const int min_baseline_; | 
| 475   const float min_height_; | 509   const float min_height_; | 
| 476   const bool multiline_; |  | 
| 477   const WordWrapBehavior word_wrap_behavior_; | 510   const WordWrapBehavior word_wrap_behavior_; | 
| 478   const base::string16& text_; | 511   const base::string16& text_; | 
| 479   const BreakList<size_t>* const words_; | 512   const BreakList<size_t>* const words_; | 
| 480   const internal::TextRunList& run_list_; | 513   const internal::TextRunList& run_list_; | 
| 481 | 514 | 
| 482   // Stores the resulting lines. | 515   // Stores the resulting lines. | 
| 483   std::vector<internal::Line> lines_; | 516   std::vector<internal::Line> lines_; | 
| 484 | 517 | 
| 485   // Text space and line space x coordinates of the next segment to be added. |  | 
| 486   SkScalar text_x_; |  | 
| 487   SkScalar line_x_; |  | 
| 488 |  | 
| 489   float max_descent_; | 518   float max_descent_; | 
| 490   float max_ascent_; | 519   float max_ascent_; | 
| 491 | 520 | 
|  | 521   // Text space x coordinates of the next segment to be added. | 
|  | 522   SkScalar text_x_; | 
|  | 523   // Stores available width in the current line. | 
|  | 524   SkScalar available_width_; | 
|  | 525 | 
| 492   // Size of the multiline text, not including the currently processed line. | 526   // Size of the multiline text, not including the currently processed line. | 
| 493   SizeF total_size_; | 527   SizeF total_size_; | 
| 494 | 528 | 
| 495   // The current RTL run segments, to be applied by |UpdateRTLSegmentRanges()|. | 529   // The current RTL run segments, to be applied by |UpdateRTLSegmentRanges()|. | 
| 496   std::vector<SegmentHandle> rtl_segments_; | 530   std::vector<SegmentHandle> rtl_segments_; | 
| 497 | 531 | 
| 498   DISALLOW_COPY_AND_ASSIGN(HarfBuzzLineBreaker); | 532   DISALLOW_COPY_AND_ASSIGN(HarfBuzzLineBreaker); | 
| 499 }; | 533 }; | 
| 500 | 534 | 
| 501 // Function object for case insensitive string comparison. | 535 // Function object for case insensitive string comparison. | 
| (...skipping 23 matching lines...) Expand all  Loading... | 
| 525       baseline_offset(0), | 559       baseline_offset(0), | 
| 526       baseline_type(0), | 560       baseline_type(0), | 
| 527       font_style(0), | 561       font_style(0), | 
| 528       strike(false), | 562       strike(false), | 
| 529       diagonal_strike(false), | 563       diagonal_strike(false), | 
| 530       underline(false) { | 564       underline(false) { | 
| 531 } | 565 } | 
| 532 | 566 | 
| 533 TextRunHarfBuzz::~TextRunHarfBuzz() {} | 567 TextRunHarfBuzz::~TextRunHarfBuzz() {} | 
| 534 | 568 | 
| 535 void TextRunHarfBuzz::GetClusterAt(size_t pos, |  | 
| 536                                    Range* chars, |  | 
| 537                                    Range* glyphs) const { |  | 
| 538   DCHECK(range.Contains(Range(pos, pos + 1))); |  | 
| 539   DCHECK(chars); |  | 
| 540   DCHECK(glyphs); |  | 
| 541 |  | 
| 542   if (glyph_count == 0) { |  | 
| 543     *chars = range; |  | 
| 544     *glyphs = Range(); |  | 
| 545     return; |  | 
| 546   } |  | 
| 547 |  | 
| 548   if (is_rtl) { |  | 
| 549     GetClusterAtImpl(pos, range, glyph_to_char.rbegin(), glyph_to_char.rend(), |  | 
| 550         true, chars, glyphs); |  | 
| 551     return; |  | 
| 552   } |  | 
| 553 |  | 
| 554   GetClusterAtImpl(pos, range, glyph_to_char.begin(), glyph_to_char.end(), |  | 
| 555       false, chars, glyphs); |  | 
| 556 } |  | 
| 557 |  | 
| 558 Range TextRunHarfBuzz::CharRangeToGlyphRange(const Range& char_range) const { | 569 Range TextRunHarfBuzz::CharRangeToGlyphRange(const Range& char_range) const { | 
| 559   DCHECK(range.Contains(char_range)); | 570   DCHECK(range.Contains(char_range)); | 
| 560   DCHECK(!char_range.is_reversed()); | 571   DCHECK(!char_range.is_reversed()); | 
| 561   DCHECK(!char_range.is_empty()); | 572   DCHECK(!char_range.is_empty()); | 
| 562 | 573 | 
| 563   Range start_glyphs; | 574   Range start_glyphs; | 
| 564   Range end_glyphs; | 575   Range end_glyphs; | 
| 565   Range temp_range; | 576   Range temp_range; | 
| 566   GetClusterAt(char_range.start(), &temp_range, &start_glyphs); | 577   GetClusterAt(char_range.start(), &temp_range, &start_glyphs); | 
| 567   GetClusterAt(char_range.end() - 1, &temp_range, &end_glyphs); | 578   GetClusterAt(char_range.end() - 1, &temp_range, &end_glyphs); | 
| 568 | 579 | 
| 569   return is_rtl ? Range(end_glyphs.start(), start_glyphs.end()) : | 580   return is_rtl ? Range(end_glyphs.start(), start_glyphs.end()) : | 
| 570       Range(start_glyphs.start(), end_glyphs.end()); | 581       Range(start_glyphs.start(), end_glyphs.end()); | 
| 571 } | 582 } | 
| 572 | 583 | 
| 573 size_t TextRunHarfBuzz::CountMissingGlyphs() const { | 584 size_t TextRunHarfBuzz::CountMissingGlyphs() const { | 
| 574   static const int kMissingGlyphId = 0; | 585   static const int kMissingGlyphId = 0; | 
| 575   size_t missing = 0; | 586   size_t missing = 0; | 
| 576   for (size_t i = 0; i < glyph_count; ++i) | 587   for (size_t i = 0; i < glyph_count; ++i) | 
| 577     missing += (glyphs[i] == kMissingGlyphId) ? 1 : 0; | 588     missing += (glyphs[i] == kMissingGlyphId) ? 1 : 0; | 
| 578   return missing; | 589   return missing; | 
| 579 } | 590 } | 
| 580 | 591 | 
|  | 592 void TextRunHarfBuzz::GetClusterAt(size_t pos, | 
|  | 593                                    Range* chars, | 
|  | 594                                    Range* glyphs) const { | 
|  | 595   DCHECK(range.Contains(Range(pos, pos + 1))); | 
|  | 596   DCHECK(chars); | 
|  | 597   DCHECK(glyphs); | 
|  | 598 | 
|  | 599   if (glyph_count == 0) { | 
|  | 600     *chars = range; | 
|  | 601     *glyphs = Range(); | 
|  | 602     return; | 
|  | 603   } | 
|  | 604 | 
|  | 605   if (is_rtl) { | 
|  | 606     GetClusterAtImpl(pos, range, glyph_to_char.rbegin(), glyph_to_char.rend(), | 
|  | 607                      true, chars, glyphs); | 
|  | 608     return; | 
|  | 609   } | 
|  | 610 | 
|  | 611   GetClusterAtImpl(pos, range, glyph_to_char.begin(), glyph_to_char.end(), | 
|  | 612                    false, chars, glyphs); | 
|  | 613 } | 
|  | 614 | 
| 581 RangeF TextRunHarfBuzz::GetGraphemeBounds( | 615 RangeF TextRunHarfBuzz::GetGraphemeBounds( | 
| 582     base::i18n::BreakIterator* grapheme_iterator, | 616     base::i18n::BreakIterator* grapheme_iterator, | 
| 583     size_t text_index) { | 617     size_t text_index) { | 
| 584   DCHECK_LT(text_index, range.end()); | 618   DCHECK_LT(text_index, range.end()); | 
| 585   if (glyph_count == 0) | 619   if (glyph_count == 0) | 
| 586     return RangeF(preceding_run_widths, preceding_run_widths + width); | 620     return RangeF(preceding_run_widths, preceding_run_widths + width); | 
| 587 | 621 | 
| 588   Range chars; | 622   Range chars; | 
| 589   Range glyphs; | 623   Range glyphs; | 
| 590   GetClusterAt(text_index, &chars, &glyphs); | 624   GetClusterAt(text_index, &chars, &glyphs); | 
| (...skipping 28 matching lines...) Expand all  Loading... | 
| 619           cluster_width * (before + 1) / static_cast<float>(total)); | 653           cluster_width * (before + 1) / static_cast<float>(total)); | 
| 620       return RangeF(preceding_run_widths + grapheme_begin_x, | 654       return RangeF(preceding_run_widths + grapheme_begin_x, | 
| 621                     preceding_run_widths + grapheme_end_x); | 655                     preceding_run_widths + grapheme_end_x); | 
| 622     } | 656     } | 
| 623   } | 657   } | 
| 624 | 658 | 
| 625   return RangeF(preceding_run_widths + cluster_begin_x, | 659   return RangeF(preceding_run_widths + cluster_begin_x, | 
| 626                 preceding_run_widths + cluster_end_x); | 660                 preceding_run_widths + cluster_end_x); | 
| 627 } | 661 } | 
| 628 | 662 | 
|  | 663 SkScalar TextRunHarfBuzz::GetGlyphWidthForCharRange( | 
|  | 664     const Range& char_range) const { | 
|  | 665   if (char_range.is_empty()) | 
|  | 666     return 0; | 
|  | 667 | 
|  | 668   DCHECK(range.Contains(char_range)); | 
|  | 669   Range glyph_range = CharRangeToGlyphRange(char_range); | 
|  | 670   return ((glyph_range.end() == glyph_count) | 
|  | 671               ? SkFloatToScalar(width) | 
|  | 672               : positions[glyph_range.end()].x()) - | 
|  | 673          positions[glyph_range.start()].x(); | 
|  | 674 } | 
|  | 675 | 
| 629 TextRunList::TextRunList() : width_(0.0f) {} | 676 TextRunList::TextRunList() : width_(0.0f) {} | 
| 630 | 677 | 
| 631 TextRunList::~TextRunList() {} | 678 TextRunList::~TextRunList() {} | 
| 632 | 679 | 
| 633 void TextRunList::Reset() { | 680 void TextRunList::Reset() { | 
| 634   runs_.clear(); | 681   runs_.clear(); | 
| 635   width_ = 0.0f; | 682   width_ = 0.0f; | 
| 636 } | 683 } | 
| 637 | 684 | 
| 638 void TextRunList::InitIndexMap() { | 685 void TextRunList::InitIndexMap() { | 
| (...skipping 14 matching lines...) Expand all  Loading... | 
| 653 void TextRunList::ComputePrecedingRunWidths() { | 700 void TextRunList::ComputePrecedingRunWidths() { | 
| 654   // Precalculate run width information. | 701   // Precalculate run width information. | 
| 655   width_ = 0.0f; | 702   width_ = 0.0f; | 
| 656   for (size_t i = 0; i < runs_.size(); ++i) { | 703   for (size_t i = 0; i < runs_.size(); ++i) { | 
| 657     TextRunHarfBuzz* run = runs_[visual_to_logical_[i]]; | 704     TextRunHarfBuzz* run = runs_[visual_to_logical_[i]]; | 
| 658     run->preceding_run_widths = width_; | 705     run->preceding_run_widths = width_; | 
| 659     width_ += run->width; | 706     width_ += run->width; | 
| 660   } | 707   } | 
| 661 } | 708 } | 
| 662 | 709 | 
|  | 710 size_t TextRunList::GetRunIndexAt(size_t position) const { | 
|  | 711   for (size_t i = 0; i < runs_.size(); ++i) { | 
|  | 712     if (runs_[i]->range.start() <= position && runs_[i]->range.end() > position) | 
|  | 713       return i; | 
|  | 714   } | 
|  | 715   return runs_.size(); | 
|  | 716 } | 
|  | 717 | 
| 663 }  // namespace internal | 718 }  // namespace internal | 
| 664 | 719 | 
| 665 RenderTextHarfBuzz::RenderTextHarfBuzz() | 720 RenderTextHarfBuzz::RenderTextHarfBuzz() | 
| 666     : RenderText(), | 721     : RenderText(), | 
| 667       update_layout_run_list_(false), | 722       update_layout_run_list_(false), | 
| 668       update_display_run_list_(false), | 723       update_display_run_list_(false), | 
| 669       update_grapheme_iterator_(false), | 724       update_grapheme_iterator_(false), | 
| 670       update_display_text_(false), | 725       update_display_text_(false), | 
| 671       glyph_width_for_test_(0u) { | 726       glyph_width_for_test_(0u) { | 
| 672   set_truncate_length(kMaxTextLength); | 727   set_truncate_length(kMaxTextLength); | 
| (...skipping 328 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 1001   if (lines().empty()) { | 1056   if (lines().empty()) { | 
| 1002     // TODO(ckocagil): Remove ScopedTracker below once crbug.com/441028 is | 1057     // TODO(ckocagil): Remove ScopedTracker below once crbug.com/441028 is | 
| 1003     // fixed. | 1058     // fixed. | 
| 1004     scoped_ptr<tracked_objects::ScopedTracker> tracking_profile( | 1059     scoped_ptr<tracked_objects::ScopedTracker> tracking_profile( | 
| 1005         new tracked_objects::ScopedTracker( | 1060         new tracked_objects::ScopedTracker( | 
| 1006             FROM_HERE_WITH_EXPLICIT_FUNCTION("441028 HarfBuzzLineBreaker"))); | 1061             FROM_HERE_WITH_EXPLICIT_FUNCTION("441028 HarfBuzzLineBreaker"))); | 
| 1007 | 1062 | 
| 1008     internal::TextRunList* run_list = GetRunList(); | 1063     internal::TextRunList* run_list = GetRunList(); | 
| 1009     HarfBuzzLineBreaker line_breaker( | 1064     HarfBuzzLineBreaker line_breaker( | 
| 1010         display_rect().width(), font_list().GetBaseline(), | 1065         display_rect().width(), font_list().GetBaseline(), | 
| 1011         std::max(font_list().GetHeight(), min_line_height()), multiline(), | 1066         std::max(font_list().GetHeight(), min_line_height()), | 
| 1012         word_wrap_behavior(), GetDisplayText(), | 1067         word_wrap_behavior(), GetDisplayText(), | 
| 1013         multiline() ? &GetLineBreaks() : nullptr, *run_list); | 1068         multiline() ? &GetLineBreaks() : nullptr, *run_list); | 
| 1014 | 1069 | 
| 1015     tracking_profile.reset(); | 1070     tracking_profile.reset(); | 
| 1016 | 1071 | 
| 1017     for (size_t i = 0; i < run_list->size(); ++i) | 1072     if (multiline()) | 
| 1018       line_breaker.AddRun(i); | 1073       line_breaker.ConstructMultiLines(); | 
|  | 1074     else | 
|  | 1075       line_breaker.ConstructSingleLine(); | 
| 1019     std::vector<internal::Line> lines; | 1076     std::vector<internal::Line> lines; | 
| 1020     line_breaker.Finalize(&lines, &total_size_); | 1077     line_breaker.FinalizeLines(&lines, &total_size_); | 
| 1021     set_lines(&lines); | 1078     set_lines(&lines); | 
| 1022   } | 1079   } | 
| 1023 } | 1080 } | 
| 1024 | 1081 | 
| 1025 void RenderTextHarfBuzz::DrawVisualText(Canvas* canvas) { | 1082 void RenderTextHarfBuzz::DrawVisualText(Canvas* canvas) { | 
| 1026   internal::SkiaTextRenderer renderer(canvas); | 1083   internal::SkiaTextRenderer renderer(canvas); | 
| 1027   DrawVisualTextInternal(&renderer); | 1084   DrawVisualTextInternal(&renderer); | 
| 1028 } | 1085 } | 
| 1029 | 1086 | 
| 1030 void RenderTextHarfBuzz::DrawVisualTextInternal( | 1087 void RenderTextHarfBuzz::DrawVisualTextInternal( | 
| (...skipping 448 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 1479   DCHECK(!update_layout_run_list_); | 1536   DCHECK(!update_layout_run_list_); | 
| 1480   DCHECK(!update_display_run_list_); | 1537   DCHECK(!update_display_run_list_); | 
| 1481   return text_elided() ? display_run_list_.get() : &layout_run_list_; | 1538   return text_elided() ? display_run_list_.get() : &layout_run_list_; | 
| 1482 } | 1539 } | 
| 1483 | 1540 | 
| 1484 const internal::TextRunList* RenderTextHarfBuzz::GetRunList() const { | 1541 const internal::TextRunList* RenderTextHarfBuzz::GetRunList() const { | 
| 1485   return const_cast<RenderTextHarfBuzz*>(this)->GetRunList(); | 1542   return const_cast<RenderTextHarfBuzz*>(this)->GetRunList(); | 
| 1486 } | 1543 } | 
| 1487 | 1544 | 
| 1488 }  // namespace gfx | 1545 }  // namespace gfx | 
| OLD | NEW | 
|---|