| 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/utf16_indexing.h" | 22 #include "ui/gfx/utf16_indexing.h" |
| 23 | 23 |
| 24 #if defined(OS_WIN) | 24 #if defined(OS_WIN) |
| 25 #include "ui/gfx/font_fallback_win.h" | 25 #include "ui/gfx/font_fallback_win.h" |
| 26 #endif | 26 #endif |
| 27 | 27 |
| 28 using gfx::internal::RangeF; |
| 29 using gfx::internal::RoundRangeF; |
| 30 |
| 28 namespace gfx { | 31 namespace gfx { |
| 29 | 32 |
| 30 namespace { | 33 namespace { |
| 31 | 34 |
| 32 // Text length limit. Longer strings are slow and not fully tested. | 35 // Text length limit. Longer strings are slow and not fully tested. |
| 33 const size_t kMaxTextLength = 10000; | 36 const size_t kMaxTextLength = 10000; |
| 34 | 37 |
| 35 // The maximum number of scripts a Unicode character can belong to. This value | 38 // The maximum number of scripts a Unicode character can belong to. This value |
| 36 // is arbitrarily chosen to be a good limit because it is unlikely for a single | 39 // is arbitrarily chosen to be a good limit because it is unlikely for a single |
| 37 // character to belong to more scripts. | 40 // character to belong to more scripts. |
| (...skipping 416 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 454 DCHECK(!chars->is_reversed()); | 457 DCHECK(!chars->is_reversed()); |
| 455 DCHECK(!chars->is_empty()); | 458 DCHECK(!chars->is_empty()); |
| 456 DCHECK(!glyphs->is_reversed()); | 459 DCHECK(!glyphs->is_reversed()); |
| 457 DCHECK(!glyphs->is_empty()); | 460 DCHECK(!glyphs->is_empty()); |
| 458 } | 461 } |
| 459 | 462 |
| 460 } // namespace | 463 } // namespace |
| 461 | 464 |
| 462 namespace internal { | 465 namespace internal { |
| 463 | 466 |
| 467 Range RoundRangeF(const RangeF& range_f) { |
| 468 return Range(std::floor(range_f.first + 0.5f), |
| 469 std::floor(range_f.second + 0.5f)); |
| 470 } |
| 471 |
| 464 TextRunHarfBuzz::TextRunHarfBuzz() | 472 TextRunHarfBuzz::TextRunHarfBuzz() |
| 465 : width(0.0f), | 473 : width(0.0f), |
| 466 preceding_run_widths(0.0f), | 474 preceding_run_widths(0.0f), |
| 467 is_rtl(false), | 475 is_rtl(false), |
| 468 level(0), | 476 level(0), |
| 469 script(USCRIPT_INVALID_CODE), | 477 script(USCRIPT_INVALID_CODE), |
| 470 glyph_count(static_cast<size_t>(-1)), | 478 glyph_count(static_cast<size_t>(-1)), |
| 471 font_size(0), | 479 font_size(0), |
| 472 font_style(0), | 480 font_style(0), |
| 473 strike(false), | 481 strike(false), |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 515 } | 523 } |
| 516 | 524 |
| 517 size_t TextRunHarfBuzz::CountMissingGlyphs() const { | 525 size_t TextRunHarfBuzz::CountMissingGlyphs() const { |
| 518 static const int kMissingGlyphId = 0; | 526 static const int kMissingGlyphId = 0; |
| 519 size_t missing = 0; | 527 size_t missing = 0; |
| 520 for (size_t i = 0; i < glyph_count; ++i) | 528 for (size_t i = 0; i < glyph_count; ++i) |
| 521 missing += (glyphs[i] == kMissingGlyphId) ? 1 : 0; | 529 missing += (glyphs[i] == kMissingGlyphId) ? 1 : 0; |
| 522 return missing; | 530 return missing; |
| 523 } | 531 } |
| 524 | 532 |
| 525 Range TextRunHarfBuzz::GetGraphemeBounds( | 533 RangeF TextRunHarfBuzz::GetGraphemeBounds( |
| 526 base::i18n::BreakIterator* grapheme_iterator, | 534 base::i18n::BreakIterator* grapheme_iterator, |
| 527 size_t text_index) { | 535 size_t text_index) { |
| 528 DCHECK_LT(text_index, range.end()); | 536 DCHECK_LT(text_index, range.end()); |
| 529 // TODO(msw): Support floating point grapheme bounds. | |
| 530 const int preceding_run_widths_int = SkScalarRoundToInt(preceding_run_widths); | |
| 531 if (glyph_count == 0) | 537 if (glyph_count == 0) |
| 532 return Range(preceding_run_widths_int, preceding_run_widths_int + width); | 538 return RangeF(preceding_run_widths, preceding_run_widths + width); |
| 533 | 539 |
| 534 Range chars; | 540 Range chars; |
| 535 Range glyphs; | 541 Range glyphs; |
| 536 GetClusterAt(text_index, &chars, &glyphs); | 542 GetClusterAt(text_index, &chars, &glyphs); |
| 537 const int cluster_begin_x = SkScalarRoundToInt(positions[glyphs.start()].x()); | 543 const float cluster_begin_x = positions[glyphs.start()].x(); |
| 538 const int cluster_end_x = glyphs.end() < glyph_count ? | 544 const float cluster_end_x = glyphs.end() < glyph_count ? |
| 539 SkScalarRoundToInt(positions[glyphs.end()].x()) : width; | 545 positions[glyphs.end()].x() : SkFloatToScalar(width); |
| 540 | 546 |
| 541 // A cluster consists of a number of code points and corresponds to a number | 547 // A cluster consists of a number of code points and corresponds to a number |
| 542 // of glyphs that should be drawn together. A cluster can contain multiple | 548 // of glyphs that should be drawn together. A cluster can contain multiple |
| 543 // graphemes. In order to place the cursor at a grapheme boundary inside the | 549 // graphemes. In order to place the cursor at a grapheme boundary inside the |
| 544 // cluster, we simply divide the cluster width by the number of graphemes. | 550 // cluster, we simply divide the cluster width by the number of graphemes. |
| 545 if (chars.length() > 1 && grapheme_iterator) { | 551 if (chars.length() > 1 && grapheme_iterator) { |
| 546 int before = 0; | 552 int before = 0; |
| 547 int total = 0; | 553 int total = 0; |
| 548 for (size_t i = chars.start(); i < chars.end(); ++i) { | 554 for (size_t i = chars.start(); i < chars.end(); ++i) { |
| 549 if (grapheme_iterator->IsGraphemeBoundary(i)) { | 555 if (grapheme_iterator->IsGraphemeBoundary(i)) { |
| 550 if (i < text_index) | 556 if (i < text_index) |
| 551 ++before; | 557 ++before; |
| 552 ++total; | 558 ++total; |
| 553 } | 559 } |
| 554 } | 560 } |
| 555 DCHECK_GT(total, 0); | 561 DCHECK_GT(total, 0); |
| 556 if (total > 1) { | 562 if (total > 1) { |
| 557 if (is_rtl) | 563 if (is_rtl) |
| 558 before = total - before - 1; | 564 before = total - before - 1; |
| 559 DCHECK_GE(before, 0); | 565 DCHECK_GE(before, 0); |
| 560 DCHECK_LT(before, total); | 566 DCHECK_LT(before, total); |
| 561 const int cluster_width = cluster_end_x - cluster_begin_x; | 567 const int cluster_width = cluster_end_x - cluster_begin_x; |
| 562 const int grapheme_begin_x = cluster_begin_x + static_cast<int>(0.5f + | 568 const int grapheme_begin_x = cluster_begin_x + static_cast<int>(0.5f + |
| 563 cluster_width * before / static_cast<float>(total)); | 569 cluster_width * before / static_cast<float>(total)); |
| 564 const int grapheme_end_x = cluster_begin_x + static_cast<int>(0.5f + | 570 const int grapheme_end_x = cluster_begin_x + static_cast<int>(0.5f + |
| 565 cluster_width * (before + 1) / static_cast<float>(total)); | 571 cluster_width * (before + 1) / static_cast<float>(total)); |
| 566 return Range(preceding_run_widths_int + grapheme_begin_x, | 572 return RangeF(preceding_run_widths + grapheme_begin_x, |
| 567 preceding_run_widths_int + grapheme_end_x); | 573 preceding_run_widths + grapheme_end_x); |
| 568 } | 574 } |
| 569 } | 575 } |
| 570 | 576 |
| 571 return Range(preceding_run_widths_int + cluster_begin_x, | 577 return RangeF(preceding_run_widths + cluster_begin_x, |
| 572 preceding_run_widths_int + cluster_end_x); | 578 preceding_run_widths + cluster_end_x); |
| 573 } | 579 } |
| 574 | 580 |
| 575 } // namespace internal | 581 } // namespace internal |
| 576 | 582 |
| 577 RenderTextHarfBuzz::RenderTextHarfBuzz() | 583 RenderTextHarfBuzz::RenderTextHarfBuzz() |
| 578 : RenderText(), | 584 : RenderText(), |
| 579 needs_layout_(false) { | 585 needs_layout_(false) { |
| 580 set_truncate_length(kMaxTextLength); | 586 set_truncate_length(kMaxTextLength); |
| 581 } | 587 } |
| 582 | 588 |
| 583 RenderTextHarfBuzz::~RenderTextHarfBuzz() {} | 589 RenderTextHarfBuzz::~RenderTextHarfBuzz() {} |
| 584 | 590 |
| 585 Size RenderTextHarfBuzz::GetStringSize() { | 591 Size RenderTextHarfBuzz::GetStringSize() { |
| 586 const SizeF size_f = GetStringSizeF(); | 592 const SizeF size_f = GetStringSizeF(); |
| 587 return Size(std::ceil(size_f.width()), size_f.height()); | 593 return Size(std::ceil(size_f.width()), size_f.height()); |
| 588 } | 594 } |
| 589 | 595 |
| 590 SizeF RenderTextHarfBuzz::GetStringSizeF() { | 596 SizeF RenderTextHarfBuzz::GetStringSizeF() { |
| 591 EnsureLayout(); | 597 EnsureLayout(); |
| 592 return lines()[0].size; | 598 return lines()[0].size; |
| 593 } | 599 } |
| 594 | 600 |
| 595 SelectionModel RenderTextHarfBuzz::FindCursorPosition(const Point& point) { | 601 SelectionModel RenderTextHarfBuzz::FindCursorPosition(const Point& point) { |
| 596 EnsureLayout(); | 602 EnsureLayout(); |
| 597 | 603 |
| 598 int x = ToTextPoint(point).x(); | 604 int x = ToTextPoint(point).x(); |
| 599 int offset = 0; | 605 float offset = 0; |
| 600 size_t run_index = GetRunContainingXCoord(x, &offset); | 606 size_t run_index = GetRunContainingXCoord(x, &offset); |
| 601 if (run_index >= runs_.size()) | 607 if (run_index >= runs_.size()) |
| 602 return EdgeSelectionModel((x < 0) ? CURSOR_LEFT : CURSOR_RIGHT); | 608 return EdgeSelectionModel((x < 0) ? CURSOR_LEFT : CURSOR_RIGHT); |
| 603 const internal::TextRunHarfBuzz& run = *runs_[run_index]; | 609 const internal::TextRunHarfBuzz& run = *runs_[run_index]; |
| 604 | 610 |
| 605 for (size_t i = 0; i < run.glyph_count; ++i) { | 611 for (size_t i = 0; i < run.glyph_count; ++i) { |
| 606 const SkScalar end = | 612 const SkScalar end = |
| 607 i + 1 == run.glyph_count ? run.width : run.positions[i + 1].x(); | 613 i + 1 == run.glyph_count ? run.width : run.positions[i + 1].x(); |
| 608 const SkScalar middle = (end + run.positions[i].x()) / 2; | 614 const SkScalar middle = (end + run.positions[i].x()) / 2; |
| 609 | 615 |
| (...skipping 29 matching lines...) Expand all Loading... |
| 639 | 645 |
| 640 Range RenderTextHarfBuzz::GetGlyphBounds(size_t index) { | 646 Range RenderTextHarfBuzz::GetGlyphBounds(size_t index) { |
| 641 EnsureLayout(); | 647 EnsureLayout(); |
| 642 const size_t run_index = | 648 const size_t run_index = |
| 643 GetRunContainingCaret(SelectionModel(index, CURSOR_FORWARD)); | 649 GetRunContainingCaret(SelectionModel(index, CURSOR_FORWARD)); |
| 644 // Return edge bounds if the index is invalid or beyond the layout text size. | 650 // Return edge bounds if the index is invalid or beyond the layout text size. |
| 645 if (run_index >= runs_.size()) | 651 if (run_index >= runs_.size()) |
| 646 return Range(GetStringSize().width()); | 652 return Range(GetStringSize().width()); |
| 647 const size_t layout_index = TextIndexToLayoutIndex(index); | 653 const size_t layout_index = TextIndexToLayoutIndex(index); |
| 648 internal::TextRunHarfBuzz* run = runs_[run_index]; | 654 internal::TextRunHarfBuzz* run = runs_[run_index]; |
| 649 Range bounds = run->GetGraphemeBounds(grapheme_iterator_.get(), layout_index); | 655 RangeF bounds = |
| 650 return run->is_rtl ? Range(bounds.end(), bounds.start()) : bounds; | 656 run->GetGraphemeBounds(grapheme_iterator_.get(), layout_index); |
| 657 // If cursor is enabled, extend the last glyph up to the rightmost cursor |
| 658 // position since clients expect them to be contiguous. |
| 659 if (cursor_enabled() && run_index == runs_.size() - 1 && |
| 660 index == (run->is_rtl ? run->range.start() : run->range.end() - 1)) |
| 661 bounds.second = std::ceil(bounds.second); |
| 662 return RoundRangeF(run->is_rtl ? |
| 663 RangeF(bounds.second, bounds.first) : bounds); |
| 651 } | 664 } |
| 652 | 665 |
| 653 int RenderTextHarfBuzz::GetLayoutTextBaseline() { | 666 int RenderTextHarfBuzz::GetLayoutTextBaseline() { |
| 654 EnsureLayout(); | 667 EnsureLayout(); |
| 655 return lines()[0].baseline; | 668 return lines()[0].baseline; |
| 656 } | 669 } |
| 657 | 670 |
| 658 SelectionModel RenderTextHarfBuzz::AdjacentCharSelectionModel( | 671 SelectionModel RenderTextHarfBuzz::AdjacentCharSelectionModel( |
| 659 const SelectionModel& selection, | 672 const SelectionModel& selection, |
| 660 VisualCursorDirection direction) { | 673 VisualCursorDirection direction) { |
| (...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 771 return rects; | 784 return rects; |
| 772 std::vector<Range> bounds; | 785 std::vector<Range> bounds; |
| 773 | 786 |
| 774 // Add a Range for each run/selection intersection. | 787 // Add a Range for each run/selection intersection. |
| 775 for (size_t i = 0; i < runs_.size(); ++i) { | 788 for (size_t i = 0; i < runs_.size(); ++i) { |
| 776 internal::TextRunHarfBuzz* run = runs_[visual_to_logical_[i]]; | 789 internal::TextRunHarfBuzz* run = runs_[visual_to_logical_[i]]; |
| 777 Range intersection = run->range.Intersect(layout_range); | 790 Range intersection = run->range.Intersect(layout_range); |
| 778 if (!intersection.IsValid()) | 791 if (!intersection.IsValid()) |
| 779 continue; | 792 continue; |
| 780 DCHECK(!intersection.is_reversed()); | 793 DCHECK(!intersection.is_reversed()); |
| 781 const Range leftmost_character_x = run->GetGraphemeBounds( | 794 const Range leftmost_character_x = RoundRangeF(run->GetGraphemeBounds( |
| 782 grapheme_iterator_.get(), | 795 grapheme_iterator_.get(), |
| 783 run->is_rtl ? intersection.end() - 1 : intersection.start()); | 796 run->is_rtl ? intersection.end() - 1 : intersection.start())); |
| 784 const Range rightmost_character_x = run->GetGraphemeBounds( | 797 const Range rightmost_character_x = RoundRangeF(run->GetGraphemeBounds( |
| 785 grapheme_iterator_.get(), | 798 grapheme_iterator_.get(), |
| 786 run->is_rtl ? intersection.start() : intersection.end() - 1); | 799 run->is_rtl ? intersection.start() : intersection.end() - 1)); |
| 787 Range range_x(leftmost_character_x.start(), rightmost_character_x.end()); | 800 Range range_x(leftmost_character_x.start(), rightmost_character_x.end()); |
| 788 DCHECK(!range_x.is_reversed()); | 801 DCHECK(!range_x.is_reversed()); |
| 789 if (range_x.is_empty()) | 802 if (range_x.is_empty()) |
| 790 continue; | 803 continue; |
| 791 | 804 |
| 792 // Union this with the last range if they're adjacent. | 805 // Union this with the last range if they're adjacent. |
| 793 DCHECK(bounds.empty() || bounds.back().GetMax() <= range_x.GetMin()); | 806 DCHECK(bounds.empty() || bounds.back().GetMax() <= range_x.GetMin()); |
| 794 if (!bounds.empty() && bounds.back().GetMax() == range_x.GetMin()) { | 807 if (!bounds.empty() && bounds.back().GetMax() == range_x.GetMin()) { |
| 795 range_x = Range(bounds.back().GetMin(), range_x.GetMax()); | 808 range_x = Range(bounds.back().GetMin(), range_x.GetMax()); |
| 796 bounds.pop_back(); | 809 bounds.pop_back(); |
| (...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 904 } | 917 } |
| 905 } | 918 } |
| 906 | 919 |
| 907 void RenderTextHarfBuzz::DrawVisualText(Canvas* canvas) { | 920 void RenderTextHarfBuzz::DrawVisualText(Canvas* canvas) { |
| 908 DCHECK(!needs_layout_); | 921 DCHECK(!needs_layout_); |
| 909 internal::SkiaTextRenderer renderer(canvas); | 922 internal::SkiaTextRenderer renderer(canvas); |
| 910 ApplyFadeEffects(&renderer); | 923 ApplyFadeEffects(&renderer); |
| 911 ApplyTextShadows(&renderer); | 924 ApplyTextShadows(&renderer); |
| 912 ApplyCompositionAndSelectionStyles(); | 925 ApplyCompositionAndSelectionStyles(); |
| 913 | 926 |
| 914 int current_x = 0; | |
| 915 const Vector2d line_offset = GetLineOffset(0); | 927 const Vector2d line_offset = GetLineOffset(0); |
| 916 for (size_t i = 0; i < runs_.size(); ++i) { | 928 for (size_t i = 0; i < runs_.size(); ++i) { |
| 917 const internal::TextRunHarfBuzz& run = *runs_[visual_to_logical_[i]]; | 929 const internal::TextRunHarfBuzz& run = *runs_[visual_to_logical_[i]]; |
| 918 renderer.SetTypeface(run.skia_face.get()); | 930 renderer.SetTypeface(run.skia_face.get()); |
| 919 renderer.SetTextSize(SkIntToScalar(run.font_size)); | 931 renderer.SetTextSize(SkIntToScalar(run.font_size)); |
| 920 renderer.SetFontRenderParams(run.render_params, | 932 renderer.SetFontRenderParams(run.render_params, |
| 921 background_is_transparent()); | 933 background_is_transparent()); |
| 922 | 934 |
| 923 Vector2d origin = line_offset + Vector2d(current_x, lines()[0].baseline); | 935 Vector2d origin = line_offset + Vector2d(0, lines()[0].baseline); |
| 924 scoped_ptr<SkPoint[]> positions(new SkPoint[run.glyph_count]); | 936 scoped_ptr<SkPoint[]> positions(new SkPoint[run.glyph_count]); |
| 925 for (size_t j = 0; j < run.glyph_count; ++j) { | 937 for (size_t j = 0; j < run.glyph_count; ++j) { |
| 926 positions[j] = run.positions[j]; | 938 positions[j] = run.positions[j]; |
| 927 positions[j].offset(SkIntToScalar(origin.x()), SkIntToScalar(origin.y())); | 939 positions[j].offset(SkIntToScalar(origin.x()) + run.preceding_run_widths, |
| 940 SkIntToScalar(origin.y())); |
| 928 } | 941 } |
| 929 | 942 |
| 930 for (BreakList<SkColor>::const_iterator it = | 943 for (BreakList<SkColor>::const_iterator it = |
| 931 colors().GetBreak(run.range.start()); | 944 colors().GetBreak(run.range.start()); |
| 932 it != colors().breaks().end() && it->first < run.range.end(); | 945 it != colors().breaks().end() && it->first < run.range.end(); |
| 933 ++it) { | 946 ++it) { |
| 934 const Range intersection = colors().GetRange(it).Intersect(run.range); | 947 const Range intersection = colors().GetRange(it).Intersect(run.range); |
| 935 const Range colored_glyphs = run.CharRangeToGlyphRange(intersection); | 948 const Range colored_glyphs = run.CharRangeToGlyphRange(intersection); |
| 936 // The range may be empty if a portion of a multi-character grapheme is | 949 // The range may be empty if a portion of a multi-character grapheme is |
| 937 // selected, yielding two colors for a single glyph. For now, this just | 950 // selected, yielding two colors for a single glyph. For now, this just |
| 938 // paints the glyph with a single style, but it should paint it twice, | 951 // paints the glyph with a single style, but it should paint it twice, |
| 939 // clipped according to selection bounds. See http://crbug.com/366786 | 952 // clipped according to selection bounds. See http://crbug.com/366786 |
| 940 if (colored_glyphs.is_empty()) | 953 if (colored_glyphs.is_empty()) |
| 941 continue; | 954 continue; |
| 942 | 955 |
| 943 renderer.SetForegroundColor(it->second); | 956 renderer.SetForegroundColor(it->second); |
| 944 renderer.DrawPosText(&positions[colored_glyphs.start()], | 957 renderer.DrawPosText(&positions[colored_glyphs.start()], |
| 945 &run.glyphs[colored_glyphs.start()], | 958 &run.glyphs[colored_glyphs.start()], |
| 946 colored_glyphs.length()); | 959 colored_glyphs.length()); |
| 947 int start_x = SkScalarRoundToInt(positions[colored_glyphs.start()].x()); | 960 int start_x = SkScalarRoundToInt(positions[colored_glyphs.start()].x()); |
| 948 int end_x = SkScalarRoundToInt((colored_glyphs.end() == run.glyph_count) ? | 961 int end_x = SkScalarRoundToInt((colored_glyphs.end() == run.glyph_count) ? |
| 949 (run.width + SkIntToScalar(origin.x())) : | 962 (run.width + run.preceding_run_widths + SkIntToScalar(origin.x())) : |
| 950 positions[colored_glyphs.end()].x()); | 963 positions[colored_glyphs.end()].x()); |
| 951 renderer.DrawDecorations(start_x, origin.y(), end_x - start_x, | 964 renderer.DrawDecorations(start_x, origin.y(), end_x - start_x, |
| 952 run.underline, run.strike, run.diagonal_strike); | 965 run.underline, run.strike, run.diagonal_strike); |
| 953 } | 966 } |
| 954 | |
| 955 current_x += run.width; | |
| 956 } | 967 } |
| 957 | 968 |
| 958 renderer.EndDiagonalStrike(); | 969 renderer.EndDiagonalStrike(); |
| 959 | 970 |
| 960 UndoCompositionAndSelectionStyles(); | 971 UndoCompositionAndSelectionStyles(); |
| 961 } | 972 } |
| 962 | 973 |
| 963 size_t RenderTextHarfBuzz::GetRunContainingCaret( | 974 size_t RenderTextHarfBuzz::GetRunContainingCaret( |
| 964 const SelectionModel& caret) const { | 975 const SelectionModel& caret) const { |
| 965 DCHECK(!needs_layout_); | 976 DCHECK(!needs_layout_); |
| 966 size_t layout_position = TextIndexToLayoutIndex(caret.caret_pos()); | 977 size_t layout_position = TextIndexToLayoutIndex(caret.caret_pos()); |
| 967 LogicalCursorDirection affinity = caret.caret_affinity(); | 978 LogicalCursorDirection affinity = caret.caret_affinity(); |
| 968 for (size_t run = 0; run < runs_.size(); ++run) { | 979 for (size_t run = 0; run < runs_.size(); ++run) { |
| 969 if (RangeContainsCaret(runs_[run]->range, layout_position, affinity)) | 980 if (RangeContainsCaret(runs_[run]->range, layout_position, affinity)) |
| 970 return run; | 981 return run; |
| 971 } | 982 } |
| 972 return runs_.size(); | 983 return runs_.size(); |
| 973 } | 984 } |
| 974 | 985 |
| 975 size_t RenderTextHarfBuzz::GetRunContainingXCoord(int x, int* offset) const { | 986 size_t RenderTextHarfBuzz::GetRunContainingXCoord(float x, |
| 987 float* offset) const { |
| 976 DCHECK(!needs_layout_); | 988 DCHECK(!needs_layout_); |
| 977 if (x < 0) | 989 if (x < 0) |
| 978 return runs_.size(); | 990 return runs_.size(); |
| 979 // Find the text run containing the argument point (assumed already offset). | 991 // Find the text run containing the argument point (assumed already offset). |
| 980 int current_x = 0; | 992 float current_x = 0; |
| 981 for (size_t i = 0; i < runs_.size(); ++i) { | 993 for (size_t i = 0; i < runs_.size(); ++i) { |
| 982 size_t run = visual_to_logical_[i]; | 994 size_t run = visual_to_logical_[i]; |
| 983 current_x += runs_[run]->width; | 995 current_x += runs_[run]->width; |
| 984 if (x < current_x) { | 996 if (x < current_x) { |
| 985 *offset = x - (current_x - runs_[run]->width); | 997 *offset = x - (current_x - runs_[run]->width); |
| 986 return run; | 998 return run; |
| 987 } | 999 } |
| 988 } | 1000 } |
| 989 return runs_.size(); | 1001 return runs_.size(); |
| 990 } | 1002 } |
| (...skipping 231 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1222 if (!run->render_params.subpixel_positioning) | 1234 if (!run->render_params.subpixel_positioning) |
| 1223 run->width = std::floor(run->width + 0.5f); | 1235 run->width = std::floor(run->width + 0.5f); |
| 1224 } | 1236 } |
| 1225 | 1237 |
| 1226 hb_buffer_destroy(buffer); | 1238 hb_buffer_destroy(buffer); |
| 1227 hb_font_destroy(harfbuzz_font); | 1239 hb_font_destroy(harfbuzz_font); |
| 1228 return true; | 1240 return true; |
| 1229 } | 1241 } |
| 1230 | 1242 |
| 1231 } // namespace gfx | 1243 } // namespace gfx |
| OLD | NEW |