 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| 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 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 44 // character to belong to more scripts. | 44 // character to belong to more scripts. | 
| 45 const size_t kMaxScripts = 5; | 45 const size_t kMaxScripts = 5; | 
| 46 | 46 | 
| 47 // Returns true if characters of |block_code| may trigger font fallback. | 47 // Returns true if characters of |block_code| may trigger font fallback. | 
| 48 // Dingbats and emoticons can be rendered through the color emoji font file, | 48 // 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 | 49 // therefore it needs to be trigerred as fallbacks. See crbug.com/448909 | 
| 50 bool IsUnusualBlockCode(UBlockCode block_code) { | 50 bool IsUnusualBlockCode(UBlockCode block_code) { | 
| 51 return block_code == UBLOCK_GEOMETRIC_SHAPES || | 51 return block_code == UBLOCK_GEOMETRIC_SHAPES || | 
| 52 block_code == UBLOCK_MISCELLANEOUS_SYMBOLS || | 52 block_code == UBLOCK_MISCELLANEOUS_SYMBOLS || | 
| 53 block_code == UBLOCK_DINGBATS || | 53 block_code == UBLOCK_DINGBATS || | 
| 54 block_code == UBLOCK_EMOTICONS; | 54 block_code == UBLOCK_EMOTICONS; | 
| 
Jun Mukai
2015/05/11 05:14:38
Remove these special cases of DINGBATS and EMOTICI
 
xdai1
2015/05/11 21:55:22
Done.
 | |
| 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 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 232 min_baseline_(min_baseline), | 232 min_baseline_(min_baseline), | 
| 233 min_height_(min_height), | 233 min_height_(min_height), | 
| 234 multiline_(multiline), | 234 multiline_(multiline), | 
| 235 word_wrap_behavior_(word_wrap_behavior), | 235 word_wrap_behavior_(word_wrap_behavior), | 
| 236 text_(text), | 236 text_(text), | 
| 237 words_(words), | 237 words_(words), | 
| 238 run_list_(run_list), | 238 run_list_(run_list), | 
| 239 text_x_(0), | 239 text_x_(0), | 
| 240 line_x_(0), | 240 line_x_(0), | 
| 241 max_descent_(0), | 241 max_descent_(0), | 
| 242 max_ascent_(0) { | 242 max_ascent_(0), | 
| 243 next_char_pos_(0), | |
| 244 word_width_(0) { | |
| 243 DCHECK_EQ(multiline_, (words_ != nullptr)); | 245 DCHECK_EQ(multiline_, (words_ != nullptr)); | 
| 244 AdvanceLine(); | 246 AdvanceLine(); | 
| 245 } | 247 } | 
| 246 | 248 | 
| 247 // Breaks the run at given |run_index| into Line structs. | 249 // Breaks the run at given |run_index| into Line structs. | 
| 248 void AddRun(int run_index) { | 250 void AddRun(int run_index) { | 
| 249 const internal::TextRunHarfBuzz* run = run_list_.runs()[run_index]; | 251 const internal::TextRunHarfBuzz* run = run_list_.runs()[run_index]; | 
| 250 base::char16 first_char = text_[run->range.start()]; | 252 base::char16 first_char = text_[run->range.start()]; | 
| 251 if (multiline_ && first_char == '\n') { | 253 if (multiline_ && first_char == '\n') { | 
| 252 AdvanceLine(); | 254 AdvanceLine(); | 
| 253 } else if (multiline_ && (line_x_ + SkFloatToScalar(run->width)) > | 255 return; | 
| 254 max_width_) { | 256 } | 
| 255 BreakRun(run_index); | 257 | 
| 258 int ignored_char_offset = 0; | |
| 259 SkScalar ignored_width = 0; | |
| 260 if (multiline_ && word_wrap_behavior_ == TRUNCATE_LONG_WORDS ) { | |
| 
Jun Mukai
2015/05/11 05:14:38
This logic looks fairly complicated.  I don't get
 
xdai1
2015/05/11 21:55:22
Done.
 | |
| 261 // Check if the run needs to be fully or partially ignored. | |
| 262 if (run->range.start() < next_char_pos_) { | |
| 263 BreakList<size_t>::const_iterator first_word_in_run = | |
| 264 words_->GetBreak(run->range.start()); | |
| 265 BreakList<size_t>::const_iterator last_word_in_run = | |
| 266 words_->GetBreak(run->range.end() - 1); | |
| 267 if (first_word_in_run == last_word_in_run) { | |
| 268 // The run's range is fully covered by the current word. | |
| 269 return; | |
| 270 } else { | |
| 271 // The run contains multiple words, calculate |ignored_char_offset| | |
| 272 // and |ignored_width|. | |
| 273 BreakList<size_t>::const_iterator next_word = first_word_in_run + 1; | |
| 274 ignored_char_offset = next_word->first - run->range.start(); | |
| 275 Range ignored_char_range(run->range.start(), next_word->first); | |
| 276 Range ignored_glyph_range = | |
| 277 run->CharRangeToGlyphRange(ignored_char_range); | |
| 278 ignored_width = | |
| 279 ((ignored_glyph_range.end() >= run->glyph_count) | |
| 280 ? SkFloatToScalar(run->width) | |
| 281 : run->positions[ignored_glyph_range.end()].x()) - | |
| 282 run->positions[ignored_glyph_range.start()].x(); | |
| 283 } | |
| 284 } | |
| 285 } | |
| 286 | |
| 287 if (multiline_ && line_x_ != 0) { | |
| 288 // If the following word is not the first word in the current line and | |
| 289 // can't be fit into the current line, advance a new line. | |
| 290 BreakList<size_t>::const_iterator word = | |
| 291 words_->GetBreak(run->range.start()); | |
| 292 if (run->range.start() == word->first && | |
| 293 line_x_ + GetWordWidth(word) - ignored_width > max_width_) { | |
| 294 AdvanceLine(); | |
| 295 } | |
| 296 } | |
| 297 | |
| 298 if (multiline_ && | |
| 299 (line_x_ + SkFloatToScalar(run->width) - ignored_width) > max_width_) { | |
| 300 BreakRun(run_index, ignored_char_offset); | |
| 256 } else { | 301 } else { | 
| 257 AddSegment(run_index, run->range, run->width); | 302 Range char_range(run->range.start() + ignored_char_offset, | 
| 303 run->range.end()); | |
| 304 AddSegment(run_index, char_range, run->width - ignored_width); | |
| 258 } | 305 } | 
| 259 } | 306 } | 
| 260 | 307 | 
| 261 // Finishes line breaking and outputs the results. Can be called at most once. | 308 // Finishes line breaking and outputs the results. Can be called at most once. | 
| 262 void Finalize(std::vector<internal::Line>* lines, SizeF* size) { | 309 void Finalize(std::vector<internal::Line>* lines, SizeF* size) { | 
| 263 DCHECK(!lines_.empty()); | 310 DCHECK(!lines_.empty()); | 
| 264 // Add an empty line to finish the line size calculation and remove it. | 311 // Add an empty line to finish the line size calculation and remove it. | 
| 265 AdvanceLine(); | 312 AdvanceLine(); | 
| 266 lines_.pop_back(); | 313 lines_.pop_back(); | 
| 267 *size = total_size_; | 314 *size = total_size_; | 
| 268 lines->swap(lines_); | 315 lines->swap(lines_); | 
| 269 } | 316 } | 
| 270 | 317 | 
| 271 private: | 318 private: | 
| 272 // A (line index, segment index) pair that specifies a segment in |lines_|. | 319 // A (line index, segment index) pair that specifies a segment in |lines_|. | 
| 273 typedef std::pair<size_t, size_t> SegmentHandle; | 320 typedef std::pair<size_t, size_t> SegmentHandle; | 
| 274 | 321 | 
| 275 internal::LineSegment* SegmentFromHandle(const SegmentHandle& handle) { | 322 internal::LineSegment* SegmentFromHandle(const SegmentHandle& handle) { | 
| 276 return &lines_[handle.first].segments[handle.second]; | 323 return &lines_[handle.first].segments[handle.second]; | 
| 277 } | 324 } | 
| 278 | 325 | 
| 326 SkScalar GetWordWidth(BreakList<size_t>::const_iterator word) { | |
| 327 if (word == words_->breaks().end()) | |
| 328 return 0; | |
| 329 size_t word_st = word->first; | |
| 330 size_t word_en = (word + 1 == words_->breaks().end()) | |
| 331 ? text_.size() | |
| 332 : (word + 1)->first; | |
| 333 internal::TextRunList::TextRunHarfBuzzIter run_st = | |
| 334 run_list_.GetRunAt(word_st); | |
| 335 internal::TextRunList::TextRunHarfBuzzIter run_en = | |
| 336 run_list_.GetRunAt(word_en); | |
| 337 SkScalar width = 0; | |
| 338 for (auto iter = run_st; iter != run_en; iter++) { | |
| 339 Range char_range = (*iter)->range.Intersect(Range(word_st, word_en)); | |
| 340 Range glyph_range = (*iter)->CharRangeToGlyphRange(char_range); | |
| 341 SkScalar char_width = ((glyph_range.end() >= (*iter)->glyph_count) | |
| 342 ? SkFloatToScalar((*iter)->width) | |
| 343 : (*iter)->positions[glyph_range.end()].x()) - | |
| 344 (*iter)->positions[glyph_range.start()].x(); | |
| 345 width += char_width; | |
| 346 } | |
| 347 return width; | |
| 348 } | |
| 349 | |
| 279 // Breaks a run into segments that fit in the last line in |lines_| and adds | 350 // 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 | 351 // 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_|. | 352 // be added without the Line's width exceeding |max_width_|. | 
| 282 void BreakRun(int run_index) { | 353 void BreakRun(int run_index, int ignored_offset) { | 
| 283 const internal::TextRunHarfBuzz& run = *(run_list_.runs()[run_index]); | 354 const internal::TextRunHarfBuzz& run = *(run_list_.runs()[run_index]); | 
| 284 SkScalar width = 0; | 355 SkScalar width = 0; | 
| 285 size_t next_char = run.range.start(); | 356 next_char_pos_ = run.range.start() + ignored_offset; | 
| 286 | 357 | 
| 287 // Break the run until it fits the current line. | 358 // Break the run until it fits the current line. | 
| 288 while (next_char < run.range.end()) { | 359 while (next_char_pos_ < run.range.end()) { | 
| 289 const size_t current_char = next_char; | 360 const size_t current_char = next_char_pos_; | 
| 290 size_t end_char = next_char; | 361 size_t end_char = next_char_pos_; | 
| 291 const bool skip_line = | 362 const bool skip_line = | 
| 292 BreakRunAtWidth(run, current_char, &width, &end_char, &next_char); | 363 BreakRunAtWidth(run, current_char, &width, &end_char); | 
| 293 AddSegment(run_index, Range(current_char, end_char), | 364 AddSegment(run_index, Range(current_char, end_char), | 
| 294 SkScalarToFloat(width)); | 365 SkScalarToFloat(width)); | 
| 295 if (skip_line) | 366 if (skip_line) | 
| 296 AdvanceLine(); | 367 AdvanceLine(); | 
| 297 } | 368 } | 
| 298 } | 369 } | 
| 299 | 370 | 
| 300 // Starting from |start_char|, finds a suitable line break position at or | 371 // 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 | 372 // 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 | 373 // 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 | 374 // |*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 | 375 // 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. | 376 // smaller than |*next_char| for certain word wrapping behavior. | 
| 306 // Returns whether to skip the line before |*next_char|. | 377 // Returns whether to skip the line before |*next_char|. | 
| 307 // TODO(ckocagil): We might have to reshape after breaking at ligatures. | 378 // TODO(ckocagil): We might have to reshape after breaking at ligatures. | 
| 308 // See whether resolving the TODO above resolves this too. | 379 // See whether resolving the TODO above resolves this too. | 
| 309 // TODO(ckocagil): Do not reserve width for whitespace at the end of lines. | 380 // TODO(ckocagil): Do not reserve width for whitespace at the end of lines. | 
| 310 bool BreakRunAtWidth(const internal::TextRunHarfBuzz& run, | 381 bool BreakRunAtWidth(const internal::TextRunHarfBuzz& run, | 
| 311 size_t start_char, | 382 size_t start_char, | 
| 312 SkScalar* width, | 383 SkScalar* width, | 
| 313 size_t* end_char, | 384 size_t* end_char) { | 
| 314 size_t* next_char) { | |
| 315 DCHECK(words_); | 385 DCHECK(words_); | 
| 316 DCHECK(run.range.Contains(Range(start_char, start_char + 1))); | 386 DCHECK(run.range.Contains(Range(start_char, start_char + 1))); | 
| 317 SkScalar available_width = max_width_ - line_x_; | 387 SkScalar available_width = max_width_ - line_x_; | 
| 318 BreakList<size_t>::const_iterator word = words_->GetBreak(start_char); | 388 BreakList<size_t>::const_iterator word = words_->GetBreak(start_char); | 
| 319 BreakList<size_t>::const_iterator next_word = word + 1; | 389 BreakList<size_t>::const_iterator next_word = word + 1; | 
| 320 // Width from |std::max(word->first, start_char)| to the current character. | 390 size_t word_end_pos = | 
| 321 SkScalar word_width = 0; | 391 (next_word == words_->breaks().end()) ? text_.size() : next_word->first; | 
| 392 word_width_ = (start_char == word->first) ? 0 : word_width_; | |
| 322 *width = 0; | 393 *width = 0; | 
| 323 | 394 | 
| 324 Range char_range; | 395 Range char_range; | 
| 325 SkScalar truncated_width = 0; | 396 SkScalar truncated_width = 0; | 
| 326 for (size_t i = start_char; i < run.range.end(); i += char_range.length()) { | 397 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 | 398 // |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| | 399 // the word boundary right after |i|. Advance both |word| and |next_word| | 
| 329 // when |i| reaches |next_word|. | 400 // when |i| reaches |next_word|. | 
| 330 if (next_word != words_->breaks().end() && i >= next_word->first) { | 401 if (next_word != words_->breaks().end() && i >= next_word->first) { | 
| 331 if (*width > available_width) { | |
| 
Jun Mukai
2015/05/11 05:14:38
And then, you will have to keep this logic right?
 
xdai1
2015/05/11 21:55:22
As we discussed offline, "aaaaa'bb ccc" has two wo
 | |
| 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++; | 402 word = next_word++; | 
| 341 word_width = 0; | 403 word_end_pos = (next_word == words_->breaks().end()) | 
| 404 ? text_.size() | |
| 405 : next_word->first; | |
| 406 word_width_ = 0; | |
| 342 } | 407 } | 
| 343 | 408 | 
| 344 Range glyph_range; | 409 Range glyph_range; | 
| 345 run.GetClusterAt(i, &char_range, &glyph_range); | 410 run.GetClusterAt(i, &char_range, &glyph_range); | 
| 346 DCHECK_LT(0U, char_range.length()); | 411 DCHECK_LT(0U, char_range.length()); | 
| 347 | 412 | 
| 348 SkScalar char_width = ((glyph_range.end() >= run.glyph_count) | 413 SkScalar char_width = ((glyph_range.end() >= run.glyph_count) | 
| 349 ? SkFloatToScalar(run.width) | 414 ? SkFloatToScalar(run.width) | 
| 350 : run.positions[glyph_range.end()].x()) - | 415 : run.positions[glyph_range.end()].x()) - | 
| 351 run.positions[glyph_range.start()].x(); | 416 run.positions[glyph_range.start()].x(); | 
| 352 | 417 | 
| 353 *width += char_width; | 418 *width += char_width; | 
| 354 word_width += char_width; | 419 word_width_ += char_width; | 
| 355 | 420 | 
| 356 // TODO(mukai): implement ELIDE_LONG_WORDS. | 421 // TODO(mukai): implement ELIDE_LONG_WORDS. | 
| 357 if (*width > available_width) { | 422 if (*width > available_width) { | 
| 358 if (line_x_ != 0 || word_width < *width) { | 423 if ((line_x_ != 0 && word_width_ <= *width) || | 
| 424 (line_x_ == 0 && word_width_ < *width)) { | |
| 359 // Roll back one word. | 425 // Roll back one word. | 
| 360 *width -= word_width; | 426 *width -= word_width_; | 
| 361 *next_char = std::max(word->first, start_char); | 427 next_char_pos_ = std::max(word->first, start_char);; | 
| 362 *end_char = *next_char; | 428 *end_char = next_char_pos_; | 
| 429 word_width_ = 0; | |
| 363 return true; | 430 return true; | 
| 364 } else if (word_wrap_behavior_ == WRAP_LONG_WORDS) { | 431 } else if (word_wrap_behavior_ == WRAP_LONG_WORDS) { | 
| 365 if (char_width < *width) { | 432 if (char_width < *width || | 
| 433 (word_width_ > *width && char_width <= *width)) { | |
| 366 // Roll back one character. | 434 // Roll back one character. | 
| 367 *width -= char_width; | 435 *width -= char_width; | 
| 368 *next_char = i; | 436 next_char_pos_ = i; | 
| 369 } else { | 437 } else { | 
| 370 // Continue from the next character. | 438 // Continue from the next character. | 
| 371 *next_char = i + char_range.length(); | 439 next_char_pos_ = i + char_range.length(); | 
| 372 } | 440 } | 
| 373 *end_char = *next_char; | 441 *end_char = next_char_pos_; | 
| 442 word_width_ = 0; | |
| 443 return true; | |
| 444 } else if (word_wrap_behavior_ == TRUNCATE_LONG_WORDS) { | |
| 445 *width = truncated_width; | |
| 446 next_char_pos_ = word_end_pos; | |
| 447 word_width_ = 0; | |
| 374 return true; | 448 return true; | 
| 375 } | 449 } | 
| 376 } else { | 450 } else { | 
| 377 *end_char = char_range.end(); | 451 *end_char = char_range.end(); | 
| 378 truncated_width = *width; | 452 truncated_width = *width; | 
| 379 } | 453 } | 
| 380 } | 454 } | 
| 381 | 455 | 
| 382 if (word_wrap_behavior_ == TRUNCATE_LONG_WORDS) | 456 if (word_wrap_behavior_ == TRUNCATE_LONG_WORDS) | 
| 383 *width = truncated_width; | 457 *width = truncated_width; | 
| 384 *end_char = *next_char = run.range.end(); | 458 *end_char = next_char_pos_ = run.range.end(); | 
| 385 return false; | 459 return false; | 
| 386 } | 460 } | 
| 387 | 461 | 
| 388 // RTL runs are broken in logical order but displayed in visual order. To find | 462 // 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) | 463 // 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. | 464 // |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]}. | 465 // e.g. {[5, 10], [10, 40]} will become {[35, 40], [5, 35]}. | 
| 392 void UpdateRTLSegmentRanges() { | 466 void UpdateRTLSegmentRanges() { | 
| 393 if (rtl_segments_.empty()) | 467 if (rtl_segments_.empty()) | 
| 394 return; | 468 return; | 
| (...skipping 21 matching lines...) Expand all Loading... | |
| 416 line->size.set_height(std::max(min_height_, max_descent_ + max_ascent_)); | 490 line->size.set_height(std::max(min_height_, max_descent_ + max_ascent_)); | 
| 417 line->baseline = | 491 line->baseline = | 
| 418 std::max(min_baseline_, SkScalarRoundToInt(max_ascent_)); | 492 std::max(min_baseline_, SkScalarRoundToInt(max_ascent_)); | 
| 419 line->preceding_heights = std::ceil(total_size_.height()); | 493 line->preceding_heights = std::ceil(total_size_.height()); | 
| 420 total_size_.set_height(total_size_.height() + line->size.height()); | 494 total_size_.set_height(total_size_.height() + line->size.height()); | 
| 421 total_size_.set_width(std::max(total_size_.width(), line->size.width())); | 495 total_size_.set_width(std::max(total_size_.width(), line->size.width())); | 
| 422 } | 496 } | 
| 423 max_descent_ = 0; | 497 max_descent_ = 0; | 
| 424 max_ascent_ = 0; | 498 max_ascent_ = 0; | 
| 425 line_x_ = 0; | 499 line_x_ = 0; | 
| 500 word_width_ = 0; | |
| 426 lines_.push_back(internal::Line()); | 501 lines_.push_back(internal::Line()); | 
| 427 } | 502 } | 
| 428 | 503 | 
| 429 // Adds a new segment with the given properties to |lines_.back()|. | 504 // Adds a new segment with the given properties to |lines_.back()|. | 
| 430 void AddSegment(int run_index, Range char_range, float width) { | 505 void AddSegment(int run_index, Range char_range, float width) { | 
| 
Jun Mukai
2015/05/11 05:14:38
I think all of your problem is that treating the '
 | |
| 431 if (char_range.is_empty()) { | 506 if (char_range.is_empty()) { | 
| 432 DCHECK_EQ(0, width); | 507 DCHECK_EQ(0, width); | 
| 433 return; | 508 return; | 
| 434 } | 509 } | 
| 435 const internal::TextRunHarfBuzz& run = *(run_list_.runs()[run_index]); | 510 const internal::TextRunHarfBuzz& run = *(run_list_.runs()[run_index]); | 
| 436 | 511 | 
| 437 internal::LineSegment segment; | 512 internal::LineSegment segment; | 
| 438 segment.run = run_index; | 513 segment.run = run_index; | 
| 439 segment.char_range = char_range; | 514 segment.char_range = char_range; | 
| 440 segment.x_range = Range( | 515 segment.x_range = Range( | 
| (...skipping 20 matching lines...) Expand all Loading... | |
| 461 if (run.is_rtl) { | 536 if (run.is_rtl) { | 
| 462 rtl_segments_.push_back( | 537 rtl_segments_.push_back( | 
| 463 SegmentHandle(lines_.size() - 1, line->segments.size() - 1)); | 538 SegmentHandle(lines_.size() - 1, line->segments.size() - 1)); | 
| 464 // If this is the last segment of an RTL run, reprocess the text-space x | 539 // If this is the last segment of an RTL run, reprocess the text-space x | 
| 465 // ranges of all segments from the run. | 540 // ranges of all segments from the run. | 
| 466 if (char_range.end() == run.range.end()) | 541 if (char_range.end() == run.range.end()) | 
| 467 UpdateRTLSegmentRanges(); | 542 UpdateRTLSegmentRanges(); | 
| 468 } | 543 } | 
| 469 text_x_ += SkFloatToScalar(width); | 544 text_x_ += SkFloatToScalar(width); | 
| 470 line_x_ += SkFloatToScalar(width); | 545 line_x_ += SkFloatToScalar(width); | 
| 546 | |
| 547 // Calculate |word_width_|. | |
| 548 if (words_) { | |
| 549 BreakList<size_t>::const_iterator word_for_range_start = | |
| 550 words_->GetBreak(char_range.start()); | |
| 551 BreakList<size_t>::const_iterator word_for_range_end = | |
| 552 (char_range.end() == 0) | |
| 553 ? words_->breaks().begin() | |
| 554 : words_->GetBreak(char_range.end() - 1); | |
| 555 | |
| 556 if (word_for_range_start == word_for_range_end) { | |
| 557 // Current word covers the entire char_range. | |
| 558 if (word_width_ == 0) { | |
| 559 Range glyph_range = run.CharRangeToGlyphRange(char_range); | |
| 560 SkScalar char_width = ((glyph_range.end() >= run.glyph_count) | |
| 561 ? SkFloatToScalar(run.width) | |
| 562 : run.positions[glyph_range.end()].x()) - | |
| 563 run.positions[glyph_range.start()].x(); | |
| 564 word_width_ += char_width; | |
| 565 } | |
| 566 } else { | |
| 567 // Current char_range intersects with more than one word. | |
| 568 word_width_ = char_range.end() - word_for_range_end->first; | |
| 569 } | |
| 570 } | |
| 471 } | 571 } | 
| 472 | 572 | 
| 473 const SkScalar max_width_; | 573 const SkScalar max_width_; | 
| 474 const int min_baseline_; | 574 const int min_baseline_; | 
| 475 const float min_height_; | 575 const float min_height_; | 
| 476 const bool multiline_; | 576 const bool multiline_; | 
| 477 const WordWrapBehavior word_wrap_behavior_; | 577 const WordWrapBehavior word_wrap_behavior_; | 
| 478 const base::string16& text_; | 578 const base::string16& text_; | 
| 479 const BreakList<size_t>* const words_; | 579 const BreakList<size_t>* const words_; | 
| 480 const internal::TextRunList& run_list_; | 580 const internal::TextRunList& run_list_; | 
| 481 | 581 | 
| 482 // Stores the resulting lines. | 582 // Stores the resulting lines. | 
| 483 std::vector<internal::Line> lines_; | 583 std::vector<internal::Line> lines_; | 
| 484 | 584 | 
| 485 // Text space and line space x coordinates of the next segment to be added. | 585 // Text space and line space x coordinates of the next segment to be added. | 
| 486 SkScalar text_x_; | 586 SkScalar text_x_; | 
| 487 SkScalar line_x_; | 587 SkScalar line_x_; | 
| 488 | 588 | 
| 489 float max_descent_; | 589 float max_descent_; | 
| 490 float max_ascent_; | 590 float max_ascent_; | 
| 491 | 591 | 
| 592 // Stores the position of the character that need to be processed when | |
| 593 // breaking runs into multiple lines. | |
| 594 size_t next_char_pos_; | |
| 595 // Stores the glyph width between the current position and the start position | |
| 596 // of the current word. If the current position is pointing to the first | |
| 597 // character of the current word, |word_width_| is set to 0. | |
| 598 SkScalar word_width_; | |
| 599 | |
| 492 // Size of the multiline text, not including the currently processed line. | 600 // Size of the multiline text, not including the currently processed line. | 
| 493 SizeF total_size_; | 601 SizeF total_size_; | 
| 494 | 602 | 
| 495 // The current RTL run segments, to be applied by |UpdateRTLSegmentRanges()|. | 603 // The current RTL run segments, to be applied by |UpdateRTLSegmentRanges()|. | 
| 496 std::vector<SegmentHandle> rtl_segments_; | 604 std::vector<SegmentHandle> rtl_segments_; | 
| 497 | 605 | 
| 498 DISALLOW_COPY_AND_ASSIGN(HarfBuzzLineBreaker); | 606 DISALLOW_COPY_AND_ASSIGN(HarfBuzzLineBreaker); | 
| 499 }; | 607 }; | 
| 500 | 608 | 
| 501 // Function object for case insensitive string comparison. | 609 // Function object for case insensitive string comparison. | 
| (...skipping 151 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 653 void TextRunList::ComputePrecedingRunWidths() { | 761 void TextRunList::ComputePrecedingRunWidths() { | 
| 654 // Precalculate run width information. | 762 // Precalculate run width information. | 
| 655 width_ = 0.0f; | 763 width_ = 0.0f; | 
| 656 for (size_t i = 0; i < runs_.size(); ++i) { | 764 for (size_t i = 0; i < runs_.size(); ++i) { | 
| 657 TextRunHarfBuzz* run = runs_[visual_to_logical_[i]]; | 765 TextRunHarfBuzz* run = runs_[visual_to_logical_[i]]; | 
| 658 run->preceding_run_widths = width_; | 766 run->preceding_run_widths = width_; | 
| 659 width_ += run->width; | 767 width_ += run->width; | 
| 660 } | 768 } | 
| 661 } | 769 } | 
| 662 | 770 | 
| 771 TextRunList::TextRunHarfBuzzIter TextRunList::GetRunAt(size_t position) const { | |
| 772 for (TextRunList::TextRunHarfBuzzIter iter = runs_.begin(); | |
| 773 iter != runs_.end(); iter++) { | |
| 774 if ((*iter)->range.start() <= position && (*iter)->range.end() > position) | |
| 775 return iter; | |
| 776 } | |
| 777 return runs_.end(); | |
| 778 } | |
| 779 | |
| 663 } // namespace internal | 780 } // namespace internal | 
| 664 | 781 | 
| 665 RenderTextHarfBuzz::RenderTextHarfBuzz() | 782 RenderTextHarfBuzz::RenderTextHarfBuzz() | 
| 666 : RenderText(), | 783 : RenderText(), | 
| 667 update_layout_run_list_(false), | 784 update_layout_run_list_(false), | 
| 668 update_display_run_list_(false), | 785 update_display_run_list_(false), | 
| 669 update_grapheme_iterator_(false), | 786 update_grapheme_iterator_(false), | 
| 670 update_display_text_(false), | 787 update_display_text_(false), | 
| 671 glyph_width_for_test_(0u) { | 788 glyph_width_for_test_(0u) { | 
| 672 set_truncate_length(kMaxTextLength); | 789 set_truncate_length(kMaxTextLength); | 
| (...skipping 806 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1479 DCHECK(!update_layout_run_list_); | 1596 DCHECK(!update_layout_run_list_); | 
| 1480 DCHECK(!update_display_run_list_); | 1597 DCHECK(!update_display_run_list_); | 
| 1481 return text_elided() ? display_run_list_.get() : &layout_run_list_; | 1598 return text_elided() ? display_run_list_.get() : &layout_run_list_; | 
| 1482 } | 1599 } | 
| 1483 | 1600 | 
| 1484 const internal::TextRunList* RenderTextHarfBuzz::GetRunList() const { | 1601 const internal::TextRunList* RenderTextHarfBuzz::GetRunList() const { | 
| 1485 return const_cast<RenderTextHarfBuzz*>(this)->GetRunList(); | 1602 return const_cast<RenderTextHarfBuzz*>(this)->GetRunList(); | 
| 1486 } | 1603 } | 
| 1487 | 1604 | 
| 1488 } // namespace gfx | 1605 } // namespace gfx | 
| OLD | NEW |