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 <map> | 8 #include <map> |
| 9 | 9 |
| 10 #include "base/i18n/bidi_line_iterator.h" | 10 #include "base/i18n/bidi_line_iterator.h" |
| 11 #include "base/i18n/break_iterator.h" | 11 #include "base/i18n/break_iterator.h" |
| 12 #include "base/i18n/char_iterator.h" | 12 #include "base/i18n/char_iterator.h" |
| 13 #include "base/lazy_instance.h" | 13 #include "base/lazy_instance.h" |
| 14 #include "base/profiler/scoped_tracker.h" | 14 #include "base/profiler/scoped_tracker.h" |
| 15 #include "third_party/harfbuzz-ng/src/hb.h" | 15 #include "third_party/harfbuzz-ng/src/hb.h" |
| 16 #include "third_party/icu/source/common/unicode/ubidi.h" | 16 #include "third_party/icu/source/common/unicode/ubidi.h" |
| 17 #include "third_party/skia/include/core/SkColor.h" | 17 #include "third_party/skia/include/core/SkColor.h" |
| 18 #include "third_party/skia/include/core/SkTypeface.h" | 18 #include "third_party/skia/include/core/SkTypeface.h" |
| 19 #include "ui/gfx/canvas.h" | 19 #include "ui/gfx/canvas.h" |
| 20 #include "ui/gfx/font_fallback.h" | 20 #include "ui/gfx/font_fallback.h" |
| 21 #include "ui/gfx/font_render_params.h" | 21 #include "ui/gfx/font_render_params.h" |
| 22 #include "ui/gfx/geometry/size_conversions.h" | |
| 22 #include "ui/gfx/utf16_indexing.h" | 23 #include "ui/gfx/utf16_indexing.h" |
| 23 | 24 |
| 24 #if defined(OS_WIN) | 25 #if defined(OS_WIN) |
| 25 #include "ui/gfx/font_fallback_win.h" | 26 #include "ui/gfx/font_fallback_win.h" |
| 26 #endif | 27 #endif |
| 27 | 28 |
| 28 using gfx::internal::RangeF; | 29 using gfx::internal::RangeF; |
| 29 using gfx::internal::RoundRangeF; | 30 using gfx::internal::RoundRangeF; |
| 30 | 31 |
| 31 namespace gfx { | 32 namespace gfx { |
| (...skipping 425 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 457 reversed ? elements_end - element : element - elements_begin); | 458 reversed ? elements_end - element : element - elements_begin); |
| 458 if (reversed) | 459 if (reversed) |
| 459 *glyphs = Range(glyphs->end(), glyphs->start()); | 460 *glyphs = Range(glyphs->end(), glyphs->start()); |
| 460 | 461 |
| 461 DCHECK(!chars->is_reversed()); | 462 DCHECK(!chars->is_reversed()); |
| 462 DCHECK(!chars->is_empty()); | 463 DCHECK(!chars->is_empty()); |
| 463 DCHECK(!glyphs->is_reversed()); | 464 DCHECK(!glyphs->is_reversed()); |
| 464 DCHECK(!glyphs->is_empty()); | 465 DCHECK(!glyphs->is_empty()); |
| 465 } | 466 } |
| 466 | 467 |
| 468 // Internal class to generate Line structures. If |multiline| is true, the text | |
| 469 // is broken into lines at |words| boundaries such that each line is no longer | |
| 470 // than |max_width|. If |multiline| is false, only outputs a single Line from | |
| 471 // the given runs. |min_baseline| and |min_height| are the minimum baseline and | |
| 472 // height for each line. | |
| 473 // TODO(ckocagil): Expose the interface of this class in the header and test | |
| 474 // this class directly. | |
| 475 class HarfBuzzLineBreaker { | |
| 476 public: | |
| 477 HarfBuzzLineBreaker(int max_width, | |
| 478 int min_baseline, | |
| 479 int min_height, | |
| 480 bool multiline, | |
| 481 const base::string16& text, | |
| 482 const BreakList<size_t>* words, | |
| 483 const ScopedVector<internal::TextRunHarfBuzz>& runs) | |
| 484 : max_width_((max_width == 0) ? kuint32max : max_width), | |
|
msw
2015/02/04 01:44:18
This constant is deprecated; use std::numeric_limi
Jun Mukai
2015/02/04 23:35:54
Done.
| |
| 485 min_baseline_(min_baseline), | |
| 486 min_height_(min_height), | |
| 487 multiline_(multiline), | |
| 488 text_(text), | |
| 489 words_(words), | |
| 490 runs_(runs), | |
| 491 text_x_(0), | |
| 492 line_x_(0) { | |
| 493 DCHECK_EQ(multiline_, (words_ != nullptr)); | |
| 494 AdvanceLine(); | |
| 495 } | |
| 496 | |
| 497 // Breaks the run at given |run_index| into Line structs. | |
| 498 void AddRun(int run_index) { | |
| 499 const internal::TextRunHarfBuzz* run = runs_[run_index]; | |
| 500 bool run_fits = !multiline_; | |
| 501 if (multiline_ && line_x_ + run->width <= max_width_) { | |
| 502 DCHECK(!run->range.is_empty()); | |
| 503 // Uniscribe always puts newline characters in their own runs. | |
|
msw
2015/02/04 01:44:18
This behavior should be re-checked with ICU/HarfBu
Jun Mukai
2015/02/04 23:35:54
Modified itemizing runs a bit so that runs are spl
| |
| 504 if (!IsNewline(text_[run->range.start()])) | |
| 505 run_fits = true; | |
| 506 } | |
| 507 | |
| 508 if (!run_fits) | |
| 509 BreakRun(run_index); | |
| 510 else | |
| 511 AddSegment(run_index, run->range, run->width); | |
| 512 } | |
| 513 | |
| 514 // Finishes line breaking and outputs the results. Can be called at most once. | |
| 515 void Finalize(std::vector<internal::Line>* lines, Size* size) { | |
| 516 DCHECK(!lines_.empty()); | |
| 517 // Add an empty line to finish the line size calculation and remove it. | |
| 518 AdvanceLine(); | |
| 519 lines_.pop_back(); | |
| 520 *size = total_size_; | |
| 521 lines->swap(lines_); | |
| 522 } | |
| 523 | |
| 524 private: | |
| 525 // A (line index, segment index) pair that specifies a segment in |lines_|. | |
| 526 typedef std::pair<size_t, size_t> SegmentHandle; | |
| 527 | |
| 528 bool IsNewline(base::char16 c) { | |
| 529 return U16_IS_SINGLE(c) && c == static_cast<base::char16>('\n'); | |
| 530 } | |
| 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 DCHECK(words_); | |
| 541 const internal::TextRunHarfBuzz& run = *runs_[run_index]; | |
| 542 int width = 0; | |
| 543 size_t next_char = run.range.start(); | |
| 544 | |
| 545 // Break the run until it fits the current line. | |
| 546 while (next_char < run.range.end()) { | |
| 547 const size_t current_char = next_char; | |
| 548 const bool skip_line = | |
| 549 BreakRunAtWidth(run, current_char, &width, &next_char); | |
| 550 AddSegment(run_index, Range(current_char, next_char), 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 info from |breaks|. If the current | |
| 558 // position is at the beginning of a line, this function will not roll back to | |
| 559 // |start_char| and |*next_char| will be greater than |start_char| (to avoid | |
| 560 // constructing empty lines). | |
| 561 // Returns whether to skip the line before |*next_char|. | |
| 562 // TODO(ckocagil): Do not break ligatures and diacritics. | |
| 563 // TextRun::logical_clusters might help. | |
|
msw
2015/02/04 01:44:18
nit: TextRun::logical_clusters was RenderTextWin s
Jun Mukai
2015/02/04 23:35:54
Done.
| |
| 564 // TODO(ckocagil): We might have to reshape after breaking at ligatures. | |
| 565 // See whether resolving the TODO above resolves this too. | |
| 566 // TODO(ckocagil): Do not reserve width for whitespace at the end of lines. | |
| 567 bool BreakRunAtWidth(const internal::TextRunHarfBuzz& run, | |
| 568 size_t start_char, | |
| 569 int* width, | |
| 570 size_t* next_char) { | |
| 571 DCHECK(words_); | |
| 572 DCHECK(run.range.Contains(Range(start_char, start_char + 1))); | |
| 573 SkScalar available_width = SkIntToScalar(max_width_ - line_x_); | |
| 574 BreakList<size_t>::const_iterator word = words_->GetBreak(start_char); | |
| 575 BreakList<size_t>::const_iterator next_word = word + 1; | |
| 576 // Width from |std::max(word->first, start_char)| to the current character. | |
| 577 SkScalar word_width = 0; | |
| 578 SkScalar width_scalar = 0; | |
|
msw
2015/02/04 01:44:18
Could |width| simply be an SkScalar?
Jun Mukai
2015/02/04 23:35:54
Done.
| |
| 579 *width = 0; | |
| 580 | |
| 581 for (size_t i = start_char; i < run.range.end(); ++i) { | |
| 582 if (IsNewline(text_[i])) { | |
| 583 *next_char = i + 1; | |
| 584 return true; | |
| 585 } | |
| 586 | |
| 587 // |word| holds the word boundary at or before |i|, and |next_word| holds | |
| 588 // the word boundary right after |i|. Advance both |word| and |next_word| | |
| 589 // when |i| reaches |next_word|. | |
| 590 if (next_word != words_->breaks().end() && i >= next_word->first) { | |
| 591 word = next_word++; | |
| 592 word_width = 0; | |
| 593 } | |
| 594 | |
| 595 Range glyph_range = run.CharRangeToGlyphRange(Range(i, i + 1)); | |
| 596 SkScalar char_width = ((glyph_range.end() >= run.glyph_count) | |
| 597 ? SkFloatToScalar(run.width) | |
| 598 : run.positions[glyph_range.end()].x()) - | |
| 599 run.positions[glyph_range.start()].x(); | |
| 600 | |
| 601 width_scalar += char_width; | |
| 602 word_width += char_width; | |
| 603 | |
| 604 if (width_scalar > available_width) { | |
| 605 if (line_x_ != 0 || word_width < width_scalar) { | |
| 606 // Roll back one word. | |
| 607 *width = SkScalarCeilToInt(width_scalar - word_width); | |
| 608 *next_char = std::max(word->first, start_char); | |
| 609 } else if (char_width < width_scalar) { | |
| 610 // Roll back one character. | |
| 611 *width = SkScalarCeilToInt(width_scalar - char_width); | |
| 612 *next_char = i; | |
| 613 } else { | |
| 614 // Continue from the next character. | |
| 615 *width = SkScalarCeilToInt(width_scalar); | |
| 616 *next_char = i + 1; | |
| 617 } | |
| 618 | |
| 619 return true; | |
| 620 } | |
| 621 } | |
| 622 | |
| 623 *width = SkScalarCeilToInt(width_scalar); | |
| 624 *next_char = run.range.end(); | |
| 625 return false; | |
| 626 } | |
| 627 | |
| 628 // RTL runs are broken in logical order but displayed in visual order. To find | |
| 629 // the text-space coordinate (where it would fall in a single-line text) | |
| 630 // |x_range| of RTL segments, segment widths are applied in reverse order. | |
| 631 // e.g. {[5, 10], [10, 40]} will become {[35, 40], [5, 35]}. | |
| 632 void UpdateRTLSegmentRanges() { | |
| 633 if (rtl_segments_.empty()) | |
| 634 return; | |
| 635 int x = SegmentFromHandle(rtl_segments_[0])->x_range.start(); | |
| 636 for (size_t i = rtl_segments_.size(); i > 0; --i) { | |
| 637 internal::LineSegment* segment = SegmentFromHandle(rtl_segments_[i - 1]); | |
| 638 const size_t segment_width = segment->x_range.length(); | |
| 639 segment->x_range = Range(x, x + segment_width); | |
| 640 x += segment_width; | |
| 641 } | |
| 642 rtl_segments_.clear(); | |
| 643 } | |
| 644 | |
| 645 // Finishes the size calculations of the last Line in |lines_|. Adds a new | |
| 646 // Line to the back of |lines_|. | |
| 647 void AdvanceLine() { | |
| 648 if (!lines_.empty()) { | |
| 649 internal::Line* line = &lines_.back(); | |
| 650 line->preceding_heights = total_size_.height(); | |
| 651 const Size line_size(ToCeiledSize(line->size)); | |
| 652 total_size_.set_height(total_size_.height() + line_size.height()); | |
| 653 total_size_.set_width(std::max(total_size_.width(), line_size.width())); | |
| 654 } | |
| 655 line_x_ = 0; | |
| 656 lines_.push_back(internal::Line()); | |
| 657 lines_.back().baseline = min_baseline_; | |
| 658 lines_.back().size.set_height(min_height_); | |
| 659 } | |
| 660 | |
| 661 // Adds a new segment with the given properties to |lines_.back()|. | |
| 662 void AddSegment(int run_index, Range char_range, int width) { | |
| 663 if (char_range.is_empty()) { | |
| 664 DCHECK_EQ(width, 0); | |
| 665 return; | |
| 666 } | |
| 667 const internal::TextRunHarfBuzz& run = *runs_[run_index]; | |
| 668 | |
| 669 internal::LineSegment segment; | |
| 670 segment.run = run_index; | |
| 671 segment.char_range = char_range; | |
| 672 segment.x_range = Range(text_x_, text_x_ + width); | |
| 673 | |
| 674 internal::Line* line = &lines_.back(); | |
| 675 line->segments.push_back(segment); | |
| 676 | |
| 677 SkPaint paint; | |
| 678 paint.setTypeface(run.skia_face.get()); | |
| 679 paint.setTextSize(SkIntToScalar(run.font_size)); | |
| 680 paint.setAntiAlias(run.render_params.antialiasing); | |
| 681 SkPaint::FontMetrics metrics; | |
| 682 paint.getFontMetrics(&metrics); | |
| 683 | |
| 684 line->size.set_width(line->size.width() + segment.x_range.length()); | |
| 685 line->size.set_height( | |
| 686 std::max(line->size.height(), metrics.fDescent - metrics.fAscent)); | |
|
msw
2015/02/04 01:44:18
I think you'll need to retain the logic for tracki
Jun Mukai
2015/02/04 23:35:54
Done.
| |
| 687 line->baseline = | |
| 688 std::max(line->baseline, SkScalarRoundToInt(-metrics.fAscent)); | |
| 689 | |
| 690 if (run.is_rtl) { | |
| 691 rtl_segments_.push_back( | |
| 692 SegmentHandle(lines_.size() - 1, line->segments.size() - 1)); | |
| 693 // If this is the last segment of an RTL run, reprocess the text-space x | |
| 694 // ranges of all segments from the run. | |
| 695 if (char_range.end() == run.range.end()) | |
| 696 UpdateRTLSegmentRanges(); | |
| 697 } | |
| 698 text_x_ += width; | |
| 699 line_x_ += width; | |
| 700 } | |
| 701 | |
| 702 const size_t max_width_; | |
| 703 const int min_baseline_; | |
| 704 const int min_height_; | |
| 705 const bool multiline_; | |
| 706 const base::string16& text_; | |
| 707 const BreakList<size_t>* const words_; | |
| 708 const ScopedVector<internal::TextRunHarfBuzz>& runs_; | |
| 709 | |
| 710 // Stores the resulting lines. | |
| 711 std::vector<internal::Line> lines_; | |
| 712 | |
| 713 // Text space and line space x coordinates of the next segment to be added. | |
| 714 int text_x_; | |
| 715 int line_x_; | |
| 716 | |
| 717 // Size of the multiline text, not including the currently processed line. | |
| 718 Size total_size_; | |
| 719 | |
| 720 // The current RTL run segments, to be applied by |UpdateRTLSegmentRanges()|. | |
| 721 std::vector<SegmentHandle> rtl_segments_; | |
| 722 | |
| 723 DISALLOW_COPY_AND_ASSIGN(HarfBuzzLineBreaker); | |
| 724 }; | |
| 725 | |
| 467 } // namespace | 726 } // namespace |
| 468 | 727 |
| 469 namespace internal { | 728 namespace internal { |
| 470 | 729 |
| 471 Range RoundRangeF(const RangeF& range_f) { | 730 Range RoundRangeF(const RangeF& range_f) { |
| 472 return Range(std::floor(range_f.first + 0.5f), | 731 return Range(std::floor(range_f.first + 0.5f), |
| 473 std::floor(range_f.second + 0.5f)); | 732 std::floor(range_f.second + 0.5f)); |
| 474 } | 733 } |
| 475 | 734 |
| 476 TextRunHarfBuzz::TextRunHarfBuzz() | 735 TextRunHarfBuzz::TextRunHarfBuzz() |
| (...skipping 113 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 590 set_truncate_length(kMaxTextLength); | 849 set_truncate_length(kMaxTextLength); |
| 591 } | 850 } |
| 592 | 851 |
| 593 RenderTextHarfBuzz::~RenderTextHarfBuzz() {} | 852 RenderTextHarfBuzz::~RenderTextHarfBuzz() {} |
| 594 | 853 |
| 595 scoped_ptr<RenderText> RenderTextHarfBuzz::CreateInstanceOfSameType() const { | 854 scoped_ptr<RenderText> RenderTextHarfBuzz::CreateInstanceOfSameType() const { |
| 596 return scoped_ptr<RenderTextHarfBuzz>(new RenderTextHarfBuzz); | 855 return scoped_ptr<RenderTextHarfBuzz>(new RenderTextHarfBuzz); |
| 597 } | 856 } |
| 598 | 857 |
| 599 Size RenderTextHarfBuzz::GetStringSize() { | 858 Size RenderTextHarfBuzz::GetStringSize() { |
| 600 const SizeF size_f = GetStringSizeF(); | 859 return gfx::ToCeiledSize(GetStringSizeF()); |
| 601 return Size(std::ceil(size_f.width()), size_f.height()); | |
| 602 } | 860 } |
| 603 | 861 |
| 604 SizeF RenderTextHarfBuzz::GetStringSizeF() { | 862 SizeF RenderTextHarfBuzz::GetStringSizeF() { |
| 605 EnsureLayout(); | 863 EnsureLayout(); |
| 606 return lines()[0].size; | 864 return total_size_; |
| 607 } | 865 } |
| 608 | 866 |
| 609 SelectionModel RenderTextHarfBuzz::FindCursorPosition(const Point& point) { | 867 SelectionModel RenderTextHarfBuzz::FindCursorPosition(const Point& point) { |
| 610 EnsureLayout(); | 868 EnsureLayout(); |
| 611 | 869 |
| 612 int x = ToTextPoint(point).x(); | 870 int x = ToTextPoint(point).x(); |
| 613 float offset = 0; | 871 float offset = 0; |
| 614 size_t run_index = GetRunContainingXCoord(x, &offset); | 872 size_t run_index = GetRunContainingXCoord(x, &offset); |
| 615 if (run_index >= runs_.size()) | 873 if (run_index >= runs_.size()) |
| 616 return EdgeSelectionModel((x < 0) ? CURSOR_LEFT : CURSOR_RIGHT); | 874 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; | 1178 std::vector<internal::Line> empty_lines; |
| 921 set_lines(&empty_lines); | 1179 set_lines(&empty_lines); |
| 922 } | 1180 } |
| 923 | 1181 |
| 924 if (lines().empty()) { | 1182 if (lines().empty()) { |
| 925 // TODO(vadimt): Remove ScopedTracker below once crbug.com/431326 is fixed. | 1183 // TODO(vadimt): Remove ScopedTracker below once crbug.com/431326 is fixed. |
| 926 tracked_objects::ScopedTracker tracking_profile2( | 1184 tracked_objects::ScopedTracker tracking_profile2( |
| 927 FROM_HERE_WITH_EXPLICIT_FUNCTION( | 1185 FROM_HERE_WITH_EXPLICIT_FUNCTION( |
| 928 "431326 RenderTextHarfBuzz::EnsureLayout2")); | 1186 "431326 RenderTextHarfBuzz::EnsureLayout2")); |
| 929 | 1187 |
| 930 std::vector<internal::Line> lines; | |
| 931 lines.push_back(internal::Line()); | |
| 932 lines[0].baseline = font_list().GetBaseline(); | |
| 933 lines[0].size.set_height(font_list().GetHeight()); | |
| 934 | |
| 935 int current_x = 0; | |
| 936 SkPaint paint; | |
| 937 | |
| 938 // TODO(vadimt): Remove ScopedTracker below once crbug.com/431326 is fixed. | 1188 // TODO(vadimt): Remove ScopedTracker below once crbug.com/431326 is fixed. |
|
msw
2015/02/04 01:44:18
nit: You might be able to collapse EnsureLayout2 a
Jun Mukai
2015/02/04 23:35:54
To keep the consistency with the previous code, I'
| |
| 939 tracked_objects::ScopedTracker tracking_profile3( | 1189 tracked_objects::ScopedTracker tracking_profile3( |
| 940 FROM_HERE_WITH_EXPLICIT_FUNCTION( | 1190 FROM_HERE_WITH_EXPLICIT_FUNCTION( |
| 941 "431326 RenderTextHarfBuzz::EnsureLayout3")); | 1191 "431326 RenderTextHarfBuzz::EnsureLayout3")); |
| 942 | 1192 |
| 943 for (size_t i = 0; i < runs_.size(); ++i) { | 1193 HarfBuzzLineBreaker line_breaker( |
| 944 const internal::TextRunHarfBuzz& run = *runs_[visual_to_logical_[i]]; | 1194 display_rect().width(), font_list().GetBaseline(), |
| 945 internal::LineSegment segment; | 1195 font_list().GetHeight(), multiline(), GetLayoutText(), |
| 946 segment.x_range = Range(current_x, current_x + run.width); | 1196 multiline() ? &GetLineBreaks() : nullptr, runs_); |
| 947 segment.char_range = run.range; | 1197 for (size_t i = 0; i < runs_.size(); ++i) |
| 948 segment.run = i; | 1198 line_breaker.AddRun(visual_to_logical_[i]); |
| 949 lines[0].segments.push_back(segment); | 1199 std::vector<internal::Line> lines; |
| 950 | 1200 line_breaker.Finalize(&lines, &total_size_); |
| 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); | 1201 set_lines(&lines); |
| 965 } | 1202 } |
| 966 } | 1203 } |
| 967 | 1204 |
| 968 void RenderTextHarfBuzz::DrawVisualText(Canvas* canvas) { | 1205 void RenderTextHarfBuzz::DrawVisualText(Canvas* canvas) { |
| 969 DCHECK(!needs_layout_); | 1206 DCHECK(!needs_layout_); |
| 1207 if (lines().empty()) | |
| 1208 return; | |
| 1209 | |
| 970 internal::SkiaTextRenderer renderer(canvas); | 1210 internal::SkiaTextRenderer renderer(canvas); |
| 971 ApplyFadeEffects(&renderer); | 1211 ApplyFadeEffects(&renderer); |
| 972 ApplyTextShadows(&renderer); | 1212 ApplyTextShadows(&renderer); |
| 973 ApplyCompositionAndSelectionStyles(); | 1213 ApplyCompositionAndSelectionStyles(); |
| 974 | 1214 |
| 975 const Vector2d line_offset = GetLineOffset(0); | 1215 Vector2d origin = GetLineOffset(0) + Vector2d(0, lines()[0].baseline); |
|
msw
2015/02/04 01:44:18
nit: you might be able to actually use GetLineOffs
Jun Mukai
2015/02/04 23:35:54
Done.
| |
| 976 for (size_t i = 0; i < runs_.size(); ++i) { | 1216 for (const internal::Line& line : lines()) { |
| 977 const internal::TextRunHarfBuzz& run = *runs_[visual_to_logical_[i]]; | 1217 SkScalar preceding_run_widths = 0; |
|
msw
2015/02/04 01:44:18
nit: rename this |preceding_segment_widths|
Jun Mukai
2015/02/04 23:35:54
Done.
| |
| 978 renderer.SetTypeface(run.skia_face.get()); | 1218 for (const internal::LineSegment& segment : line.segments) { |
| 979 renderer.SetTextSize(SkIntToScalar(run.font_size)); | 1219 const internal::TextRunHarfBuzz& run = *runs_[segment.run]; |
| 980 renderer.SetFontRenderParams(run.render_params, | 1220 renderer.SetTypeface(run.skia_face.get()); |
| 981 background_is_transparent()); | 1221 renderer.SetTextSize(SkIntToScalar(run.font_size)); |
| 1222 renderer.SetFontRenderParams(run.render_params, | |
| 1223 background_is_transparent()); | |
| 1224 scoped_ptr<SkPoint[]> positions(new SkPoint[segment.char_range.length()]); | |
|
msw
2015/02/04 01:44:18
The length of |positions| should correspond to the
Jun Mukai
2015/02/04 23:35:54
Done.
| |
| 1225 size_t segment_start = segment.char_range.start() - run.range.start(); | |
| 1226 SkScalar segment_base_x = run.positions[segment_start].x(); | |
| 1227 for (size_t j = 0; j < segment.char_range.length(); ++j) { | |
| 1228 positions[j] = run.positions[segment_start + j]; | |
|
msw
2015/02/04 01:44:18
Yeah, TextRunHarfBuzz::positions is sized to the n
Jun Mukai
2015/02/04 23:35:54
Done.
| |
| 1229 positions[j].offset( | |
| 1230 SkIntToScalar(origin.x()) - segment_base_x + preceding_run_widths, | |
| 1231 SkIntToScalar(origin.y())); | |
| 1232 } | |
| 1233 for (BreakList<SkColor>::const_iterator it = | |
| 1234 colors().GetBreak(segment.char_range.start()); | |
| 1235 it != colors().breaks().end() && | |
| 1236 it->first < segment.char_range.end(); | |
| 1237 ++it) { | |
| 1238 const Range intersection = | |
| 1239 colors().GetRange(it).Intersect(segment.char_range); | |
| 1240 const Range colored_glyphs = run.CharRangeToGlyphRange(intersection); | |
| 1241 // The range may be empty if a portion of a multi-character grapheme is | |
| 1242 // selected, yielding two colors for a single glyph. For now, this just | |
| 1243 // paints the glyph with a single style, but it should paint it twice, | |
| 1244 // clipped according to selection bounds. See http://crbug.com/366786 | |
| 1245 if (colored_glyphs.is_empty()) | |
| 1246 continue; | |
| 982 | 1247 |
| 983 Vector2d origin = line_offset + Vector2d(0, lines()[0].baseline); | 1248 renderer.SetForegroundColor(it->second); |
| 984 scoped_ptr<SkPoint[]> positions(new SkPoint[run.glyph_count]); | 1249 renderer.DrawPosText(&positions[colored_glyphs.start() - segment_start], |
|
msw
2015/02/04 01:44:18
The |segment_start| here should also be in glyph i
Jun Mukai
2015/02/04 23:35:54
Done.
| |
| 985 for (size_t j = 0; j < run.glyph_count; ++j) { | 1250 &run.glyphs[colored_glyphs.start()], |
| 986 positions[j] = run.positions[j]; | 1251 colored_glyphs.length()); |
| 987 positions[j].offset(SkIntToScalar(origin.x()) + run.preceding_run_widths, | 1252 int start_x = SkScalarRoundToInt(positions[colored_glyphs.start()].x()); |
|
msw
2015/02/04 01:44:18
Should this use the positions[colored_glyphs.start
Jun Mukai
2015/02/04 23:35:54
Done.
| |
| 988 SkIntToScalar(origin.y())); | 1253 int end_x = SkScalarRoundToInt( |
| 1254 (colored_glyphs.end() == run.glyph_count) | |
| 1255 ? (segment.x_range.length() + preceding_run_widths + | |
| 1256 SkIntToScalar(origin.x())) | |
| 1257 : positions[colored_glyphs.end()].x()); | |
|
msw
2015/02/04 01:44:18
Should this use the positions[colored_glyphs.end()
Jun Mukai
2015/02/04 23:35:54
Done.
| |
| 1258 renderer.DrawDecorations(start_x, origin.y(), end_x - start_x, | |
| 1259 run.underline, run.strike, | |
| 1260 run.diagonal_strike); | |
| 1261 } | |
| 1262 preceding_run_widths += SkIntToScalar(segment.x_range.length()); | |
| 989 } | 1263 } |
| 990 | 1264 origin += Vector2d(0, line.size.height()); |
| 991 for (BreakList<SkColor>::const_iterator it = | |
| 992 colors().GetBreak(run.range.start()); | |
| 993 it != colors().breaks().end() && it->first < run.range.end(); | |
| 994 ++it) { | |
| 995 const Range intersection = colors().GetRange(it).Intersect(run.range); | |
| 996 const Range colored_glyphs = run.CharRangeToGlyphRange(intersection); | |
| 997 // The range may be empty if a portion of a multi-character grapheme is | |
| 998 // selected, yielding two colors for a single glyph. For now, this just | |
| 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 } | |
| 1015 } | 1265 } |
| 1016 | 1266 |
| 1017 renderer.EndDiagonalStrike(); | 1267 renderer.EndDiagonalStrike(); |
| 1018 | 1268 |
| 1019 UndoCompositionAndSelectionStyles(); | 1269 UndoCompositionAndSelectionStyles(); |
| 1020 } | 1270 } |
| 1021 | 1271 |
| 1022 size_t RenderTextHarfBuzz::GetRunContainingCaret( | 1272 size_t RenderTextHarfBuzz::GetRunContainingCaret( |
| 1023 const SelectionModel& caret) const { | 1273 const SelectionModel& caret) const { |
| 1024 DCHECK(!needs_layout_); | 1274 DCHECK(!needs_layout_); |
| (...skipping 308 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1333 if (!run->render_params.subpixel_positioning) | 1583 if (!run->render_params.subpixel_positioning) |
| 1334 run->width = std::floor(run->width + 0.5f); | 1584 run->width = std::floor(run->width + 0.5f); |
| 1335 } | 1585 } |
| 1336 | 1586 |
| 1337 hb_buffer_destroy(buffer); | 1587 hb_buffer_destroy(buffer); |
| 1338 hb_font_destroy(harfbuzz_font); | 1588 hb_font_destroy(harfbuzz_font); |
| 1339 return true; | 1589 return true; |
| 1340 } | 1590 } |
| 1341 | 1591 |
| 1342 } // namespace gfx | 1592 } // namespace gfx |
| OLD | NEW |