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 206 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 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, | 226 bool multiline, |
| 227 WordWrapBehavior word_wrap_behavior, | |
| 227 const base::string16& text, | 228 const base::string16& text, |
| 228 const BreakList<size_t>* words, | 229 const BreakList<size_t>* words, |
| 229 const internal::TextRunList& run_list) | 230 const internal::TextRunList& run_list) |
| 230 : max_width_((max_width == 0) ? SK_ScalarMax : SkIntToScalar(max_width)), | 231 : max_width_((max_width == 0) ? SK_ScalarMax : SkIntToScalar(max_width)), |
| 231 min_baseline_(min_baseline), | 232 min_baseline_(min_baseline), |
| 232 min_height_(min_height), | 233 min_height_(min_height), |
| 233 multiline_(multiline), | 234 multiline_(multiline), |
| 235 word_wrap_behavior_(word_wrap_behavior), | |
| 234 text_(text), | 236 text_(text), |
| 235 words_(words), | 237 words_(words), |
| 236 run_list_(run_list), | 238 run_list_(run_list), |
| 237 text_x_(0), | 239 text_x_(0), |
| 238 line_x_(0), | 240 line_x_(0), |
| 239 max_descent_(0), | 241 max_descent_(0), |
| 240 max_ascent_(0) { | 242 max_ascent_(0) { |
| 241 DCHECK_EQ(multiline_, (words_ != nullptr)); | 243 DCHECK_EQ(multiline_, (words_ != nullptr)); |
| 242 AdvanceLine(); | 244 AdvanceLine(); |
| 243 } | 245 } |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 278 // them. Adds a new Line to the back of |lines_| whenever a new segment can't | 280 // them. Adds a new Line to the back of |lines_| whenever a new segment can't |
| 279 // be added without the Line's width exceeding |max_width_|. | 281 // be added without the Line's width exceeding |max_width_|. |
| 280 void BreakRun(int run_index) { | 282 void BreakRun(int run_index) { |
| 281 const internal::TextRunHarfBuzz& run = *(run_list_.runs()[run_index]); | 283 const internal::TextRunHarfBuzz& run = *(run_list_.runs()[run_index]); |
| 282 SkScalar width = 0; | 284 SkScalar width = 0; |
| 283 size_t next_char = run.range.start(); | 285 size_t next_char = run.range.start(); |
| 284 | 286 |
| 285 // Break the run until it fits the current line. | 287 // Break the run until it fits the current line. |
| 286 while (next_char < run.range.end()) { | 288 while (next_char < run.range.end()) { |
| 287 const size_t current_char = next_char; | 289 const size_t current_char = next_char; |
| 290 size_t end_char = next_char; | |
| 288 const bool skip_line = | 291 const bool skip_line = |
| 289 BreakRunAtWidth(run, current_char, &width, &next_char); | 292 BreakRunAtWidth(run, current_char, &width, &end_char, &next_char); |
| 290 AddSegment(run_index, Range(current_char, next_char), | 293 AddSegment(run_index, Range(current_char, end_char), |
| 291 SkScalarToFloat(width)); | 294 SkScalarToFloat(width)); |
| 292 if (skip_line) | 295 if (skip_line) |
| 293 AdvanceLine(); | 296 AdvanceLine(); |
| 294 } | 297 } |
| 295 } | 298 } |
| 296 | 299 |
| 297 // Starting from |start_char|, finds a suitable line break position at or | 300 // Starting from |start_char|, finds a suitable line break position at or |
| 298 // before available width using word break. If the current position is at the | 301 // before available width using word break. If the current position is at the |
| 299 // beginning of a line, this function will not roll back to |start_char| and | 302 // beginning of a line, this function will not roll back to |start_char| and |
| 300 // |*next_char| will be greater than |start_char| (to avoid constructing empty | 303 // |*next_char| will be greater than |start_char| (to avoid constructing empty |
| 301 // lines). | 304 // lines). It stores the end of the segment range to |end_char|, which can be |
| 305 // smaller than |*next_char| if word_wrap_behavior is TRUNCATE_LONG_WORDS. | |
|
msw
2015/03/26 19:36:29
nit: "smaller than |*next_char| for certain word w
Jun Mukai
2015/03/26 22:47:38
Done.
| |
| 302 // Returns whether to skip the line before |*next_char|. | 306 // Returns whether to skip the line before |*next_char|. |
| 303 // TODO(ckocagil): We might have to reshape after breaking at ligatures. | 307 // TODO(ckocagil): We might have to reshape after breaking at ligatures. |
| 304 // See whether resolving the TODO above resolves this too. | 308 // See whether resolving the TODO above resolves this too. |
| 305 // TODO(ckocagil): Do not reserve width for whitespace at the end of lines. | 309 // TODO(ckocagil): Do not reserve width for whitespace at the end of lines. |
| 306 bool BreakRunAtWidth(const internal::TextRunHarfBuzz& run, | 310 bool BreakRunAtWidth(const internal::TextRunHarfBuzz& run, |
| 307 size_t start_char, | 311 size_t start_char, |
| 308 SkScalar* width, | 312 SkScalar* width, |
| 313 size_t* end_char, | |
| 309 size_t* next_char) { | 314 size_t* next_char) { |
| 310 DCHECK(words_); | 315 DCHECK(words_); |
| 311 DCHECK(run.range.Contains(Range(start_char, start_char + 1))); | 316 DCHECK(run.range.Contains(Range(start_char, start_char + 1))); |
| 312 SkScalar available_width = max_width_ - line_x_; | 317 SkScalar available_width = max_width_ - line_x_; |
| 313 BreakList<size_t>::const_iterator word = words_->GetBreak(start_char); | 318 BreakList<size_t>::const_iterator word = words_->GetBreak(start_char); |
| 314 BreakList<size_t>::const_iterator next_word = word + 1; | 319 BreakList<size_t>::const_iterator next_word = word + 1; |
| 315 // Width from |std::max(word->first, start_char)| to the current character. | 320 // Width from |std::max(word->first, start_char)| to the current character. |
| 316 SkScalar word_width = 0; | 321 SkScalar word_width = 0; |
| 317 *width = 0; | 322 *width = 0; |
| 318 | 323 |
| 319 Range char_range; | 324 Range char_range; |
| 325 bool truncated = false; | |
| 320 for (size_t i = start_char; i < run.range.end(); i += char_range.length()) { | 326 for (size_t i = start_char; i < run.range.end(); i += char_range.length()) { |
| 321 // |word| holds the word boundary at or before |i|, and |next_word| holds | 327 // |word| holds the word boundary at or before |i|, and |next_word| holds |
| 322 // the word boundary right after |i|. Advance both |word| and |next_word| | 328 // the word boundary right after |i|. Advance both |word| and |next_word| |
| 323 // when |i| reaches |next_word|. | 329 // when |i| reaches |next_word|. |
| 324 if (next_word != words_->breaks().end() && i >= next_word->first) { | 330 if (next_word != words_->breaks().end() && i >= next_word->first) { |
| 331 if (*width > available_width) { | |
| 332 DCHECK(word_wrap_behavior_ == IGNORE_LONG_WORDS || | |
|
msw
2015/03/26 19:36:30
nit: maybe just DCHECK_NE WRAP_LONG_WORDS?
Jun Mukai
2015/03/26 22:47:38
Done.
| |
| 333 word_wrap_behavior_ == TRUNCATE_LONG_WORDS); | |
| 334 *next_char = i; | |
| 335 if (!truncated) | |
| 336 *end_char = *next_char; | |
| 337 return true; | |
| 338 } | |
| 325 word = next_word++; | 339 word = next_word++; |
| 326 word_width = 0; | 340 word_width = 0; |
| 327 } | 341 } |
| 328 | 342 |
| 329 Range glyph_range; | 343 Range glyph_range; |
| 330 run.GetClusterAt(i, &char_range, &glyph_range); | 344 run.GetClusterAt(i, &char_range, &glyph_range); |
| 331 DCHECK_LT(0U, char_range.length()); | 345 DCHECK_LT(0U, char_range.length()); |
| 332 | 346 |
| 333 SkScalar char_width = ((glyph_range.end() >= run.glyph_count) | 347 SkScalar char_width = ((glyph_range.end() >= run.glyph_count) |
| 334 ? SkFloatToScalar(run.width) | 348 ? SkFloatToScalar(run.width) |
| 335 : run.positions[glyph_range.end()].x()) - | 349 : run.positions[glyph_range.end()].x()) - |
| 336 run.positions[glyph_range.start()].x(); | 350 run.positions[glyph_range.start()].x(); |
| 337 | 351 |
| 338 *width += char_width; | 352 *width += char_width; |
| 339 word_width += char_width; | 353 word_width += char_width; |
| 340 | 354 |
| 355 // TODO(mukai): implement ELIDE_LONG_WORDS. | |
| 341 if (*width > available_width) { | 356 if (*width > available_width) { |
| 342 if (line_x_ != 0 || word_width < *width) { | 357 if (line_x_ != 0 || word_width < *width) { |
| 343 // Roll back one word. | 358 // Roll back one word. |
| 344 *width -= word_width; | 359 *width -= word_width; |
| 345 *next_char = std::max(word->first, start_char); | 360 *next_char = std::max(word->first, start_char); |
| 346 } else if (char_width < *width) { | 361 *end_char = *next_char; |
| 347 // Roll back one character. | 362 return true; |
| 348 *width -= char_width; | 363 } else if (word_wrap_behavior_ == TRUNCATE_LONG_WORDS) { |
| 349 *next_char = i; | 364 *end_char = i; |
|
msw
2015/03/26 19:36:30
Shouldn't end_char be set as soon as the width exc
Jun Mukai
2015/03/26 22:47:38
You are right, "!truncated" is necessary in the co
msw
2015/03/26 23:05:54
Hmm, if we only set "*end_char = i;" if (*width <=
Jun Mukai
2015/03/27 00:31:58
Aagh, I see. Sorry for my poor understanding, now
| |
| 350 } else { | 365 truncated = true; |
| 351 // Continue from the next character. | 366 } else if (word_wrap_behavior_ == WRAP_LONG_WORDS) { |
| 352 *next_char = i + char_range.length(); | 367 if (char_width < *width) { |
| 368 // Roll back one character. | |
| 369 *width -= char_width; | |
| 370 *next_char = i; | |
| 371 } else { | |
| 372 // Continue from the next character. | |
| 373 *next_char = i + char_range.length(); | |
| 374 } | |
| 375 *end_char = *next_char; | |
| 376 return true; | |
| 353 } | 377 } |
| 354 return true; | |
| 355 } | 378 } |
| 356 } | 379 } |
| 357 | 380 |
| 358 *next_char = run.range.end(); | 381 *next_char = run.range.end(); |
| 382 if (!truncated) | |
| 383 *end_char = *next_char; | |
| 359 return false; | 384 return false; |
| 360 } | 385 } |
| 361 | 386 |
| 362 // RTL runs are broken in logical order but displayed in visual order. To find | 387 // RTL runs are broken in logical order but displayed in visual order. To find |
| 363 // the text-space coordinate (where it would fall in a single-line text) | 388 // the text-space coordinate (where it would fall in a single-line text) |
| 364 // |x_range| of RTL segments, segment widths are applied in reverse order. | 389 // |x_range| of RTL segments, segment widths are applied in reverse order. |
| 365 // e.g. {[5, 10], [10, 40]} will become {[35, 40], [5, 35]}. | 390 // e.g. {[5, 10], [10, 40]} will become {[35, 40], [5, 35]}. |
| 366 void UpdateRTLSegmentRanges() { | 391 void UpdateRTLSegmentRanges() { |
| 367 if (rtl_segments_.empty()) | 392 if (rtl_segments_.empty()) |
| 368 return; | 393 return; |
| (...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 441 UpdateRTLSegmentRanges(); | 466 UpdateRTLSegmentRanges(); |
| 442 } | 467 } |
| 443 text_x_ += SkFloatToScalar(width); | 468 text_x_ += SkFloatToScalar(width); |
| 444 line_x_ += SkFloatToScalar(width); | 469 line_x_ += SkFloatToScalar(width); |
| 445 } | 470 } |
| 446 | 471 |
| 447 const SkScalar max_width_; | 472 const SkScalar max_width_; |
| 448 const int min_baseline_; | 473 const int min_baseline_; |
| 449 const float min_height_; | 474 const float min_height_; |
| 450 const bool multiline_; | 475 const bool multiline_; |
| 476 const WordWrapBehavior word_wrap_behavior_; | |
| 451 const base::string16& text_; | 477 const base::string16& text_; |
| 452 const BreakList<size_t>* const words_; | 478 const BreakList<size_t>* const words_; |
| 453 const internal::TextRunList& run_list_; | 479 const internal::TextRunList& run_list_; |
| 454 | 480 |
| 455 // Stores the resulting lines. | 481 // Stores the resulting lines. |
| 456 std::vector<internal::Line> lines_; | 482 std::vector<internal::Line> lines_; |
| 457 | 483 |
| 458 // Text space and line space x coordinates of the next segment to be added. | 484 // Text space and line space x coordinates of the next segment to be added. |
| 459 SkScalar text_x_; | 485 SkScalar text_x_; |
| 460 SkScalar line_x_; | 486 SkScalar line_x_; |
| (...skipping 529 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 990 if (lines().empty()) { | 1016 if (lines().empty()) { |
| 991 // TODO(vadimt): Remove ScopedTracker below once crbug.com/431326 is fixed. | 1017 // TODO(vadimt): Remove ScopedTracker below once crbug.com/431326 is fixed. |
| 992 tracked_objects::ScopedTracker tracking_profile2( | 1018 tracked_objects::ScopedTracker tracking_profile2( |
| 993 FROM_HERE_WITH_EXPLICIT_FUNCTION( | 1019 FROM_HERE_WITH_EXPLICIT_FUNCTION( |
| 994 "431326 RenderTextHarfBuzz::EnsureLayout2")); | 1020 "431326 RenderTextHarfBuzz::EnsureLayout2")); |
| 995 | 1021 |
| 996 internal::TextRunList* run_list = GetRunList(); | 1022 internal::TextRunList* run_list = GetRunList(); |
| 997 HarfBuzzLineBreaker line_breaker( | 1023 HarfBuzzLineBreaker line_breaker( |
| 998 display_rect().width(), font_list().GetBaseline(), | 1024 display_rect().width(), font_list().GetBaseline(), |
| 999 std::max(font_list().GetHeight(), min_line_height()), multiline(), | 1025 std::max(font_list().GetHeight(), min_line_height()), multiline(), |
| 1000 GetDisplayText(), multiline() ? &GetLineBreaks() : nullptr, *run_list); | 1026 word_wrap_behavior(), GetDisplayText(), |
| 1027 multiline() ? &GetLineBreaks() : nullptr, *run_list); | |
| 1001 | 1028 |
| 1002 // TODO(vadimt): Remove ScopedTracker below once crbug.com/431326 is fixed. | 1029 // TODO(vadimt): Remove ScopedTracker below once crbug.com/431326 is fixed. |
| 1003 tracked_objects::ScopedTracker tracking_profile3( | 1030 tracked_objects::ScopedTracker tracking_profile3( |
| 1004 FROM_HERE_WITH_EXPLICIT_FUNCTION( | 1031 FROM_HERE_WITH_EXPLICIT_FUNCTION( |
| 1005 "431326 RenderTextHarfBuzz::EnsureLayout3")); | 1032 "431326 RenderTextHarfBuzz::EnsureLayout3")); |
| 1006 | 1033 |
| 1007 for (size_t i = 0; i < run_list->size(); ++i) | 1034 for (size_t i = 0; i < run_list->size(); ++i) |
| 1008 line_breaker.AddRun(i); | 1035 line_breaker.AddRun(i); |
| 1009 std::vector<internal::Line> lines; | 1036 std::vector<internal::Line> lines; |
| 1010 line_breaker.Finalize(&lines, &total_size_); | 1037 line_breaker.Finalize(&lines, &total_size_); |
| (...skipping 514 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1525 DCHECK(!update_layout_run_list_); | 1552 DCHECK(!update_layout_run_list_); |
| 1526 DCHECK(!update_display_run_list_); | 1553 DCHECK(!update_display_run_list_); |
| 1527 return text_elided() ? display_run_list_.get() : &layout_run_list_; | 1554 return text_elided() ? display_run_list_.get() : &layout_run_list_; |
| 1528 } | 1555 } |
| 1529 | 1556 |
| 1530 const internal::TextRunList* RenderTextHarfBuzz::GetRunList() const { | 1557 const internal::TextRunList* RenderTextHarfBuzz::GetRunList() const { |
| 1531 return const_cast<RenderTextHarfBuzz*>(this)->GetRunList(); | 1558 return const_cast<RenderTextHarfBuzz*>(this)->GetRunList(); |
| 1532 } | 1559 } |
| 1533 | 1560 |
| 1534 } // namespace gfx | 1561 } // namespace gfx |
| OLD | NEW |