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| for certain word wrapping behavior. | |
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; |
320 for (size_t i = start_char; i < run.range.end(); i += char_range.length()) { | 325 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 | 326 // |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| | 327 // the word boundary right after |i|. Advance both |word| and |next_word| |
323 // when |i| reaches |next_word|. | 328 // when |i| reaches |next_word|. |
324 if (next_word != words_->breaks().end() && i >= next_word->first) { | 329 if (next_word != words_->breaks().end() && i >= next_word->first) { |
330 if (*width > available_width) { | |
331 DCHECK_NE(WRAP_LONG_WORDS, word_wrap_behavior_); | |
332 *next_char = i; | |
333 if (word_wrap_behavior_ != TRUNCATE_LONG_WORDS) | |
334 *end_char = *next_char; | |
335 return true; | |
336 } | |
325 word = next_word++; | 337 word = next_word++; |
326 word_width = 0; | 338 word_width = 0; |
327 } | 339 } |
328 | 340 |
329 Range glyph_range; | 341 Range glyph_range; |
330 run.GetClusterAt(i, &char_range, &glyph_range); | 342 run.GetClusterAt(i, &char_range, &glyph_range); |
331 DCHECK_LT(0U, char_range.length()); | 343 DCHECK_LT(0U, char_range.length()); |
332 | 344 |
333 SkScalar char_width = ((glyph_range.end() >= run.glyph_count) | 345 SkScalar char_width = ((glyph_range.end() >= run.glyph_count) |
334 ? SkFloatToScalar(run.width) | 346 ? SkFloatToScalar(run.width) |
335 : run.positions[glyph_range.end()].x()) - | 347 : run.positions[glyph_range.end()].x()) - |
336 run.positions[glyph_range.start()].x(); | 348 run.positions[glyph_range.start()].x(); |
337 | 349 |
338 *width += char_width; | 350 *width += char_width; |
339 word_width += char_width; | 351 word_width += char_width; |
340 | 352 |
353 // TODO(mukai): implement ELIDE_LONG_WORDS. | |
341 if (*width > available_width) { | 354 if (*width > available_width) { |
342 if (line_x_ != 0 || word_width < *width) { | 355 if (line_x_ != 0 || word_width < *width) { |
343 // Roll back one word. | 356 // Roll back one word. |
344 *width -= word_width; | 357 *width -= word_width; |
345 *next_char = std::max(word->first, start_char); | 358 *next_char = std::max(word->first, start_char); |
346 } else if (char_width < *width) { | 359 *end_char = *next_char; |
347 // Roll back one character. | 360 return true; |
348 *width -= char_width; | 361 } else if (word_wrap_behavior_ == WRAP_LONG_WORDS) { |
349 *next_char = i; | 362 if (char_width < *width) { |
350 } else { | 363 // Roll back one character. |
351 // Continue from the next character. | 364 *width -= char_width; |
352 *next_char = i + char_range.length(); | 365 *next_char = i; |
366 } else { | |
367 // Continue from the next character. | |
368 *next_char = i + char_range.length(); | |
369 } | |
370 *end_char = *next_char; | |
371 return true; | |
353 } | 372 } |
354 return true; | 373 } else { |
374 *end_char = char_range.end(); | |
355 } | 375 } |
356 } | 376 } |
357 | 377 |
358 *next_char = run.range.end(); | 378 *next_char = run.range.end(); |
379 *end_char = *next_char; | |
msw
2015/03/27 01:45:27
optional nit: "*end_char = *next_char = run.range.
Jun Mukai
2015/03/27 16:36:12
Done.
| |
359 return false; | 380 return false; |
360 } | 381 } |
361 | 382 |
362 // RTL runs are broken in logical order but displayed in visual order. To find | 383 // 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) | 384 // 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. | 385 // |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]}. | 386 // e.g. {[5, 10], [10, 40]} will become {[35, 40], [5, 35]}. |
366 void UpdateRTLSegmentRanges() { | 387 void UpdateRTLSegmentRanges() { |
367 if (rtl_segments_.empty()) | 388 if (rtl_segments_.empty()) |
368 return; | 389 return; |
(...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
441 UpdateRTLSegmentRanges(); | 462 UpdateRTLSegmentRanges(); |
442 } | 463 } |
443 text_x_ += SkFloatToScalar(width); | 464 text_x_ += SkFloatToScalar(width); |
444 line_x_ += SkFloatToScalar(width); | 465 line_x_ += SkFloatToScalar(width); |
445 } | 466 } |
446 | 467 |
447 const SkScalar max_width_; | 468 const SkScalar max_width_; |
448 const int min_baseline_; | 469 const int min_baseline_; |
449 const float min_height_; | 470 const float min_height_; |
450 const bool multiline_; | 471 const bool multiline_; |
472 const WordWrapBehavior word_wrap_behavior_; | |
451 const base::string16& text_; | 473 const base::string16& text_; |
452 const BreakList<size_t>* const words_; | 474 const BreakList<size_t>* const words_; |
453 const internal::TextRunList& run_list_; | 475 const internal::TextRunList& run_list_; |
454 | 476 |
455 // Stores the resulting lines. | 477 // Stores the resulting lines. |
456 std::vector<internal::Line> lines_; | 478 std::vector<internal::Line> lines_; |
457 | 479 |
458 // Text space and line space x coordinates of the next segment to be added. | 480 // Text space and line space x coordinates of the next segment to be added. |
459 SkScalar text_x_; | 481 SkScalar text_x_; |
460 SkScalar line_x_; | 482 SkScalar line_x_; |
(...skipping 529 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
990 if (lines().empty()) { | 1012 if (lines().empty()) { |
991 // TODO(vadimt): Remove ScopedTracker below once crbug.com/431326 is fixed. | 1013 // TODO(vadimt): Remove ScopedTracker below once crbug.com/431326 is fixed. |
992 tracked_objects::ScopedTracker tracking_profile2( | 1014 tracked_objects::ScopedTracker tracking_profile2( |
993 FROM_HERE_WITH_EXPLICIT_FUNCTION( | 1015 FROM_HERE_WITH_EXPLICIT_FUNCTION( |
994 "431326 RenderTextHarfBuzz::EnsureLayout2")); | 1016 "431326 RenderTextHarfBuzz::EnsureLayout2")); |
995 | 1017 |
996 internal::TextRunList* run_list = GetRunList(); | 1018 internal::TextRunList* run_list = GetRunList(); |
997 HarfBuzzLineBreaker line_breaker( | 1019 HarfBuzzLineBreaker line_breaker( |
998 display_rect().width(), font_list().GetBaseline(), | 1020 display_rect().width(), font_list().GetBaseline(), |
999 std::max(font_list().GetHeight(), min_line_height()), multiline(), | 1021 std::max(font_list().GetHeight(), min_line_height()), multiline(), |
1000 GetDisplayText(), multiline() ? &GetLineBreaks() : nullptr, *run_list); | 1022 word_wrap_behavior(), GetDisplayText(), |
1023 multiline() ? &GetLineBreaks() : nullptr, *run_list); | |
1001 | 1024 |
1002 // TODO(vadimt): Remove ScopedTracker below once crbug.com/431326 is fixed. | 1025 // TODO(vadimt): Remove ScopedTracker below once crbug.com/431326 is fixed. |
1003 tracked_objects::ScopedTracker tracking_profile3( | 1026 tracked_objects::ScopedTracker tracking_profile3( |
1004 FROM_HERE_WITH_EXPLICIT_FUNCTION( | 1027 FROM_HERE_WITH_EXPLICIT_FUNCTION( |
1005 "431326 RenderTextHarfBuzz::EnsureLayout3")); | 1028 "431326 RenderTextHarfBuzz::EnsureLayout3")); |
1006 | 1029 |
1007 for (size_t i = 0; i < run_list->size(); ++i) | 1030 for (size_t i = 0; i < run_list->size(); ++i) |
1008 line_breaker.AddRun(i); | 1031 line_breaker.AddRun(i); |
1009 std::vector<internal::Line> lines; | 1032 std::vector<internal::Line> lines; |
1010 line_breaker.Finalize(&lines, &total_size_); | 1033 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_); | 1548 DCHECK(!update_layout_run_list_); |
1526 DCHECK(!update_display_run_list_); | 1549 DCHECK(!update_display_run_list_); |
1527 return text_elided() ? display_run_list_.get() : &layout_run_list_; | 1550 return text_elided() ? display_run_list_.get() : &layout_run_list_; |
1528 } | 1551 } |
1529 | 1552 |
1530 const internal::TextRunList* RenderTextHarfBuzz::GetRunList() const { | 1553 const internal::TextRunList* RenderTextHarfBuzz::GetRunList() const { |
1531 return const_cast<RenderTextHarfBuzz*>(this)->GetRunList(); | 1554 return const_cast<RenderTextHarfBuzz*>(this)->GetRunList(); |
1532 } | 1555 } |
1533 | 1556 |
1534 } // namespace gfx | 1557 } // namespace gfx |
OLD | NEW |