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 <map> | 8 #include <map> |
9 | 9 |
10 #include "base/i18n/bidi_line_iterator.h" | 10 #include "base/i18n/bidi_line_iterator.h" |
(...skipping 310 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
321 } | 321 } |
322 | 322 |
323 // Returns the boundary between a special and a regular character. Special | 323 // Returns the boundary between a special and a regular character. Special |
324 // characters are brackets or characters that satisfy |IsUnusualBlockCode|. | 324 // characters are brackets or characters that satisfy |IsUnusualBlockCode|. |
325 size_t FindRunBreakingCharacter(const base::string16& text, | 325 size_t FindRunBreakingCharacter(const base::string16& text, |
326 size_t run_start, | 326 size_t run_start, |
327 size_t run_break) { | 327 size_t run_break) { |
328 const int32 run_length = static_cast<int32>(run_break - run_start); | 328 const int32 run_length = static_cast<int32>(run_break - run_start); |
329 base::i18n::UTF16CharIterator iter(text.c_str() + run_start, run_length); | 329 base::i18n::UTF16CharIterator iter(text.c_str() + run_start, run_length); |
330 const UChar32 first_char = iter.get(); | 330 const UChar32 first_char = iter.get(); |
| 331 // The newline character should form a single run so that the line breaker |
| 332 // can handle them easily. |
| 333 if (first_char == '\n') |
| 334 return run_start + 1; |
| 335 |
331 const UBlockCode first_block = ublock_getCode(first_char); | 336 const UBlockCode first_block = ublock_getCode(first_char); |
332 const bool first_block_unusual = IsUnusualBlockCode(first_block); | 337 const bool first_block_unusual = IsUnusualBlockCode(first_block); |
333 const bool first_bracket = IsBracket(first_char); | 338 const bool first_bracket = IsBracket(first_char); |
334 | 339 |
335 while (iter.Advance() && iter.array_pos() < run_length) { | 340 while (iter.Advance() && iter.array_pos() < run_length) { |
336 const UChar32 current_char = iter.get(); | 341 const UChar32 current_char = iter.get(); |
337 const UBlockCode current_block = ublock_getCode(current_char); | 342 const UBlockCode current_block = ublock_getCode(current_char); |
338 const bool block_break = current_block != first_block && | 343 const bool block_break = current_block != first_block && |
339 (first_block_unusual || IsUnusualBlockCode(current_block)); | 344 (first_block_unusual || IsUnusualBlockCode(current_block)); |
340 if (block_break || first_bracket != IsBracket(current_char)) | 345 if (block_break || current_char == '\n' || |
| 346 first_bracket != IsBracket(current_char)) { |
341 return run_start + iter.array_pos(); | 347 return run_start + iter.array_pos(); |
| 348 } |
342 } | 349 } |
343 return run_break; | 350 return run_break; |
344 } | 351 } |
345 | 352 |
346 // If the given scripts match, returns the one that isn't USCRIPT_COMMON or | 353 // If the given scripts match, returns the one that isn't USCRIPT_COMMON or |
347 // USCRIPT_INHERITED, i.e. the more specific one. Otherwise returns | 354 // USCRIPT_INHERITED, i.e. the more specific one. Otherwise returns |
348 // USCRIPT_INVALID_CODE. | 355 // USCRIPT_INVALID_CODE. |
349 UScriptCode ScriptIntersect(UScriptCode first, UScriptCode second) { | 356 UScriptCode ScriptIntersect(UScriptCode first, UScriptCode second) { |
350 if (first == second || | 357 if (first == second || |
351 (second > USCRIPT_INVALID_CODE && second <= USCRIPT_INHERITED)) { | 358 (second > USCRIPT_INVALID_CODE && second <= USCRIPT_INHERITED)) { |
(...skipping 105 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
457 reversed ? elements_end - element : element - elements_begin); | 464 reversed ? elements_end - element : element - elements_begin); |
458 if (reversed) | 465 if (reversed) |
459 *glyphs = Range(glyphs->end(), glyphs->start()); | 466 *glyphs = Range(glyphs->end(), glyphs->start()); |
460 | 467 |
461 DCHECK(!chars->is_reversed()); | 468 DCHECK(!chars->is_reversed()); |
462 DCHECK(!chars->is_empty()); | 469 DCHECK(!chars->is_empty()); |
463 DCHECK(!glyphs->is_reversed()); | 470 DCHECK(!glyphs->is_reversed()); |
464 DCHECK(!glyphs->is_empty()); | 471 DCHECK(!glyphs->is_empty()); |
465 } | 472 } |
466 | 473 |
| 474 // Internal class to generate Line structures. If |multiline| is true, the text |
| 475 // is broken into lines at |words| boundaries such that each line is no longer |
| 476 // than |max_width|. If |multiline| is false, only outputs a single Line from |
| 477 // the given runs. |min_baseline| and |min_height| are the minimum baseline and |
| 478 // height for each line. |
| 479 // TODO(ckocagil): Expose the interface of this class in the header and test |
| 480 // this class directly. |
| 481 class HarfBuzzLineBreaker { |
| 482 public: |
| 483 HarfBuzzLineBreaker(size_t max_width, |
| 484 int min_baseline, |
| 485 float min_height, |
| 486 bool multiline, |
| 487 const base::string16& text, |
| 488 const BreakList<size_t>* words, |
| 489 const ScopedVector<internal::TextRunHarfBuzz>& runs) |
| 490 : max_width_((max_width == 0) ? std::numeric_limits<size_t>::max() |
| 491 : max_width), |
| 492 min_baseline_(min_baseline), |
| 493 min_height_(min_height), |
| 494 multiline_(multiline), |
| 495 text_(text), |
| 496 words_(words), |
| 497 runs_(runs), |
| 498 text_x_(0), |
| 499 line_x_(0), |
| 500 max_descent_(0), |
| 501 max_ascent_(0) { |
| 502 DCHECK_EQ(multiline_, (words_ != nullptr)); |
| 503 AdvanceLine(); |
| 504 } |
| 505 |
| 506 // Breaks the run at given |run_index| into Line structs. |
| 507 void AddRun(int run_index) { |
| 508 const internal::TextRunHarfBuzz* run = runs_[run_index]; |
| 509 base::char16 first_char = text_[run->range.start()]; |
| 510 if (multiline_ && first_char == '\n') |
| 511 AdvanceLine(); |
| 512 else if (multiline_ && (line_x_ + run->width) > max_width_) |
| 513 BreakRun(run_index); |
| 514 else |
| 515 AddSegment(run_index, run->range, run->width); |
| 516 } |
| 517 |
| 518 // Finishes line breaking and outputs the results. Can be called at most once. |
| 519 void Finalize(std::vector<internal::Line>* lines, SizeF* size) { |
| 520 DCHECK(!lines_.empty()); |
| 521 // Add an empty line to finish the line size calculation and remove it. |
| 522 AdvanceLine(); |
| 523 lines_.pop_back(); |
| 524 *size = total_size_; |
| 525 lines->swap(lines_); |
| 526 } |
| 527 |
| 528 private: |
| 529 // A (line index, segment index) pair that specifies a segment in |lines_|. |
| 530 typedef std::pair<size_t, size_t> SegmentHandle; |
| 531 |
| 532 internal::LineSegment* SegmentFromHandle(const SegmentHandle& handle) { |
| 533 return &lines_[handle.first].segments[handle.second]; |
| 534 } |
| 535 |
| 536 // Breaks a run into segments that fit in the last line in |lines_| and adds |
| 537 // them. Adds a new Line to the back of |lines_| whenever a new segment can't |
| 538 // be added without the Line's width exceeding |max_width_|. |
| 539 void BreakRun(int run_index) { |
| 540 const internal::TextRunHarfBuzz& run = *runs_[run_index]; |
| 541 SkScalar width = 0; |
| 542 size_t next_char = run.range.start(); |
| 543 |
| 544 // Break the run until it fits the current line. |
| 545 while (next_char < run.range.end()) { |
| 546 const size_t current_char = next_char; |
| 547 const bool skip_line = |
| 548 BreakRunAtWidth(run, current_char, &width, &next_char); |
| 549 AddSegment(run_index, Range(current_char, next_char), |
| 550 SkScalarToFloat(width)); |
| 551 if (skip_line) |
| 552 AdvanceLine(); |
| 553 } |
| 554 } |
| 555 |
| 556 // Starting from |start_char|, finds a suitable line break position at or |
| 557 // before available width using word break. If the current position is at the |
| 558 // beginning of a line, this function will not roll back to |start_char| and |
| 559 // |*next_char| will be greater than |start_char| (to avoid constructing empty |
| 560 // lines). |
| 561 // Returns whether to skip the line before |*next_char|. |
| 562 // TODO(ckocagil): Check clusters to avoid breaking ligatures and diacritics. |
| 563 // TODO(ckocagil): We might have to reshape after breaking at ligatures. |
| 564 // See whether resolving the TODO above resolves this too. |
| 565 // TODO(ckocagil): Do not reserve width for whitespace at the end of lines. |
| 566 bool BreakRunAtWidth(const internal::TextRunHarfBuzz& run, |
| 567 size_t start_char, |
| 568 SkScalar* width, |
| 569 size_t* next_char) { |
| 570 DCHECK(words_); |
| 571 DCHECK(run.range.Contains(Range(start_char, start_char + 1))); |
| 572 SkScalar available_width = SkIntToScalar(max_width_ - line_x_); |
| 573 BreakList<size_t>::const_iterator word = words_->GetBreak(start_char); |
| 574 BreakList<size_t>::const_iterator next_word = word + 1; |
| 575 // Width from |std::max(word->first, start_char)| to the current character. |
| 576 SkScalar word_width = 0; |
| 577 *width = 0; |
| 578 |
| 579 for (size_t i = start_char; i < run.range.end(); ++i) { |
| 580 // |word| holds the word boundary at or before |i|, and |next_word| holds |
| 581 // the word boundary right after |i|. Advance both |word| and |next_word| |
| 582 // when |i| reaches |next_word|. |
| 583 if (next_word != words_->breaks().end() && i >= next_word->first) { |
| 584 word = next_word++; |
| 585 word_width = 0; |
| 586 } |
| 587 |
| 588 Range glyph_range = run.CharRangeToGlyphRange(Range(i, i + 1)); |
| 589 SkScalar char_width = ((glyph_range.end() >= run.glyph_count) |
| 590 ? SkFloatToScalar(run.width) |
| 591 : run.positions[glyph_range.end()].x()) - |
| 592 run.positions[glyph_range.start()].x(); |
| 593 |
| 594 *width += char_width; |
| 595 word_width += char_width; |
| 596 |
| 597 if (*width > available_width) { |
| 598 if (line_x_ != 0 || word_width < *width) { |
| 599 // Roll back one word. |
| 600 *width -= word_width; |
| 601 *next_char = std::max(word->first, start_char); |
| 602 } else if (char_width < *width) { |
| 603 // Roll back one character. |
| 604 *width -= char_width; |
| 605 *next_char = i; |
| 606 } else { |
| 607 // Continue from the next character. |
| 608 *next_char = i + 1; |
| 609 } |
| 610 return true; |
| 611 } |
| 612 } |
| 613 |
| 614 *next_char = run.range.end(); |
| 615 return false; |
| 616 } |
| 617 |
| 618 // RTL runs are broken in logical order but displayed in visual order. To find |
| 619 // the text-space coordinate (where it would fall in a single-line text) |
| 620 // |x_range| of RTL segments, segment widths are applied in reverse order. |
| 621 // e.g. {[5, 10], [10, 40]} will become {[35, 40], [5, 35]}. |
| 622 void UpdateRTLSegmentRanges() { |
| 623 if (rtl_segments_.empty()) |
| 624 return; |
| 625 int x = SegmentFromHandle(rtl_segments_[0])->x_range.start(); |
| 626 for (size_t i = rtl_segments_.size(); i > 0; --i) { |
| 627 internal::LineSegment* segment = SegmentFromHandle(rtl_segments_[i - 1]); |
| 628 const size_t segment_width = segment->x_range.length(); |
| 629 segment->x_range = Range(x, x + segment_width); |
| 630 x += segment_width; |
| 631 } |
| 632 rtl_segments_.clear(); |
| 633 } |
| 634 |
| 635 // Finishes the size calculations of the last Line in |lines_|. Adds a new |
| 636 // Line to the back of |lines_|. |
| 637 void AdvanceLine() { |
| 638 if (!lines_.empty()) { |
| 639 internal::Line* line = &lines_.back(); |
| 640 line->size.set_height(std::max(min_height_, max_descent_ + max_ascent_)); |
| 641 line->baseline = |
| 642 std::max(min_baseline_, SkScalarRoundToInt(max_ascent_)); |
| 643 line->preceding_heights = std::ceil(total_size_.height()); |
| 644 total_size_.set_height(total_size_.height() + line->size.height()); |
| 645 total_size_.set_width(std::max(total_size_.width(), line->size.width())); |
| 646 } |
| 647 max_descent_ = 0; |
| 648 max_ascent_ = 0; |
| 649 line_x_ = 0; |
| 650 lines_.push_back(internal::Line()); |
| 651 } |
| 652 |
| 653 // Adds a new segment with the given properties to |lines_.back()|. |
| 654 void AddSegment(int run_index, Range char_range, float width) { |
| 655 if (char_range.is_empty()) { |
| 656 DCHECK_EQ(0, width); |
| 657 return; |
| 658 } |
| 659 const internal::TextRunHarfBuzz& run = *runs_[run_index]; |
| 660 |
| 661 internal::LineSegment segment; |
| 662 segment.run = run_index; |
| 663 segment.char_range = char_range; |
| 664 segment.x_range = Range(text_x_, text_x_ + width); |
| 665 |
| 666 internal::Line* line = &lines_.back(); |
| 667 line->segments.push_back(segment); |
| 668 |
| 669 SkPaint paint; |
| 670 paint.setTypeface(run.skia_face.get()); |
| 671 paint.setTextSize(SkIntToScalar(run.font_size)); |
| 672 paint.setAntiAlias(run.render_params.antialiasing); |
| 673 SkPaint::FontMetrics metrics; |
| 674 paint.getFontMetrics(&metrics); |
| 675 |
| 676 line->size.set_width(line->size.width() + width); |
| 677 max_descent_ = std::max(max_descent_, metrics.fDescent); |
| 678 // fAscent is always negative. |
| 679 max_ascent_ = std::max(max_ascent_, -metrics.fAscent); |
| 680 |
| 681 if (run.is_rtl) { |
| 682 rtl_segments_.push_back( |
| 683 SegmentHandle(lines_.size() - 1, line->segments.size() - 1)); |
| 684 // If this is the last segment of an RTL run, reprocess the text-space x |
| 685 // ranges of all segments from the run. |
| 686 if (char_range.end() == run.range.end()) |
| 687 UpdateRTLSegmentRanges(); |
| 688 } |
| 689 text_x_ += width; |
| 690 line_x_ += width; |
| 691 } |
| 692 |
| 693 const size_t max_width_; |
| 694 const int min_baseline_; |
| 695 const float min_height_; |
| 696 const bool multiline_; |
| 697 const base::string16& text_; |
| 698 const BreakList<size_t>* const words_; |
| 699 const ScopedVector<internal::TextRunHarfBuzz>& runs_; |
| 700 |
| 701 // Stores the resulting lines. |
| 702 std::vector<internal::Line> lines_; |
| 703 |
| 704 // Text space and line space x coordinates of the next segment to be added. |
| 705 int text_x_; |
| 706 int line_x_; |
| 707 |
| 708 float max_descent_; |
| 709 float max_ascent_; |
| 710 |
| 711 // Size of the multiline text, not including the currently processed line. |
| 712 SizeF total_size_; |
| 713 |
| 714 // The current RTL run segments, to be applied by |UpdateRTLSegmentRanges()|. |
| 715 std::vector<SegmentHandle> rtl_segments_; |
| 716 |
| 717 DISALLOW_COPY_AND_ASSIGN(HarfBuzzLineBreaker); |
| 718 }; |
| 719 |
467 } // namespace | 720 } // namespace |
468 | 721 |
469 namespace internal { | 722 namespace internal { |
470 | 723 |
471 Range RoundRangeF(const RangeF& range_f) { | 724 Range RoundRangeF(const RangeF& range_f) { |
472 return Range(std::floor(range_f.first + 0.5f), | 725 return Range(std::floor(range_f.first + 0.5f), |
473 std::floor(range_f.second + 0.5f)); | 726 std::floor(range_f.second + 0.5f)); |
474 } | 727 } |
475 | 728 |
476 TextRunHarfBuzz::TextRunHarfBuzz() | 729 TextRunHarfBuzz::TextRunHarfBuzz() |
(...skipping 102 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
579 } | 832 } |
580 | 833 |
581 return RangeF(preceding_run_widths + cluster_begin_x, | 834 return RangeF(preceding_run_widths + cluster_begin_x, |
582 preceding_run_widths + cluster_end_x); | 835 preceding_run_widths + cluster_end_x); |
583 } | 836 } |
584 | 837 |
585 } // namespace internal | 838 } // namespace internal |
586 | 839 |
587 RenderTextHarfBuzz::RenderTextHarfBuzz() | 840 RenderTextHarfBuzz::RenderTextHarfBuzz() |
588 : RenderText(), | 841 : RenderText(), |
589 needs_layout_(false) { | 842 needs_layout_(false), |
| 843 glyph_width_for_test_(0u) { |
590 set_truncate_length(kMaxTextLength); | 844 set_truncate_length(kMaxTextLength); |
591 } | 845 } |
592 | 846 |
593 RenderTextHarfBuzz::~RenderTextHarfBuzz() {} | 847 RenderTextHarfBuzz::~RenderTextHarfBuzz() {} |
594 | 848 |
595 scoped_ptr<RenderText> RenderTextHarfBuzz::CreateInstanceOfSameType() const { | 849 scoped_ptr<RenderText> RenderTextHarfBuzz::CreateInstanceOfSameType() const { |
596 return scoped_ptr<RenderTextHarfBuzz>(new RenderTextHarfBuzz); | 850 return scoped_ptr<RenderTextHarfBuzz>(new RenderTextHarfBuzz); |
597 } | 851 } |
598 | 852 |
599 Size RenderTextHarfBuzz::GetStringSize() { | 853 Size RenderTextHarfBuzz::GetStringSize() { |
600 const SizeF size_f = GetStringSizeF(); | 854 const SizeF size_f = GetStringSizeF(); |
601 return Size(std::ceil(size_f.width()), size_f.height()); | 855 return Size(std::ceil(size_f.width()), size_f.height()); |
602 } | 856 } |
603 | 857 |
604 SizeF RenderTextHarfBuzz::GetStringSizeF() { | 858 SizeF RenderTextHarfBuzz::GetStringSizeF() { |
605 EnsureLayout(); | 859 EnsureLayout(); |
606 return lines()[0].size; | 860 return total_size_; |
607 } | 861 } |
608 | 862 |
609 SelectionModel RenderTextHarfBuzz::FindCursorPosition(const Point& point) { | 863 SelectionModel RenderTextHarfBuzz::FindCursorPosition(const Point& point) { |
610 EnsureLayout(); | 864 EnsureLayout(); |
611 | 865 |
612 int x = ToTextPoint(point).x(); | 866 int x = ToTextPoint(point).x(); |
613 float offset = 0; | 867 float offset = 0; |
614 size_t run_index = GetRunContainingXCoord(x, &offset); | 868 size_t run_index = GetRunContainingXCoord(x, &offset); |
615 if (run_index >= runs_.size()) | 869 if (run_index >= runs_.size()) |
616 return EdgeSelectionModel((x < 0) ? CURSOR_LEFT : CURSOR_RIGHT); | 870 return EdgeSelectionModel((x < 0) ? CURSOR_LEFT : CURSOR_RIGHT); |
(...skipping 303 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
920 std::vector<internal::Line> empty_lines; | 1174 std::vector<internal::Line> empty_lines; |
921 set_lines(&empty_lines); | 1175 set_lines(&empty_lines); |
922 } | 1176 } |
923 | 1177 |
924 if (lines().empty()) { | 1178 if (lines().empty()) { |
925 // TODO(vadimt): Remove ScopedTracker below once crbug.com/431326 is fixed. | 1179 // TODO(vadimt): Remove ScopedTracker below once crbug.com/431326 is fixed. |
926 tracked_objects::ScopedTracker tracking_profile2( | 1180 tracked_objects::ScopedTracker tracking_profile2( |
927 FROM_HERE_WITH_EXPLICIT_FUNCTION( | 1181 FROM_HERE_WITH_EXPLICIT_FUNCTION( |
928 "431326 RenderTextHarfBuzz::EnsureLayout2")); | 1182 "431326 RenderTextHarfBuzz::EnsureLayout2")); |
929 | 1183 |
930 std::vector<internal::Line> lines; | 1184 HarfBuzzLineBreaker line_breaker( |
931 lines.push_back(internal::Line()); | 1185 display_rect().width(), font_list().GetBaseline(), |
932 lines[0].baseline = font_list().GetBaseline(); | 1186 font_list().GetHeight(), multiline(), GetLayoutText(), |
933 lines[0].size.set_height(font_list().GetHeight()); | 1187 multiline() ? &GetLineBreaks() : nullptr, runs_); |
934 | |
935 int current_x = 0; | |
936 SkPaint paint; | |
937 | 1188 |
938 // TODO(vadimt): Remove ScopedTracker below once crbug.com/431326 is fixed. | 1189 // TODO(vadimt): Remove ScopedTracker below once crbug.com/431326 is fixed. |
939 tracked_objects::ScopedTracker tracking_profile3( | 1190 tracked_objects::ScopedTracker tracking_profile3( |
940 FROM_HERE_WITH_EXPLICIT_FUNCTION( | 1191 FROM_HERE_WITH_EXPLICIT_FUNCTION( |
941 "431326 RenderTextHarfBuzz::EnsureLayout3")); | 1192 "431326 RenderTextHarfBuzz::EnsureLayout3")); |
942 | 1193 |
943 for (size_t i = 0; i < runs_.size(); ++i) { | 1194 for (size_t i = 0; i < runs_.size(); ++i) |
944 const internal::TextRunHarfBuzz& run = *runs_[visual_to_logical_[i]]; | 1195 line_breaker.AddRun(visual_to_logical_[i]); |
945 internal::LineSegment segment; | 1196 std::vector<internal::Line> lines; |
946 segment.x_range = Range(current_x, current_x + run.width); | 1197 line_breaker.Finalize(&lines, &total_size_); |
947 segment.char_range = run.range; | |
948 segment.run = i; | |
949 lines[0].segments.push_back(segment); | |
950 | |
951 paint.setTypeface(run.skia_face.get()); | |
952 paint.setTextSize(SkIntToScalar(run.font_size)); | |
953 paint.setAntiAlias(run.render_params.antialiasing); | |
954 SkPaint::FontMetrics metrics; | |
955 paint.getFontMetrics(&metrics); | |
956 | |
957 lines[0].size.set_width(lines[0].size.width() + run.width); | |
958 lines[0].size.set_height(std::max(lines[0].size.height(), | |
959 metrics.fDescent - metrics.fAscent)); | |
960 lines[0].baseline = std::max(lines[0].baseline, | |
961 SkScalarRoundToInt(-metrics.fAscent)); | |
962 } | |
963 | |
964 set_lines(&lines); | 1198 set_lines(&lines); |
965 } | 1199 } |
966 } | 1200 } |
967 | 1201 |
968 void RenderTextHarfBuzz::DrawVisualText(Canvas* canvas) { | 1202 void RenderTextHarfBuzz::DrawVisualText(Canvas* canvas) { |
969 DCHECK(!needs_layout_); | 1203 DCHECK(!needs_layout_); |
| 1204 if (lines().empty()) |
| 1205 return; |
| 1206 |
970 internal::SkiaTextRenderer renderer(canvas); | 1207 internal::SkiaTextRenderer renderer(canvas); |
971 ApplyFadeEffects(&renderer); | 1208 ApplyFadeEffects(&renderer); |
972 ApplyTextShadows(&renderer); | 1209 ApplyTextShadows(&renderer); |
973 ApplyCompositionAndSelectionStyles(); | 1210 ApplyCompositionAndSelectionStyles(); |
974 | 1211 |
975 const Vector2d line_offset = GetLineOffset(0); | 1212 for (size_t i = 0; i < lines().size(); ++i) { |
976 for (size_t i = 0; i < runs_.size(); ++i) { | 1213 const internal::Line& line = lines()[i]; |
977 const internal::TextRunHarfBuzz& run = *runs_[visual_to_logical_[i]]; | 1214 const Vector2d origin = GetLineOffset(i) + Vector2d(0, line.baseline); |
978 renderer.SetTypeface(run.skia_face.get()); | 1215 SkScalar preceding_segment_widths = 0; |
979 renderer.SetTextSize(SkIntToScalar(run.font_size)); | 1216 for (const internal::LineSegment& segment : line.segments) { |
980 renderer.SetFontRenderParams(run.render_params, | 1217 const internal::TextRunHarfBuzz& run = *runs_[segment.run]; |
981 background_is_transparent()); | 1218 renderer.SetTypeface(run.skia_face.get()); |
| 1219 renderer.SetTextSize(SkIntToScalar(run.font_size)); |
| 1220 renderer.SetFontRenderParams(run.render_params, |
| 1221 background_is_transparent()); |
| 1222 Range glyphs_range = run.CharRangeToGlyphRange(segment.char_range); |
| 1223 scoped_ptr<SkPoint[]> positions(new SkPoint[glyphs_range.length()]); |
| 1224 SkScalar offset_x = |
| 1225 preceding_segment_widths - run.positions[glyphs_range.start()].x(); |
| 1226 for (size_t j = 0; j < glyphs_range.length(); ++j) { |
| 1227 positions[j] = run.positions[glyphs_range.start() + |
| 1228 (glyphs_range.is_reversed() ? -j : j)]; |
| 1229 positions[j].offset(SkIntToScalar(origin.x()) + offset_x, |
| 1230 SkIntToScalar(origin.y())); |
| 1231 } |
| 1232 for (BreakList<SkColor>::const_iterator it = |
| 1233 colors().GetBreak(segment.char_range.start()); |
| 1234 it != colors().breaks().end() && |
| 1235 it->first < segment.char_range.end(); |
| 1236 ++it) { |
| 1237 const Range intersection = |
| 1238 colors().GetRange(it).Intersect(segment.char_range); |
| 1239 const Range colored_glyphs = run.CharRangeToGlyphRange(intersection); |
| 1240 // The range may be empty if a portion of a multi-character grapheme is |
| 1241 // selected, yielding two colors for a single glyph. For now, this just |
| 1242 // paints the glyph with a single style, but it should paint it twice, |
| 1243 // clipped according to selection bounds. See http://crbug.com/366786 |
| 1244 if (colored_glyphs.is_empty()) |
| 1245 continue; |
982 | 1246 |
983 Vector2d origin = line_offset + Vector2d(0, lines()[0].baseline); | 1247 renderer.SetForegroundColor(it->second); |
984 scoped_ptr<SkPoint[]> positions(new SkPoint[run.glyph_count]); | 1248 renderer.DrawPosText( |
985 for (size_t j = 0; j < run.glyph_count; ++j) { | 1249 &positions[colored_glyphs.start() - glyphs_range.start()], |
986 positions[j] = run.positions[j]; | 1250 &run.glyphs[colored_glyphs.start()], colored_glyphs.length()); |
987 positions[j].offset(SkIntToScalar(origin.x()) + run.preceding_run_widths, | 1251 int start_x = SkScalarRoundToInt( |
988 SkIntToScalar(origin.y())); | 1252 positions[colored_glyphs.start() - glyphs_range.start()].x()); |
989 } | 1253 int end_x = SkScalarRoundToInt( |
990 | 1254 (colored_glyphs.end() == run.glyph_count) |
991 for (BreakList<SkColor>::const_iterator it = | 1255 ? (segment.x_range.length() + preceding_segment_widths + |
992 colors().GetBreak(run.range.start()); | 1256 SkIntToScalar(origin.x())) |
993 it != colors().breaks().end() && it->first < run.range.end(); | 1257 : positions[colored_glyphs.end() - glyphs_range.start()].x()); |
994 ++it) { | 1258 renderer.DrawDecorations(start_x, origin.y(), end_x - start_x, |
995 const Range intersection = colors().GetRange(it).Intersect(run.range); | 1259 run.underline, run.strike, |
996 const Range colored_glyphs = run.CharRangeToGlyphRange(intersection); | 1260 run.diagonal_strike); |
997 // The range may be empty if a portion of a multi-character grapheme is | 1261 } |
998 // selected, yielding two colors for a single glyph. For now, this just | 1262 preceding_segment_widths += SkIntToScalar(segment.x_range.length()); |
999 // paints the glyph with a single style, but it should paint it twice, | |
1000 // clipped according to selection bounds. See http://crbug.com/366786 | |
1001 if (colored_glyphs.is_empty()) | |
1002 continue; | |
1003 | |
1004 renderer.SetForegroundColor(it->second); | |
1005 renderer.DrawPosText(&positions[colored_glyphs.start()], | |
1006 &run.glyphs[colored_glyphs.start()], | |
1007 colored_glyphs.length()); | |
1008 int start_x = SkScalarRoundToInt(positions[colored_glyphs.start()].x()); | |
1009 int end_x = SkScalarRoundToInt((colored_glyphs.end() == run.glyph_count) ? | |
1010 (run.width + run.preceding_run_widths + SkIntToScalar(origin.x())) : | |
1011 positions[colored_glyphs.end()].x()); | |
1012 renderer.DrawDecorations(start_x, origin.y(), end_x - start_x, | |
1013 run.underline, run.strike, run.diagonal_strike); | |
1014 } | 1263 } |
1015 } | 1264 } |
1016 | 1265 |
1017 renderer.EndDiagonalStrike(); | 1266 renderer.EndDiagonalStrike(); |
1018 | 1267 |
1019 UndoCompositionAndSelectionStyles(); | 1268 UndoCompositionAndSelectionStyles(); |
1020 } | 1269 } |
1021 | 1270 |
1022 size_t RenderTextHarfBuzz::GetRunContainingCaret( | 1271 size_t RenderTextHarfBuzz::GetRunContainingCaret( |
1023 const SelectionModel& caret) const { | 1272 const SelectionModel& caret) const { |
(...skipping 297 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1321 FROM_HERE_WITH_EXPLICIT_FUNCTION( | 1570 FROM_HERE_WITH_EXPLICIT_FUNCTION( |
1322 "431326 RenderTextHarfBuzz::ShapeRunWithFont3")); | 1571 "431326 RenderTextHarfBuzz::ShapeRunWithFont3")); |
1323 | 1572 |
1324 for (size_t i = 0; i < run->glyph_count; ++i) { | 1573 for (size_t i = 0; i < run->glyph_count; ++i) { |
1325 DCHECK_LE(infos[i].codepoint, std::numeric_limits<uint16>::max()); | 1574 DCHECK_LE(infos[i].codepoint, std::numeric_limits<uint16>::max()); |
1326 run->glyphs[i] = static_cast<uint16>(infos[i].codepoint); | 1575 run->glyphs[i] = static_cast<uint16>(infos[i].codepoint); |
1327 run->glyph_to_char[i] = infos[i].cluster; | 1576 run->glyph_to_char[i] = infos[i].cluster; |
1328 const SkScalar x_offset = SkFixedToScalar(hb_positions[i].x_offset); | 1577 const SkScalar x_offset = SkFixedToScalar(hb_positions[i].x_offset); |
1329 const SkScalar y_offset = SkFixedToScalar(hb_positions[i].y_offset); | 1578 const SkScalar y_offset = SkFixedToScalar(hb_positions[i].y_offset); |
1330 run->positions[i].set(run->width + x_offset, -y_offset); | 1579 run->positions[i].set(run->width + x_offset, -y_offset); |
1331 run->width += SkFixedToScalar(hb_positions[i].x_advance); | 1580 run->width += (glyph_width_for_test_ > 0) |
| 1581 ? SkIntToScalar(glyph_width_for_test_) |
| 1582 : SkFixedToScalar(hb_positions[i].x_advance); |
1332 // Round run widths if subpixel positioning is off to match native behavior. | 1583 // Round run widths if subpixel positioning is off to match native behavior. |
1333 if (!run->render_params.subpixel_positioning) | 1584 if (!run->render_params.subpixel_positioning) |
1334 run->width = std::floor(run->width + 0.5f); | 1585 run->width = std::floor(run->width + 0.5f); |
1335 } | 1586 } |
1336 | 1587 |
1337 hb_buffer_destroy(buffer); | 1588 hb_buffer_destroy(buffer); |
1338 hb_font_destroy(harfbuzz_font); | 1589 hb_font_destroy(harfbuzz_font); |
1339 return true; | 1590 return true; |
1340 } | 1591 } |
1341 | 1592 |
1342 } // namespace gfx | 1593 } // namespace gfx |
OLD | NEW |