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 <map> | 7 #include <map> |
8 | 8 |
9 #include "base/debug/leak_annotations.h" | 9 #include "base/debug/leak_annotations.h" |
10 #include "base/i18n/bidi_line_iterator.h" | 10 #include "base/i18n/bidi_line_iterator.h" |
(...skipping 382 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
393 } | 393 } |
394 | 394 |
395 // A port of hb_icu_script_to_script because harfbuzz on CrOS is built without | 395 // A port of hb_icu_script_to_script because harfbuzz on CrOS is built without |
396 // hb-icu. See http://crbug.com/356929 | 396 // hb-icu. See http://crbug.com/356929 |
397 inline hb_script_t ICUScriptToHBScript(UScriptCode script) { | 397 inline hb_script_t ICUScriptToHBScript(UScriptCode script) { |
398 if (script == USCRIPT_INVALID_CODE) | 398 if (script == USCRIPT_INVALID_CODE) |
399 return HB_SCRIPT_INVALID; | 399 return HB_SCRIPT_INVALID; |
400 return hb_script_from_string(uscript_getShortName(script), -1); | 400 return hb_script_from_string(uscript_getShortName(script), -1); |
401 } | 401 } |
402 | 402 |
| 403 // Helper template function for |TextRunHarfBuzz::GetClusterAt()|. |Iterator| |
| 404 // can be a forward or reverse iterator type depending on the text direction. |
| 405 template <class Iterator> |
| 406 void GetClusterAtImpl(size_t pos, |
| 407 Range range, |
| 408 Iterator elements_begin, |
| 409 Iterator elements_end, |
| 410 bool reversed, |
| 411 Range* chars, |
| 412 Range* glyphs) { |
| 413 Iterator element = std::upper_bound(elements_begin, elements_end, pos); |
| 414 chars->set_end(element == elements_end ? range.end() : *element); |
| 415 glyphs->set_end(reversed ? elements_end - element : element - elements_begin); |
| 416 |
| 417 DCHECK(element != elements_begin); |
| 418 while (--element != elements_begin && *element == *(element - 1)); |
| 419 chars->set_start(*element); |
| 420 glyphs->set_start( |
| 421 reversed ? elements_end - element : element - elements_begin); |
| 422 if (reversed) |
| 423 *glyphs = Range(glyphs->end(), glyphs->start()); |
| 424 |
| 425 DCHECK(!chars->is_reversed()); |
| 426 DCHECK(!chars->is_empty()); |
| 427 DCHECK(!glyphs->is_reversed()); |
| 428 DCHECK(!glyphs->is_empty()); |
| 429 } |
| 430 |
403 } // namespace | 431 } // namespace |
404 | 432 |
405 namespace internal { | 433 namespace internal { |
406 | 434 |
407 TextRunHarfBuzz::TextRunHarfBuzz() | 435 TextRunHarfBuzz::TextRunHarfBuzz() |
408 : width(0), | 436 : width(0), |
409 preceding_run_widths(0), | 437 preceding_run_widths(0), |
410 is_rtl(false), | 438 is_rtl(false), |
411 level(0), | 439 level(0), |
412 script(USCRIPT_INVALID_CODE), | 440 script(USCRIPT_INVALID_CODE), |
413 glyph_count(static_cast<size_t>(-1)), | 441 glyph_count(static_cast<size_t>(-1)), |
414 font_size(0), | 442 font_size(0), |
415 font_style(0), | 443 font_style(0), |
416 strike(false), | 444 strike(false), |
417 diagonal_strike(false), | 445 diagonal_strike(false), |
418 underline(false) {} | 446 underline(false) {} |
419 | 447 |
420 TextRunHarfBuzz::~TextRunHarfBuzz() {} | 448 TextRunHarfBuzz::~TextRunHarfBuzz() {} |
421 | 449 |
422 size_t TextRunHarfBuzz::CharToGlyph(size_t pos) const { | 450 void TextRunHarfBuzz::GetClusterAt(size_t pos, |
423 DCHECK(range.start() <= pos && pos < range.end()); | 451 Range* chars, |
| 452 Range* glyphs) const { |
| 453 DCHECK(range.Contains(Range(pos, pos + 1))); |
| 454 DCHECK(chars); |
| 455 DCHECK(glyphs); |
424 | 456 |
425 if (!is_rtl) { | 457 if (is_rtl) { |
426 size_t cluster_start = 0; | 458 GetClusterAtImpl(pos, range, glyph_to_char.rbegin(), glyph_to_char.rend(), |
427 for (size_t i = 1; i < glyph_count && pos >= glyph_to_char[i]; ++i) | 459 true, chars, glyphs); |
428 if (glyph_to_char[i] != glyph_to_char[i - 1]) | 460 return; |
429 cluster_start = i; | |
430 return cluster_start; | |
431 } | 461 } |
432 | 462 |
433 for (size_t i = 0; i < glyph_count; ++i) { | 463 GetClusterAtImpl(pos, range, glyph_to_char.begin(), glyph_to_char.end(), |
434 if (pos >= glyph_to_char[i]) | 464 false, chars, glyphs); |
435 return i; | |
436 } | |
437 NOTREACHED(); | |
438 return 0; | |
439 } | 465 } |
440 | 466 |
441 Range TextRunHarfBuzz::CharRangeToGlyphRange(const Range& char_range) const { | 467 Range TextRunHarfBuzz::CharRangeToGlyphRange(const Range& char_range) const { |
442 DCHECK(range.Contains(char_range)); | 468 DCHECK(range.Contains(char_range)); |
443 DCHECK(!char_range.is_reversed()); | 469 DCHECK(!char_range.is_reversed()); |
444 DCHECK(!char_range.is_empty()); | 470 DCHECK(!char_range.is_empty()); |
445 | 471 |
446 size_t first = 0; | 472 Range start_glyphs; |
447 size_t last = 0; | 473 Range end_glyphs; |
| 474 Range temp_range; |
| 475 GetClusterAt(char_range.start(), &temp_range, &start_glyphs); |
| 476 GetClusterAt(char_range.end() - 1, &temp_range, &end_glyphs); |
448 | 477 |
449 if (is_rtl) { | 478 return is_rtl ? Range(end_glyphs.start(), start_glyphs.end()) : |
450 // For RTL runs, we subtract 1 from |char_range| to get the leading edges. | 479 Range(start_glyphs.start(), end_glyphs.end()); |
451 last = CharToGlyph(char_range.end() - 1); | |
452 // Loop until we find a non-empty glyph range. For multi-character clusters, | |
453 // the loop is needed to find the cluster end. Do the same for LTR below. | |
454 for (size_t i = char_range.start(); i > range.start(); --i) { | |
455 first = CharToGlyph(i - 1); | |
456 if (first != last) | |
457 return Range(last, first); | |
458 } | |
459 return Range(last, glyph_count); | |
460 } | |
461 | |
462 first = CharToGlyph(char_range.start()); | |
463 for (size_t i = char_range.end(); i < range.end(); ++i) { | |
464 last = CharToGlyph(i); | |
465 if (first != last) | |
466 return Range(first, last); | |
467 } | |
468 return Range(first, glyph_count); | |
469 } | 480 } |
470 | 481 |
471 size_t TextRunHarfBuzz::CountMissingGlyphs() const { | 482 size_t TextRunHarfBuzz::CountMissingGlyphs() const { |
472 static const int kMissingGlyphId = 0; | 483 static const int kMissingGlyphId = 0; |
473 size_t missing = 0; | 484 size_t missing = 0; |
474 for (size_t i = 0; i < glyph_count; ++i) | 485 for (size_t i = 0; i < glyph_count; ++i) |
475 missing += (glyphs[i] == kMissingGlyphId) ? 1 : 0; | 486 missing += (glyphs[i] == kMissingGlyphId) ? 1 : 0; |
476 return missing; | 487 return missing; |
477 } | 488 } |
478 | 489 |
479 int TextRunHarfBuzz::GetGlyphXBoundary(size_t text_index, bool trailing) const { | 490 Range TextRunHarfBuzz::GetGraphemeBounds( |
480 if (text_index == range.end()) { | 491 base::i18n::BreakIterator* grapheme_iterator, |
481 trailing = true; | 492 size_t text_index) { |
482 --text_index; | 493 DCHECK_LT(text_index, range.end()); |
| 494 |
| 495 Range chars; |
| 496 Range glyphs; |
| 497 GetClusterAt(text_index, &chars, &glyphs); |
| 498 const int cluster_begin_x = SkScalarRoundToInt(positions[glyphs.start()].x()); |
| 499 const int cluster_end_x = glyphs.end() < glyph_count ? |
| 500 SkScalarRoundToInt(positions[glyphs.end()].x()) : width; |
| 501 |
| 502 // A cluster consists of a number of code points and corresponds to a number |
| 503 // of glyphs that should be drawn together. A cluster can contain multiple |
| 504 // graphemes. In order to place the cursor at a grapheme boundary inside the |
| 505 // cluster, we simply divide the cluster width by the number of graphemes. |
| 506 if (chars.length() > 1 && grapheme_iterator) { |
| 507 int before = 0; |
| 508 int total = 0; |
| 509 for (size_t i = chars.start(); i < chars.end(); ++i) { |
| 510 if (grapheme_iterator->IsGraphemeBoundary(i)) { |
| 511 if (i < text_index) |
| 512 ++before; |
| 513 ++total; |
| 514 } |
| 515 } |
| 516 DCHECK_GT(total, 0); |
| 517 if (total > 1) { |
| 518 if (is_rtl) |
| 519 before = total - before - 1; |
| 520 DCHECK_GE(before, 0); |
| 521 DCHECK_LT(before, total); |
| 522 const int cluster_width = cluster_end_x - cluster_begin_x; |
| 523 const int grapheme_begin_x = cluster_begin_x + static_cast<int>(0.5f + |
| 524 cluster_width * before / static_cast<float>(total)); |
| 525 const int grapheme_end_x = cluster_begin_x + static_cast<int>(0.5f + |
| 526 cluster_width * (before + 1) / static_cast<float>(total)); |
| 527 return Range(preceding_run_widths + grapheme_begin_x, |
| 528 preceding_run_widths + grapheme_end_x); |
| 529 } |
483 } | 530 } |
484 Range glyph_range = CharRangeToGlyphRange(Range(text_index, text_index + 1)); | 531 |
485 const size_t glyph_pos = (is_rtl == trailing) ? | 532 return Range(preceding_run_widths + cluster_begin_x, |
486 glyph_range.start() : glyph_range.end(); | 533 preceding_run_widths + cluster_end_x); |
487 const int x = glyph_pos < glyph_count ? | |
488 SkScalarRoundToInt(positions[glyph_pos].x()) : width; | |
489 return preceding_run_widths + x; | |
490 } | 534 } |
491 | 535 |
492 } // namespace internal | 536 } // namespace internal |
493 | 537 |
494 RenderTextHarfBuzz::RenderTextHarfBuzz() | 538 RenderTextHarfBuzz::RenderTextHarfBuzz() |
495 : RenderText(), | 539 : RenderText(), |
496 needs_layout_(false) {} | 540 needs_layout_(false) {} |
497 | 541 |
498 RenderTextHarfBuzz::~RenderTextHarfBuzz() {} | 542 RenderTextHarfBuzz::~RenderTextHarfBuzz() {} |
499 | 543 |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
537 } | 581 } |
538 | 582 |
539 Range RenderTextHarfBuzz::GetGlyphBounds(size_t index) { | 583 Range RenderTextHarfBuzz::GetGlyphBounds(size_t index) { |
540 EnsureLayout(); | 584 EnsureLayout(); |
541 const size_t run_index = | 585 const size_t run_index = |
542 GetRunContainingCaret(SelectionModel(index, CURSOR_FORWARD)); | 586 GetRunContainingCaret(SelectionModel(index, CURSOR_FORWARD)); |
543 // Return edge bounds if the index is invalid or beyond the layout text size. | 587 // Return edge bounds if the index is invalid or beyond the layout text size. |
544 if (run_index >= runs_.size()) | 588 if (run_index >= runs_.size()) |
545 return Range(GetStringSize().width()); | 589 return Range(GetStringSize().width()); |
546 const size_t layout_index = TextIndexToLayoutIndex(index); | 590 const size_t layout_index = TextIndexToLayoutIndex(index); |
547 return Range(runs_[run_index]->GetGlyphXBoundary(layout_index, false), | 591 internal::TextRunHarfBuzz* run = runs_[run_index]; |
548 runs_[run_index]->GetGlyphXBoundary(layout_index, true)); | 592 Range bounds = run->GetGraphemeBounds(grapheme_iterator_.get(), layout_index); |
| 593 return run->is_rtl ? Range(bounds.end(), bounds.start()) : bounds; |
549 } | 594 } |
550 | 595 |
551 int RenderTextHarfBuzz::GetLayoutTextBaseline() { | 596 int RenderTextHarfBuzz::GetLayoutTextBaseline() { |
552 EnsureLayout(); | 597 EnsureLayout(); |
553 return lines()[0].baseline; | 598 return lines()[0].baseline; |
554 } | 599 } |
555 | 600 |
556 SelectionModel RenderTextHarfBuzz::AdjacentCharSelectionModel( | 601 SelectionModel RenderTextHarfBuzz::AdjacentCharSelectionModel( |
557 const SelectionModel& selection, | 602 const SelectionModel& selection, |
558 VisualCursorDirection direction) { | 603 VisualCursorDirection direction) { |
(...skipping 90 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
649 Range layout_range(TextIndexToLayoutIndex(range.start()), | 694 Range layout_range(TextIndexToLayoutIndex(range.start()), |
650 TextIndexToLayoutIndex(range.end())); | 695 TextIndexToLayoutIndex(range.end())); |
651 DCHECK(Range(0, GetLayoutText().length()).Contains(layout_range)); | 696 DCHECK(Range(0, GetLayoutText().length()).Contains(layout_range)); |
652 | 697 |
653 std::vector<Rect> rects; | 698 std::vector<Rect> rects; |
654 if (layout_range.is_empty()) | 699 if (layout_range.is_empty()) |
655 return rects; | 700 return rects; |
656 std::vector<Range> bounds; | 701 std::vector<Range> bounds; |
657 | 702 |
658 // Add a Range for each run/selection intersection. | 703 // Add a Range for each run/selection intersection. |
659 // TODO(msw): The bounds should probably not always be leading the range ends. | |
660 for (size_t i = 0; i < runs_.size(); ++i) { | 704 for (size_t i = 0; i < runs_.size(); ++i) { |
661 const internal::TextRunHarfBuzz* run = runs_[visual_to_logical_[i]]; | 705 internal::TextRunHarfBuzz* run = runs_[visual_to_logical_[i]]; |
662 Range intersection = run->range.Intersect(layout_range); | 706 Range intersection = run->range.Intersect(layout_range); |
663 if (intersection.IsValid()) { | 707 if (!intersection.IsValid()) |
664 DCHECK(!intersection.is_reversed()); | 708 continue; |
665 Range range_x(run->GetGlyphXBoundary(intersection.start(), false), | 709 DCHECK(!intersection.is_reversed()); |
666 run->GetGlyphXBoundary(intersection.end(), false)); | 710 const Range leftmost_character_x = run->GetGraphemeBounds( |
667 if (range_x.is_empty()) | 711 grapheme_iterator_.get(), |
668 continue; | 712 run->is_rtl ? intersection.end() - 1 : intersection.start()); |
669 range_x = Range(range_x.GetMin(), range_x.GetMax()); | 713 const Range rightmost_character_x = run->GetGraphemeBounds( |
670 // Union this with the last range if they're adjacent. | 714 grapheme_iterator_.get(), |
671 DCHECK(bounds.empty() || bounds.back().GetMax() <= range_x.GetMin()); | 715 run->is_rtl ? intersection.start() : intersection.end() - 1); |
672 if (!bounds.empty() && bounds.back().GetMax() == range_x.GetMin()) { | 716 Range range_x(leftmost_character_x.start(), rightmost_character_x.end()); |
673 range_x = Range(bounds.back().GetMin(), range_x.GetMax()); | 717 DCHECK(!range_x.is_reversed()); |
674 bounds.pop_back(); | 718 if (range_x.is_empty()) |
675 } | 719 continue; |
676 bounds.push_back(range_x); | 720 |
| 721 // Union this with the last range if they're adjacent. |
| 722 DCHECK(bounds.empty() || bounds.back().GetMax() <= range_x.GetMin()); |
| 723 if (!bounds.empty() && bounds.back().GetMax() == range_x.GetMin()) { |
| 724 range_x = Range(bounds.back().GetMin(), range_x.GetMax()); |
| 725 bounds.pop_back(); |
677 } | 726 } |
| 727 bounds.push_back(range_x); |
678 } | 728 } |
679 for (size_t i = 0; i < bounds.size(); ++i) { | 729 for (size_t i = 0; i < bounds.size(); ++i) { |
680 std::vector<Rect> current_rects = TextBoundsToViewBounds(bounds[i]); | 730 std::vector<Rect> current_rects = TextBoundsToViewBounds(bounds[i]); |
681 rects.insert(rects.end(), current_rects.begin(), current_rects.end()); | 731 rects.insert(rects.end(), current_rects.begin(), current_rects.end()); |
682 } | 732 } |
683 return rects; | 733 return rects; |
684 } | 734 } |
685 | 735 |
686 size_t RenderTextHarfBuzz::TextIndexToLayoutIndex(size_t index) const { | 736 size_t RenderTextHarfBuzz::TextIndexToLayoutIndex(size_t index) const { |
687 DCHECK_LE(index, text().length()); | 737 DCHECK_LE(index, text().length()); |
(...skipping 12 matching lines...) Expand all Loading... |
700 DCHECK_LE(text_index, text().length()); | 750 DCHECK_LE(text_index, text().length()); |
701 return text_index; | 751 return text_index; |
702 } | 752 } |
703 | 753 |
704 bool RenderTextHarfBuzz::IsValidCursorIndex(size_t index) { | 754 bool RenderTextHarfBuzz::IsValidCursorIndex(size_t index) { |
705 if (index == 0 || index == text().length()) | 755 if (index == 0 || index == text().length()) |
706 return true; | 756 return true; |
707 if (!IsValidLogicalIndex(index)) | 757 if (!IsValidLogicalIndex(index)) |
708 return false; | 758 return false; |
709 EnsureLayout(); | 759 EnsureLayout(); |
710 // Disallow indices amid multi-character graphemes by checking glyph bounds. | 760 return !grapheme_iterator_ || grapheme_iterator_->IsGraphemeBoundary(index); |
711 // These characters are not surrogate-pairs, but may yield a single glyph: | |
712 // \x0915\x093f - (ki) - one of many Devanagari biconsonantal conjuncts. | |
713 // \x0e08\x0e33 - (cho chan + sara am) - a Thai consonant and vowel pair. | |
714 return GetGlyphBounds(index) != GetGlyphBounds(index - 1); | |
715 } | 761 } |
716 | 762 |
717 void RenderTextHarfBuzz::ResetLayout() { | 763 void RenderTextHarfBuzz::ResetLayout() { |
718 needs_layout_ = true; | 764 needs_layout_ = true; |
719 } | 765 } |
720 | 766 |
721 void RenderTextHarfBuzz::EnsureLayout() { | 767 void RenderTextHarfBuzz::EnsureLayout() { |
722 if (needs_layout_) { | 768 if (needs_layout_) { |
723 runs_.clear(); | 769 runs_.clear(); |
| 770 grapheme_iterator_.reset(); |
724 | 771 |
725 if (!GetLayoutText().empty()) { | 772 if (!GetLayoutText().empty()) { |
| 773 grapheme_iterator_.reset(new base::i18n::BreakIterator(GetLayoutText(), |
| 774 base::i18n::BreakIterator::BREAK_CHARACTER)); |
| 775 if (!grapheme_iterator_->Init()) |
| 776 grapheme_iterator_.reset(); |
| 777 |
726 ItemizeText(); | 778 ItemizeText(); |
727 | 779 |
728 for (size_t i = 0; i < runs_.size(); ++i) | 780 for (size_t i = 0; i < runs_.size(); ++i) |
729 ShapeRun(runs_[i]); | 781 ShapeRun(runs_[i]); |
730 | 782 |
731 // Precalculate run width information. | 783 // Precalculate run width information. |
732 size_t preceding_run_widths = 0; | 784 size_t preceding_run_widths = 0; |
733 for (size_t i = 0; i < runs_.size(); ++i) { | 785 for (size_t i = 0; i < runs_.size(); ++i) { |
734 internal::TextRunHarfBuzz* run = runs_[visual_to_logical_[i]]; | 786 internal::TextRunHarfBuzz* run = runs_[visual_to_logical_[i]]; |
735 run->preceding_run_widths = preceding_run_widths; | 787 run->preceding_run_widths = preceding_run_widths; |
(...skipping 282 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1018 // Shape the text. | 1070 // Shape the text. |
1019 hb_shape(harfbuzz_font, buffer, NULL, 0); | 1071 hb_shape(harfbuzz_font, buffer, NULL, 0); |
1020 | 1072 |
1021 // Populate the run fields with the resulting glyph data in the buffer. | 1073 // Populate the run fields with the resulting glyph data in the buffer. |
1022 unsigned int glyph_count = 0; | 1074 unsigned int glyph_count = 0; |
1023 hb_glyph_info_t* infos = hb_buffer_get_glyph_infos(buffer, &glyph_count); | 1075 hb_glyph_info_t* infos = hb_buffer_get_glyph_infos(buffer, &glyph_count); |
1024 run->glyph_count = glyph_count; | 1076 run->glyph_count = glyph_count; |
1025 hb_glyph_position_t* hb_positions = | 1077 hb_glyph_position_t* hb_positions = |
1026 hb_buffer_get_glyph_positions(buffer, NULL); | 1078 hb_buffer_get_glyph_positions(buffer, NULL); |
1027 run->glyphs.reset(new uint16[run->glyph_count]); | 1079 run->glyphs.reset(new uint16[run->glyph_count]); |
1028 run->glyph_to_char.reset(new uint32[run->glyph_count]); | 1080 run->glyph_to_char.resize(run->glyph_count); |
1029 run->positions.reset(new SkPoint[run->glyph_count]); | 1081 run->positions.reset(new SkPoint[run->glyph_count]); |
1030 run->width = 0; | 1082 run->width = 0; |
1031 for (size_t i = 0; i < run->glyph_count; ++i) { | 1083 for (size_t i = 0; i < run->glyph_count; ++i) { |
1032 run->glyphs[i] = infos[i].codepoint; | 1084 run->glyphs[i] = infos[i].codepoint; |
1033 run->glyph_to_char[i] = infos[i].cluster; | 1085 run->glyph_to_char[i] = infos[i].cluster; |
1034 const int x_offset = | 1086 const int x_offset = |
1035 SkScalarRoundToInt(SkFixedToScalar(hb_positions[i].x_offset)); | 1087 SkScalarRoundToInt(SkFixedToScalar(hb_positions[i].x_offset)); |
1036 const int y_offset = | 1088 const int y_offset = |
1037 SkScalarRoundToInt(SkFixedToScalar(hb_positions[i].y_offset)); | 1089 SkScalarRoundToInt(SkFixedToScalar(hb_positions[i].y_offset)); |
1038 run->positions[i].set(run->width + x_offset, -y_offset); | 1090 run->positions[i].set(run->width + x_offset, -y_offset); |
1039 run->width += | 1091 run->width += |
1040 SkScalarRoundToInt(SkFixedToScalar(hb_positions[i].x_advance)); | 1092 SkScalarRoundToInt(SkFixedToScalar(hb_positions[i].x_advance)); |
1041 } | 1093 } |
1042 | 1094 |
1043 hb_buffer_destroy(buffer); | 1095 hb_buffer_destroy(buffer); |
1044 hb_font_destroy(harfbuzz_font); | 1096 hb_font_destroy(harfbuzz_font); |
1045 } | 1097 } |
1046 | 1098 |
1047 } // namespace gfx | 1099 } // namespace gfx |
OLD | NEW |