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 <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 381 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 392 } | 392 } |
| 393 | 393 |
| 394 // A port of hb_icu_script_to_script because harfbuzz on CrOS is built without | 394 // A port of hb_icu_script_to_script because harfbuzz on CrOS is built without |
| 395 // hb-icu. See http://crbug.com/356929 | 395 // hb-icu. See http://crbug.com/356929 |
| 396 inline hb_script_t ICUScriptToHBScript(UScriptCode script) { | 396 inline hb_script_t ICUScriptToHBScript(UScriptCode script) { |
| 397 if (script == USCRIPT_INVALID_CODE) | 397 if (script == USCRIPT_INVALID_CODE) |
| 398 return HB_SCRIPT_INVALID; | 398 return HB_SCRIPT_INVALID; |
| 399 return hb_script_from_string(uscript_getShortName(script), -1); | 399 return hb_script_from_string(uscript_getShortName(script), -1); |
| 400 } | 400 } |
| 401 | 401 |
| 402 template <class Iterator> | |
| 403 void GetClusterAtImpl(size_t pos, | |
| 404 Range range, | |
| 405 Iterator elements_begin, | |
| 406 Iterator elements_end, | |
| 407 bool reversed, | |
| 408 Range* chars, | |
| 409 Range* glyphs) { | |
| 410 Iterator element = std::upper_bound(elements_begin, elements_end, pos); | |
| 411 chars->set_end(element == elements_end ? range.end() : *element); | |
| 412 glyphs->set_end(reversed ? elements_end - element : element - elements_begin); | |
| 413 | |
| 414 DCHECK(element != elements_begin); | |
|
msw
2014/06/29 22:29:09
nit: can you DCHECK_NE?
ckocagil
2014/07/24 21:46:56
It doesn't compile with DCHECK_NE due to some temp
msw
2014/07/25 00:36:59
Acknowledged.
| |
| 415 do { | |
| 416 --element; | |
|
msw
2014/06/29 22:29:09
nit: maybe just make this a one-liner "while(--ele
ckocagil
2014/07/24 21:46:56
Done.
| |
| 417 } while (element != elements_begin && *element == element[-1]); | |
|
msw
2014/06/29 22:29:09
nit: Checking for |element[-1]| is a bit odd, can
ckocagil
2014/07/24 21:46:55
Done.
| |
| 418 chars->set_start(*element); | |
| 419 glyphs->set_start(reversed ? | |
|
msw
2014/06/29 22:29:10
nit: move "reversed ?" to the next line.
ckocagil
2014/07/24 21:46:55
Done.
| |
| 420 elements_end - element : element - elements_begin); | |
| 421 if (reversed) | |
| 422 *glyphs = Range(glyphs->end(), glyphs->start()); | |
| 423 | |
| 424 DCHECK(!chars->is_reversed()); | |
| 425 DCHECK(!chars->is_empty()); | |
| 426 DCHECK(!glyphs->is_reversed()); | |
| 427 DCHECK(!glyphs->is_empty()); | |
| 428 } | |
| 429 | |
| 402 } // namespace | 430 } // namespace |
| 403 | 431 |
| 404 namespace internal { | 432 namespace internal { |
| 405 | 433 |
| 406 TextRunHarfBuzz::TextRunHarfBuzz() | 434 TextRunHarfBuzz::TextRunHarfBuzz() |
| 407 : width(0), | 435 : width(0), |
| 408 preceding_run_widths(0), | 436 preceding_run_widths(0), |
| 409 is_rtl(false), | 437 is_rtl(false), |
| 410 level(0), | 438 level(0), |
| 411 script(USCRIPT_INVALID_CODE), | 439 script(USCRIPT_INVALID_CODE), |
| 412 glyph_count(-1), | 440 glyph_count(-1), |
| 413 font_size(0), | 441 font_size(0), |
| 414 font_style(0), | 442 font_style(0), |
| 415 strike(false), | 443 strike(false), |
| 416 diagonal_strike(false), | 444 diagonal_strike(false), |
| 417 underline(false) {} | 445 underline(false) {} |
| 418 | 446 |
| 419 TextRunHarfBuzz::~TextRunHarfBuzz() {} | 447 TextRunHarfBuzz::~TextRunHarfBuzz() {} |
| 420 | 448 |
| 421 size_t TextRunHarfBuzz::CharToGlyph(size_t pos) const { | 449 void TextRunHarfBuzz::GetClusterAt(size_t pos, |
|
msw
2014/06/29 22:29:09
Hmm, It might make more sense to just offer GetCha
ckocagil
2014/07/24 21:46:55
But in the cases where we need both, we'd have to
msw
2014/07/25 00:37:00
That's fair; it's a careful balance between perf a
| |
| 422 DCHECK(range.start() <= pos && pos < range.end()); | 450 Range* chars, |
| 451 Range* glyphs) const { | |
| 452 DCHECK(range.Contains(Range(pos, pos + 1))); | |
| 423 | 453 |
| 424 if (!is_rtl) { | 454 scoped_ptr<Range> temp_chars; |
| 425 size_t cluster_start = 0; | 455 scoped_ptr<Range> temp_glyphs; |
| 426 for (size_t i = 1; i < glyph_count && pos >= glyph_to_char[i]; ++i) | 456 if (!chars) { |
| 427 if (glyph_to_char[i] != glyph_to_char[i - 1]) | 457 temp_chars.reset(new Range); |
| 428 cluster_start = i; | 458 chars = temp_chars.get(); |
| 429 return cluster_start; | 459 } |
| 460 if (!glyphs) { | |
| 461 temp_glyphs.reset(new Range); | |
| 462 glyphs = temp_glyphs.get(); | |
| 430 } | 463 } |
| 431 | 464 |
| 432 for (size_t i = 0; i < glyph_count; ++i) { | 465 if (is_rtl) { |
| 433 if (pos >= glyph_to_char[i]) | 466 GetClusterAtImpl(pos, range, glyph_to_char.rbegin(), glyph_to_char.rend(), |
| 434 return i; | 467 true, chars, glyphs); |
| 468 return; | |
| 435 } | 469 } |
| 436 NOTREACHED(); | 470 |
| 437 return 0; | 471 GetClusterAtImpl(pos, range, glyph_to_char.begin(), glyph_to_char.end(), |
| 472 false, chars, glyphs); | |
| 438 } | 473 } |
| 439 | 474 |
| 440 Range TextRunHarfBuzz::CharRangeToGlyphRange(const Range& char_range) const { | 475 Range TextRunHarfBuzz::CharRangeToGlyphRange(const Range& char_range) const { |
| 441 DCHECK(range.Contains(char_range)); | 476 DCHECK(range.Contains(char_range)); |
| 442 DCHECK(!char_range.is_reversed()); | 477 DCHECK(!char_range.is_reversed()); |
| 443 DCHECK(!char_range.is_empty()); | 478 DCHECK(!char_range.is_empty()); |
| 444 | 479 |
| 445 size_t first = 0; | 480 Range start_glyphs; |
| 446 size_t last = 0; | 481 Range end_glyphs; |
| 482 GetClusterAt(char_range.start(), NULL, &start_glyphs); | |
| 483 GetClusterAt(char_range.end() - 1, NULL, &end_glyphs); | |
| 447 | 484 |
| 448 if (is_rtl) { | 485 return is_rtl ? Range(end_glyphs.start(), start_glyphs.end()) : |
| 449 // For RTL runs, we subtract 1 from |char_range| to get the leading edges. | 486 Range(start_glyphs.start(), end_glyphs.end()); |
| 450 last = CharToGlyph(char_range.end() - 1); | |
| 451 // Loop until we find a non-empty glyph range. For multi-character clusters, | |
| 452 // the loop is needed to find the cluster end. Do the same for LTR below. | |
| 453 for (size_t i = char_range.start(); i > range.start(); --i) { | |
| 454 first = CharToGlyph(i - 1); | |
| 455 if (first != last) | |
| 456 return Range(last, first); | |
| 457 } | |
| 458 return Range(last, glyph_count); | |
| 459 } | |
| 460 | |
| 461 first = CharToGlyph(char_range.start()); | |
| 462 for (size_t i = char_range.end(); i < range.end(); ++i) { | |
| 463 last = CharToGlyph(i); | |
| 464 if (first != last) | |
| 465 return Range(first, last); | |
| 466 } | |
| 467 return Range(first, glyph_count); | |
| 468 } | 487 } |
| 469 | 488 |
| 470 // Returns whether the given shaped run contains any missing glyphs. | 489 // Returns whether the given shaped run contains any missing glyphs. |
| 471 bool TextRunHarfBuzz::HasMissingGlyphs() const { | 490 bool TextRunHarfBuzz::HasMissingGlyphs() const { |
| 472 static const int kMissingGlyphId = 0; | 491 static const int kMissingGlyphId = 0; |
| 473 for (size_t i = 0; i < glyph_count; ++i) { | 492 for (size_t i = 0; i < glyph_count; ++i) { |
| 474 if (glyphs[i] == kMissingGlyphId) | 493 if (glyphs[i] == kMissingGlyphId) |
| 475 return true; | 494 return true; |
| 476 } | 495 } |
| 477 return false; | 496 return false; |
| 478 } | 497 } |
| 479 | 498 |
| 480 int TextRunHarfBuzz::GetGlyphXBoundary(size_t text_index, bool trailing) const { | 499 Range TextRunHarfBuzz::GetGraphemeBounds(const base::string16& text, |
|
msw
2014/06/29 22:29:09
Perhaps GetGraphemeBounds could also be a RenderTe
ckocagil
2014/07/24 21:46:56
I actually want to move more logic to TextRunHarfB
msw
2014/07/25 00:37:00
Acknowledged.
| |
| 481 if (text_index == range.end()) { | 500 size_t text_index) { |
| 482 trailing = true; | 501 DCHECK_LT(text_index, range.end()); |
| 483 --text_index; | 502 |
| 503 Range chars; | |
| 504 Range glyphs; | |
| 505 GetClusterAt(text_index, &chars, &glyphs); | |
| 506 const int cluster_begin_x = SkScalarRoundToInt(positions[glyphs.start()].x()); | |
| 507 const int cluster_end_x = glyphs.end() < glyph_count ? | |
| 508 SkScalarRoundToInt(positions[glyphs.end()].x()) : width; | |
| 509 | |
| 510 if (chars.length() != 1) { | |
|
msw
2014/06/29 22:29:09
This block needs a comment about the bounds treatm
ckocagil
2014/07/24 21:46:56
Done.
| |
| 511 base::i18n::BreakIterator* grapheme_iterator = GetGraphemeIterator(text); | |
| 512 if (grapheme_iterator) { | |
| 513 int before = 0; | |
| 514 int total = 0; | |
| 515 for (size_t i = chars.start(); i < chars.end(); ++i) { | |
|
msw
2014/06/29 22:29:09
Should we instead calculate and store all the grap
ckocagil
2014/07/24 21:46:56
It seems like ICU is caching bounds internally. Ev
msw
2014/07/25 00:36:59
Acknowledged.
| |
| 516 if (grapheme_iterator->IsGraphemeBoundary(i - range.start())) { | |
| 517 if (i < text_index) | |
| 518 ++before; | |
| 519 ++total; | |
| 520 } | |
| 521 } | |
| 522 | |
| 523 if (is_rtl) | |
| 524 before = total - before - 1; | |
| 525 if (total <= 1) { | |
|
msw
2014/06/29 22:29:10
Could total ever really be 0? (DCHECK against that
ckocagil
2014/07/24 21:46:56
You're right, I added a DCHECK.
| |
| 526 return Range(preceding_run_widths + cluster_begin_x, | |
|
msw
2014/06/29 22:29:09
Optionally reverse the conditional to execute the
ckocagil
2014/07/24 21:46:56
Done.
| |
| 527 preceding_run_widths + cluster_end_x); | |
| 528 } | |
| 529 DCHECK_GE(before, 0); | |
|
msw
2014/06/29 22:29:09
Wouldn't this be false if the index was the first
ckocagil
2014/07/24 21:46:56
I don't understand the question. Perhaps you read
msw
2014/07/25 00:37:00
Yeah, I think you're right, ignore that question.
| |
| 530 DCHECK_LE(before, total); | |
| 531 | |
| 532 const int cluster_width = cluster_end_x - cluster_begin_x; | |
| 533 int grapheme_begin_x = cluster_begin_x; | |
| 534 if (before > 0) { | |
| 535 grapheme_begin_x += static_cast<int>(0.5f + | |
| 536 cluster_width * before / static_cast<float>(total)); | |
| 537 } | |
| 538 int grapheme_end_x = cluster_end_x; | |
| 539 if (before != total - 1) { | |
| 540 grapheme_end_x = cluster_begin_x + static_cast<int>(0.5f + | |
| 541 cluster_width * (before + 1) / static_cast<float>(total)); | |
| 542 } | |
| 543 return Range(preceding_run_widths + grapheme_begin_x, | |
| 544 preceding_run_widths + grapheme_end_x); | |
| 545 } | |
| 484 } | 546 } |
| 485 Range glyph_range = CharRangeToGlyphRange(Range(text_index, text_index + 1)); | 547 |
| 486 const size_t glyph_pos = (is_rtl == trailing) ? | 548 return Range(preceding_run_widths + cluster_begin_x, |
| 487 glyph_range.start() : glyph_range.end(); | 549 preceding_run_widths + cluster_end_x); |
| 488 const int x = glyph_pos < glyph_count ? | 550 } |
| 489 SkScalarRoundToInt(positions[glyph_pos].x()) : width; | 551 |
| 490 return preceding_run_widths + x; | 552 base::i18n::BreakIterator* TextRunHarfBuzz::GetGraphemeIterator( |
|
msw
2014/06/29 22:29:09
It seems like the RenderText should have an iterat
ckocagil
2014/07/24 21:46:55
Done.
| |
| 553 const base::string16& text) { | |
| 554 if (!grapheme_iterator.get()) { | |
| 555 grapheme_iterator.reset(new base::i18n::BreakIterator( | |
| 556 base::string16(), base::i18n::BreakIterator::BREAK_CHARACTER)); | |
| 557 if (!grapheme_iterator->Init() || !grapheme_iterator->SetText( | |
| 558 text.c_str() + range.start(), range.length())) { | |
| 559 NOTREACHED(); | |
| 560 return NULL; | |
| 561 } | |
| 562 } | |
| 563 return grapheme_iterator.get(); | |
| 491 } | 564 } |
| 492 | 565 |
| 493 } // namespace internal | 566 } // namespace internal |
| 494 | 567 |
| 495 RenderTextHarfBuzz::RenderTextHarfBuzz() | 568 RenderTextHarfBuzz::RenderTextHarfBuzz() |
| 496 : RenderText(), | 569 : RenderText(), |
| 497 needs_layout_(false) {} | 570 needs_layout_(false) {} |
| 498 | 571 |
| 499 RenderTextHarfBuzz::~RenderTextHarfBuzz() {} | 572 RenderTextHarfBuzz::~RenderTextHarfBuzz() {} |
| 500 | 573 |
| (...skipping 125 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 626 break; | 699 break; |
| 627 } | 700 } |
| 628 pos = iter.pos() - iter.GetString().length(); | 701 pos = iter.pos() - iter.GetString().length(); |
| 629 } | 702 } |
| 630 } | 703 } |
| 631 } | 704 } |
| 632 return SelectionModel(pos, CURSOR_FORWARD); | 705 return SelectionModel(pos, CURSOR_FORWARD); |
| 633 } | 706 } |
| 634 | 707 |
| 635 Range RenderTextHarfBuzz::GetGlyphBounds(size_t index) { | 708 Range RenderTextHarfBuzz::GetGlyphBounds(size_t index) { |
| 709 EnsureLayout(); | |
| 636 const size_t run_index = | 710 const size_t run_index = |
| 637 GetRunContainingCaret(SelectionModel(index, CURSOR_FORWARD)); | 711 GetRunContainingCaret(SelectionModel(index, CURSOR_FORWARD)); |
| 638 // Return edge bounds if the index is invalid or beyond the layout text size. | 712 // Return edge bounds if the index is invalid or beyond the layout text size. |
| 639 if (run_index >= runs_.size()) | 713 if (run_index >= runs_.size()) |
| 640 return Range(GetStringSize().width()); | 714 return Range(GetStringSize().width()); |
| 641 const size_t layout_index = TextIndexToLayoutIndex(index); | 715 const size_t layout_index = TextIndexToLayoutIndex(index); |
| 642 return Range(runs_[run_index]->GetGlyphXBoundary(layout_index, false), | 716 internal::TextRunHarfBuzz* run = runs_[run_index]; |
| 643 runs_[run_index]->GetGlyphXBoundary(layout_index, true)); | 717 Range bounds = run->GetGraphemeBounds(GetLayoutText(), layout_index); |
| 718 return run->is_rtl ? Range(bounds.end(), bounds.start()) : bounds; | |
| 644 } | 719 } |
| 645 | 720 |
| 646 std::vector<Rect> RenderTextHarfBuzz::GetSubstringBounds(const Range& range) { | 721 std::vector<Rect> RenderTextHarfBuzz::GetSubstringBounds(const Range& range) { |
| 647 DCHECK(!needs_layout_); | 722 DCHECK(!needs_layout_); |
| 648 DCHECK(Range(0, text().length()).Contains(range)); | 723 DCHECK(Range(0, text().length()).Contains(range)); |
| 649 Range layout_range(TextIndexToLayoutIndex(range.start()), | 724 Range layout_range(TextIndexToLayoutIndex(range.start()), |
| 650 TextIndexToLayoutIndex(range.end())); | 725 TextIndexToLayoutIndex(range.end())); |
| 651 DCHECK(Range(0, GetLayoutText().length()).Contains(layout_range)); | 726 DCHECK(Range(0, GetLayoutText().length()).Contains(layout_range)); |
| 652 | 727 |
| 653 std::vector<Rect> rects; | 728 std::vector<Rect> rects; |
| 654 if (layout_range.is_empty()) | 729 if (layout_range.is_empty()) |
| 655 return rects; | 730 return rects; |
| 656 std::vector<Range> bounds; | 731 std::vector<Range> bounds; |
| 657 | 732 |
| 658 // Add a Range for each run/selection intersection. | 733 // Add a Range for each run/selection intersection. |
| 659 // TODO(msw): The bounds should probably not always be leading the range ends. | 734 // TODO(msw): The bounds should probably not always be leading the range ends. |
| 660 for (size_t i = 0; i < runs_.size(); ++i) { | 735 for (size_t i = 0; i < runs_.size(); ++i) { |
| 661 const internal::TextRunHarfBuzz* run = runs_[visual_to_logical_[i]]; | 736 internal::TextRunHarfBuzz* run = runs_[visual_to_logical_[i]]; |
| 662 Range intersection = run->range.Intersect(layout_range); | 737 Range intersection = run->range.Intersect(layout_range); |
| 663 if (intersection.IsValid()) { | 738 if (intersection.IsValid()) { |
| 664 DCHECK(!intersection.is_reversed()); | 739 DCHECK(!intersection.is_reversed()); |
| 665 Range range_x(run->GetGlyphXBoundary(intersection.start(), false), | 740 Range range_x( |
| 666 run->GetGlyphXBoundary(intersection.end(), false)); | 741 run->GetGraphemeBounds(GetLayoutText(), run->is_rtl ? |
| 742 intersection.end() - 1 : intersection.start()).start(), | |
| 743 run->GetGraphemeBounds(GetLayoutText(), run->is_rtl ? | |
| 744 intersection.start() : intersection.end() - 1).end()); | |
| 667 if (range_x.is_empty()) | 745 if (range_x.is_empty()) |
| 668 continue; | 746 continue; |
| 669 range_x = Range(range_x.GetMin(), range_x.GetMax()); | 747 range_x = Range(range_x.GetMin(), range_x.GetMax()); |
| 670 // Union this with the last range if they're adjacent. | 748 // Union this with the last range if they're adjacent. |
| 671 DCHECK(bounds.empty() || bounds.back().GetMax() <= range_x.GetMin()); | 749 DCHECK(bounds.empty() || bounds.back().GetMax() <= range_x.GetMin()); |
| 672 if (!bounds.empty() && bounds.back().GetMax() == range_x.GetMin()) { | 750 if (!bounds.empty() && bounds.back().GetMax() == range_x.GetMin()) { |
| 673 range_x = Range(bounds.back().GetMin(), range_x.GetMax()); | 751 range_x = Range(bounds.back().GetMin(), range_x.GetMax()); |
| 674 bounds.pop_back(); | 752 bounds.pop_back(); |
| 675 } | 753 } |
| 676 bounds.push_back(range_x); | 754 bounds.push_back(range_x); |
| (...skipping 23 matching lines...) Expand all Loading... | |
| 700 DCHECK_LE(text_index, text().length()); | 778 DCHECK_LE(text_index, text().length()); |
| 701 return text_index; | 779 return text_index; |
| 702 } | 780 } |
| 703 | 781 |
| 704 bool RenderTextHarfBuzz::IsValidCursorIndex(size_t index) { | 782 bool RenderTextHarfBuzz::IsValidCursorIndex(size_t index) { |
| 705 if (index == 0 || index == text().length()) | 783 if (index == 0 || index == text().length()) |
| 706 return true; | 784 return true; |
| 707 if (!IsValidLogicalIndex(index)) | 785 if (!IsValidLogicalIndex(index)) |
| 708 return false; | 786 return false; |
| 709 EnsureLayout(); | 787 EnsureLayout(); |
| 710 // Disallow indices amid multi-character graphemes by checking glyph bounds. | 788 internal::TextRunHarfBuzz* run = |
|
msw
2014/06/29 22:29:09
Please verify that these examples and Issue 327903
ckocagil
2014/07/24 21:46:56
Done. Added a test too.
| |
| 711 // These characters are not surrogate-pairs, but may yield a single glyph: | 789 runs_[GetRunContainingCaret(SelectionModel(index, CURSOR_BACKWARD))]; |
| 712 // \x0915\x093f - (ki) - one of many Devanagari biconsonantal conjuncts. | 790 base::i18n::BreakIterator* grapheme_iterator = |
|
msw
2014/06/29 22:29:09
nit: Name this |iter| for a one-liner and below "r
ckocagil
2014/07/24 21:46:56
Done. (I made it "return !iter || iter->...") sinc
msw
2014/07/25 00:36:59
Acknowledged.
| |
| 713 // \x0e08\x0e33 - (cho chan + sara am) - a Thai consonant and vowel pair. | 791 run->GetGraphemeIterator(GetLayoutText()); |
| 714 return GetGlyphBounds(index) != GetGlyphBounds(index - 1); | 792 if (!grapheme_iterator) |
| 793 return true; | |
| 794 return grapheme_iterator->IsGraphemeBoundary(index - run->range.start()); | |
| 715 } | 795 } |
| 716 | 796 |
| 717 void RenderTextHarfBuzz::ResetLayout() { | 797 void RenderTextHarfBuzz::ResetLayout() { |
| 718 needs_layout_ = true; | 798 needs_layout_ = true; |
| 719 } | 799 } |
| 720 | 800 |
| 721 void RenderTextHarfBuzz::EnsureLayout() { | 801 void RenderTextHarfBuzz::EnsureLayout() { |
| 722 if (needs_layout_) { | 802 if (needs_layout_) { |
| 723 runs_.clear(); | 803 runs_.clear(); |
| 724 | 804 |
| (...skipping 257 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 982 // Shape the text. | 1062 // Shape the text. |
| 983 hb_shape(harfbuzz_font, buffer, NULL, 0); | 1063 hb_shape(harfbuzz_font, buffer, NULL, 0); |
| 984 | 1064 |
| 985 // Populate the run fields with the resulting glyph data in the buffer. | 1065 // Populate the run fields with the resulting glyph data in the buffer. |
| 986 unsigned int glyph_count = 0; | 1066 unsigned int glyph_count = 0; |
| 987 hb_glyph_info_t* infos = hb_buffer_get_glyph_infos(buffer, &glyph_count); | 1067 hb_glyph_info_t* infos = hb_buffer_get_glyph_infos(buffer, &glyph_count); |
| 988 hb_glyph_position_t* hb_positions = hb_buffer_get_glyph_positions(buffer, | 1068 hb_glyph_position_t* hb_positions = hb_buffer_get_glyph_positions(buffer, |
| 989 NULL); | 1069 NULL); |
| 990 run->glyph_count = glyph_count; | 1070 run->glyph_count = glyph_count; |
| 991 run->glyphs.reset(new uint16[run->glyph_count]); | 1071 run->glyphs.reset(new uint16[run->glyph_count]); |
| 992 run->glyph_to_char.reset(new uint32[run->glyph_count]); | 1072 run->glyph_to_char.resize(run->glyph_count); |
| 993 run->positions.reset(new SkPoint[run->glyph_count]); | 1073 run->positions.reset(new SkPoint[run->glyph_count]); |
| 994 for (size_t i = 0; i < run->glyph_count; ++i) { | 1074 for (size_t i = 0; i < run->glyph_count; ++i) { |
| 995 run->glyphs[i] = infos[i].codepoint; | 1075 run->glyphs[i] = infos[i].codepoint; |
| 996 run->glyph_to_char[i] = infos[i].cluster; | 1076 run->glyph_to_char[i] = infos[i].cluster; |
| 997 const int x_offset = | 1077 const int x_offset = |
| 998 SkScalarRoundToInt(SkFixedToScalar(hb_positions[i].x_offset)); | 1078 SkScalarRoundToInt(SkFixedToScalar(hb_positions[i].x_offset)); |
| 999 const int y_offset = | 1079 const int y_offset = |
| 1000 SkScalarRoundToInt(SkFixedToScalar(hb_positions[i].y_offset)); | 1080 SkScalarRoundToInt(SkFixedToScalar(hb_positions[i].y_offset)); |
| 1001 run->positions[i].set(run->width + x_offset, y_offset); | 1081 run->positions[i].set(run->width + x_offset, y_offset); |
| 1002 run->width += | 1082 run->width += |
| 1003 SkScalarRoundToInt(SkFixedToScalar(hb_positions[i].x_advance)); | 1083 SkScalarRoundToInt(SkFixedToScalar(hb_positions[i].x_advance)); |
| 1004 } | 1084 } |
| 1005 | 1085 |
| 1006 hb_buffer_destroy(buffer); | 1086 hb_buffer_destroy(buffer); |
| 1007 hb_font_destroy(harfbuzz_font); | 1087 hb_font_destroy(harfbuzz_font); |
| 1008 } | 1088 } |
| 1009 | 1089 |
| 1010 } // namespace gfx | 1090 } // namespace gfx |
| OLD | NEW |