Chromium Code Reviews| 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 72 matching lines...) Expand 10 before | Expand all | Expand 10 after 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 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 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()) { |
| 169 ScriptSetIntersect(char_iterator.get(), scripts, &scripts_size); | 166 ScriptSetIntersect(char_iterator.get(), scripts, &scripts_size); |
| 167 // Special handling for white space. White space should be merged into the | |
|
msw
2015/04/23 00:00:38
nit: "Special handling to merge white space into t
xdai1
2015/04/23 22:59:42
Done.
| |
| 168 // previous run. | |
| 169 if (u_isUWhiteSpace(char_iterator.get())) { | |
|
msw
2015/04/23 00:00:38
Just "continue;" instead of faking out |scripts| a
xdai1
2015/04/23 22:59:42
Actually this is depending on how we want to break
| |
| 170 scripts[0] = *script; | |
| 171 scripts_size = 1U; | |
| 172 } | |
| 170 if (scripts_size == 0U) | 173 if (scripts_size == 0U) |
| 171 return char_iterator.array_pos(); | 174 return char_iterator.array_pos(); |
| 172 *script = scripts[0]; | 175 *script = scripts[0]; |
| 173 } | 176 } |
| 174 | 177 |
| 175 return length; | 178 return length; |
| 176 } | 179 } |
| 177 | 180 |
| 178 // A port of hb_icu_script_to_script because harfbuzz on CrOS is built without | 181 // A port of hb_icu_script_to_script because harfbuzz on CrOS is built without |
| 179 // hb-icu. See http://crbug.com/356929 | 182 // hb-icu. See http://crbug.com/356929 |
| (...skipping 131 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 311 size_t start_char, | 314 size_t start_char, |
| 312 SkScalar* width, | 315 SkScalar* width, |
| 313 size_t* end_char, | 316 size_t* end_char, |
| 314 size_t* next_char) { | 317 size_t* next_char) { |
| 315 DCHECK(words_); | 318 DCHECK(words_); |
| 316 DCHECK(run.range.Contains(Range(start_char, start_char + 1))); | 319 DCHECK(run.range.Contains(Range(start_char, start_char + 1))); |
| 317 SkScalar available_width = max_width_ - line_x_; | 320 SkScalar available_width = max_width_ - line_x_; |
| 318 BreakList<size_t>::const_iterator word = words_->GetBreak(start_char); | 321 BreakList<size_t>::const_iterator word = words_->GetBreak(start_char); |
| 319 BreakList<size_t>::const_iterator next_word = word + 1; | 322 BreakList<size_t>::const_iterator next_word = word + 1; |
| 320 // Width from |std::max(word->first, start_char)| to the current character. | 323 // Width from |std::max(word->first, start_char)| to the current character. |
| 321 SkScalar word_width = 0; | 324 SkScalar word_width = |
|
msw
2015/04/23 00:00:38
Is this necessary for the run breaking change? Can
xdai1
2015/04/23 22:59:42
Yes, it has to be modified as described in the CL
| |
| 325 start_char == 0 ? 0 : GetGlyphWidth(word->first, start_char - 1); | |
| 322 *width = 0; | 326 *width = 0; |
| 323 | 327 |
| 324 Range char_range; | 328 Range char_range; |
| 325 SkScalar truncated_width = 0; | 329 SkScalar truncated_width = 0; |
| 326 for (size_t i = start_char; i < run.range.end(); i += char_range.length()) { | 330 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 | 331 // |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| | 332 // the word boundary right after |i|. Advance both |word| and |next_word| |
| 329 // when |i| reaches |next_word|. | 333 // when |i| reaches |next_word|. |
| 330 if (next_word != words_->breaks().end() && i >= next_word->first) { | 334 if (next_word != words_->breaks().end() && i >= next_word->first) { |
| 331 if (*width > available_width) { | 335 if (*width > available_width) { |
| (...skipping 16 matching lines...) Expand all Loading... | |
| 348 SkScalar char_width = ((glyph_range.end() >= run.glyph_count) | 352 SkScalar char_width = ((glyph_range.end() >= run.glyph_count) |
| 349 ? SkFloatToScalar(run.width) | 353 ? SkFloatToScalar(run.width) |
| 350 : run.positions[glyph_range.end()].x()) - | 354 : run.positions[glyph_range.end()].x()) - |
| 351 run.positions[glyph_range.start()].x(); | 355 run.positions[glyph_range.start()].x(); |
| 352 | 356 |
| 353 *width += char_width; | 357 *width += char_width; |
| 354 word_width += char_width; | 358 word_width += char_width; |
| 355 | 359 |
| 356 // TODO(mukai): implement ELIDE_LONG_WORDS. | 360 // TODO(mukai): implement ELIDE_LONG_WORDS. |
| 357 if (*width > available_width) { | 361 if (*width > available_width) { |
| 358 if (line_x_ != 0 || word_width < *width) { | 362 if ((line_x_ != 0 && word_width <= *width) || |
| 363 (line_x_ == 0 && word_width < *width)) { | |
| 359 // Roll back one word. | 364 // Roll back one word. |
| 360 *width -= word_width; | 365 *width -= word_width; |
| 361 *next_char = std::max(word->first, start_char); | 366 *next_char = std::max(word->first, start_char); |
| 362 *end_char = *next_char; | 367 *end_char = *next_char; |
| 363 return true; | 368 return true; |
| 364 } else if (word_wrap_behavior_ == WRAP_LONG_WORDS) { | 369 } else if (word_wrap_behavior_ == WRAP_LONG_WORDS) { |
| 365 if (char_width < *width) { | 370 if (char_width < *width) { |
| 366 // Roll back one character. | 371 // Roll back one character. |
| 367 *width -= char_width; | 372 *width -= char_width; |
| 368 *next_char = i; | 373 *next_char = i; |
| (...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 463 SegmentHandle(lines_.size() - 1, line->segments.size() - 1)); | 468 SegmentHandle(lines_.size() - 1, line->segments.size() - 1)); |
| 464 // If this is the last segment of an RTL run, reprocess the text-space x | 469 // If this is the last segment of an RTL run, reprocess the text-space x |
| 465 // ranges of all segments from the run. | 470 // ranges of all segments from the run. |
| 466 if (char_range.end() == run.range.end()) | 471 if (char_range.end() == run.range.end()) |
| 467 UpdateRTLSegmentRanges(); | 472 UpdateRTLSegmentRanges(); |
| 468 } | 473 } |
| 469 text_x_ += SkFloatToScalar(width); | 474 text_x_ += SkFloatToScalar(width); |
| 470 line_x_ += SkFloatToScalar(width); | 475 line_x_ += SkFloatToScalar(width); |
| 471 } | 476 } |
| 472 | 477 |
| 478 // Returns the index for the run that contains text_[pos] using binary search. | |
| 479 size_t GetRunIndexAt(size_t pos) const { | |
| 480 pos = (pos < text_.size()) ? pos : text_.size() - 1; | |
| 481 size_t low = 0, high = run_list_.size() - 1; | |
| 482 while (low <= high) { | |
| 483 size_t mid = low + (high - low) / 2; | |
| 484 if (run_list_.runs()[mid]->range.start() <= pos && | |
| 485 run_list_.runs()[mid]->range.end() > pos) | |
| 486 return mid; | |
| 487 else if (run_list_.runs()[mid]->range.start() > pos) | |
| 488 high = mid - 1; | |
| 489 else | |
| 490 low = mid + 1; | |
| 491 } | |
| 492 return 0; | |
| 493 } | |
| 494 | |
| 495 // Returns the glyph width for |text_| between [start_pos, end_pos]. | |
| 496 SkScalar GetGlyphWidth(size_t start_pos, size_t end_pos) const { | |
| 497 end_pos = (end_pos < text_.size()) ? end_pos : text_.size() - 1; | |
| 498 if (start_pos > end_pos) | |
| 499 return 0; | |
| 500 | |
| 501 // Finding the runs that containing |start_char| and |end_char|. | |
| 502 size_t start_run_idx = GetRunIndexAt(start_pos); | |
| 503 size_t end_run_idx = GetRunIndexAt(end_pos); | |
| 504 const internal::TextRunHarfBuzz* start_run = | |
| 505 run_list_.runs()[start_run_idx]; | |
| 506 const internal::TextRunHarfBuzz* end_run = run_list_.runs()[end_run_idx]; | |
| 507 | |
| 508 SkScalar width = 0; | |
| 509 for (size_t i = start_run_idx; i <= end_run_idx; ++i) { | |
| 510 width += run_list_.runs()[i]->width; | |
| 511 } | |
| 512 | |
| 513 Range char_range; | |
| 514 Range glyph_range; | |
| 515 for (size_t pos = start_run->range.start(); pos < start_pos; ++pos) { | |
| 516 start_run->GetClusterAt(pos, &char_range, &glyph_range); | |
| 517 SkScalar char_width = | |
| 518 ((glyph_range.end() >= start_run->glyph_count) | |
| 519 ? SkFloatToScalar(start_run->width) | |
| 520 : start_run->positions[glyph_range.end()].x()) - | |
| 521 start_run->positions[glyph_range.start()].x(); | |
| 522 width -= char_width; | |
| 523 } | |
| 524 | |
| 525 for (size_t pos = end_pos + 1; pos < end_run->range.end(); ++pos) { | |
| 526 end_run->GetClusterAt(pos, &char_range, &glyph_range); | |
| 527 SkScalar char_width = ((glyph_range.end() >= end_run->glyph_count) | |
| 528 ? SkFloatToScalar(end_run->width) | |
| 529 : end_run->positions[glyph_range.end()].x()) - | |
| 530 end_run->positions[glyph_range.start()].x(); | |
| 531 width -= char_width; | |
| 532 } | |
| 533 | |
| 534 return width; | |
| 535 } | |
| 536 | |
| 473 const SkScalar max_width_; | 537 const SkScalar max_width_; |
| 474 const int min_baseline_; | 538 const int min_baseline_; |
| 475 const float min_height_; | 539 const float min_height_; |
| 476 const bool multiline_; | 540 const bool multiline_; |
| 477 const WordWrapBehavior word_wrap_behavior_; | 541 const WordWrapBehavior word_wrap_behavior_; |
| 478 const base::string16& text_; | 542 const base::string16& text_; |
| 479 const BreakList<size_t>* const words_; | 543 const BreakList<size_t>* const words_; |
| 480 const internal::TextRunList& run_list_; | 544 const internal::TextRunList& run_list_; |
| 481 | 545 |
| 482 // Stores the resulting lines. | 546 // Stores the resulting lines. |
| (...skipping 996 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1479 DCHECK(!update_layout_run_list_); | 1543 DCHECK(!update_layout_run_list_); |
| 1480 DCHECK(!update_display_run_list_); | 1544 DCHECK(!update_display_run_list_); |
| 1481 return text_elided() ? display_run_list_.get() : &layout_run_list_; | 1545 return text_elided() ? display_run_list_.get() : &layout_run_list_; |
| 1482 } | 1546 } |
| 1483 | 1547 |
| 1484 const internal::TextRunList* RenderTextHarfBuzz::GetRunList() const { | 1548 const internal::TextRunList* RenderTextHarfBuzz::GetRunList() const { |
| 1485 return const_cast<RenderTextHarfBuzz*>(this)->GetRunList(); | 1549 return const_cast<RenderTextHarfBuzz*>(this)->GetRunList(); |
| 1486 } | 1550 } |
| 1487 | 1551 |
| 1488 } // namespace gfx | 1552 } // namespace gfx |
| OLD | NEW |