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/i18n/bidi_line_iterator.h" |
9 #include "base/i18n/break_iterator.h" | 10 #include "base/i18n/break_iterator.h" |
10 #include "base/i18n/char_iterator.h" | 11 #include "base/i18n/char_iterator.h" |
11 #include "third_party/harfbuzz-ng/src/hb.h" | 12 #include "third_party/harfbuzz-ng/src/hb.h" |
12 #include "third_party/icu/source/common/unicode/ubidi.h" | 13 #include "third_party/icu/source/common/unicode/ubidi.h" |
13 #include "third_party/skia/include/core/SkColor.h" | 14 #include "third_party/skia/include/core/SkColor.h" |
14 #include "third_party/skia/include/core/SkTypeface.h" | 15 #include "third_party/skia/include/core/SkTypeface.h" |
15 #include "ui/gfx/canvas.h" | 16 #include "ui/gfx/canvas.h" |
16 #include "ui/gfx/utf16_indexing.h" | 17 #include "ui/gfx/utf16_indexing.h" |
17 | 18 |
18 #if defined(OS_WIN) | 19 #if defined(OS_WIN) |
(...skipping 302 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
321 return hb_script_from_string(uscript_getShortName(script), -1); | 322 return hb_script_from_string(uscript_getShortName(script), -1); |
322 } | 323 } |
323 | 324 |
324 } // namespace | 325 } // namespace |
325 | 326 |
326 namespace internal { | 327 namespace internal { |
327 | 328 |
328 TextRunHarfBuzz::TextRunHarfBuzz() | 329 TextRunHarfBuzz::TextRunHarfBuzz() |
329 : width(0), | 330 : width(0), |
330 preceding_run_widths(0), | 331 preceding_run_widths(0), |
331 direction(UBIDI_LTR), | 332 is_rtl(false), |
332 level(0), | 333 level(0), |
333 script(USCRIPT_INVALID_CODE), | 334 script(USCRIPT_INVALID_CODE), |
334 glyph_count(-1), | 335 glyph_count(-1), |
335 font_size(0), | 336 font_size(0), |
336 font_style(0), | 337 font_style(0), |
337 strike(false), | 338 strike(false), |
338 diagonal_strike(false), | 339 diagonal_strike(false), |
339 underline(false) {} | 340 underline(false) {} |
340 | 341 |
341 TextRunHarfBuzz::~TextRunHarfBuzz() {} | 342 TextRunHarfBuzz::~TextRunHarfBuzz() {} |
342 | 343 |
343 size_t TextRunHarfBuzz::CharToGlyph(size_t pos) const { | 344 size_t TextRunHarfBuzz::CharToGlyph(size_t pos) const { |
344 DCHECK(range.start() <= pos && pos < range.end()); | 345 DCHECK(range.start() <= pos && pos < range.end()); |
345 | 346 |
346 if (direction == UBIDI_LTR) { | 347 if (!is_rtl) { |
347 for (size_t i = 0; i < glyph_count - 1; ++i) { | 348 for (size_t i = 0; i < glyph_count - 1; ++i) { |
348 if (pos < glyph_to_char[i + 1]) | 349 if (pos < glyph_to_char[i + 1]) |
349 return i; | 350 return i; |
350 } | 351 } |
351 return glyph_count - 1; | 352 return glyph_count - 1; |
352 } | 353 } |
353 | 354 |
354 for (size_t i = 0; i < glyph_count; ++i) { | 355 for (size_t i = 0; i < glyph_count; ++i) { |
355 if (pos >= glyph_to_char[i]) | 356 if (pos >= glyph_to_char[i]) |
356 return i; | 357 return i; |
(...skipping 22 matching lines...) Expand all Loading... |
379 return true; | 380 return true; |
380 } | 381 } |
381 return false; | 382 return false; |
382 } | 383 } |
383 | 384 |
384 int TextRunHarfBuzz::GetGlyphXBoundary(size_t text_index, bool trailing) const { | 385 int TextRunHarfBuzz::GetGlyphXBoundary(size_t text_index, bool trailing) const { |
385 int x = preceding_run_widths; | 386 int x = preceding_run_widths; |
386 Range glyph_range; | 387 Range glyph_range; |
387 if (text_index == range.end()) { | 388 if (text_index == range.end()) { |
388 trailing = true; | 389 trailing = true; |
389 glyph_range = direction == UBIDI_LTR ? | 390 glyph_range = is_rtl ? Range(0, 1) : Range(glyph_count - 1, glyph_count); |
390 Range(glyph_count - 1, glyph_count) : Range(0, 1); | |
391 } else { | 391 } else { |
392 glyph_range = CharRangeToGlyphRange(Range(text_index, text_index + 1)); | 392 glyph_range = CharRangeToGlyphRange(Range(text_index, text_index + 1)); |
393 } | 393 } |
394 const int trailing_step = trailing ? 1 : 0; | 394 const int trailing_step = trailing ? 1 : 0; |
395 const size_t glyph_pos = glyph_range.start() + | 395 const size_t glyph_pos = |
396 (direction == UBIDI_LTR ? trailing_step : (1 - trailing_step)); | 396 glyph_range.start() + (is_rtl ? (1 - trailing_step) : trailing_step); |
397 x += glyph_pos < glyph_count ? | 397 x += glyph_pos < glyph_count ? |
398 SkScalarRoundToInt(positions[glyph_pos].x()) : width; | 398 SkScalarRoundToInt(positions[glyph_pos].x()) : width; |
399 return x; | 399 return x; |
400 } | 400 } |
401 | 401 |
402 } // namespace internal | 402 } // namespace internal |
403 | 403 |
404 RenderTextHarfBuzz::RenderTextHarfBuzz() | 404 RenderTextHarfBuzz::RenderTextHarfBuzz() |
405 : RenderText(), | 405 : RenderText(), |
406 needs_layout_(false) {} | 406 needs_layout_(false) {} |
(...skipping 12 matching lines...) Expand all Loading... |
419 int offset = 0; | 419 int offset = 0; |
420 size_t run_index = GetRunContainingXCoord(x, &offset); | 420 size_t run_index = GetRunContainingXCoord(x, &offset); |
421 if (run_index >= runs_.size()) | 421 if (run_index >= runs_.size()) |
422 return EdgeSelectionModel((x < 0) ? CURSOR_LEFT : CURSOR_RIGHT); | 422 return EdgeSelectionModel((x < 0) ? CURSOR_LEFT : CURSOR_RIGHT); |
423 const internal::TextRunHarfBuzz& run = *runs_[run_index]; | 423 const internal::TextRunHarfBuzz& run = *runs_[run_index]; |
424 | 424 |
425 for (size_t i = 0; i < run.glyph_count; ++i) { | 425 for (size_t i = 0; i < run.glyph_count; ++i) { |
426 const SkScalar end = | 426 const SkScalar end = |
427 i + 1 == run.glyph_count ? run.width : run.positions[i + 1].x(); | 427 i + 1 == run.glyph_count ? run.width : run.positions[i + 1].x(); |
428 const SkScalar middle = (end + run.positions[i].x()) / 2; | 428 const SkScalar middle = (end + run.positions[i].x()) / 2; |
429 const bool is_rtl = run.direction == UBIDI_RTL; | 429 |
430 if (offset < middle) { | 430 if (offset < middle) { |
431 return SelectionModel(LayoutIndexToTextIndex( | 431 return SelectionModel(LayoutIndexToTextIndex( |
432 run.glyph_to_char[i] + (is_rtl ? 1 : 0)), | 432 run.glyph_to_char[i] + (run.is_rtl ? 1 : 0)), |
433 (is_rtl ? CURSOR_BACKWARD : CURSOR_FORWARD)); | 433 (run.is_rtl ? CURSOR_BACKWARD : CURSOR_FORWARD)); |
434 } | 434 } |
435 if (offset < end) { | 435 if (offset < end) { |
436 return SelectionModel(LayoutIndexToTextIndex( | 436 return SelectionModel(LayoutIndexToTextIndex( |
437 run.glyph_to_char[i] + (is_rtl ? 0 : 1)), | 437 run.glyph_to_char[i] + (run.is_rtl ? 0 : 1)), |
438 (is_rtl ? CURSOR_FORWARD : CURSOR_BACKWARD)); | 438 (run.is_rtl ? CURSOR_FORWARD : CURSOR_BACKWARD)); |
439 } | 439 } |
440 } | 440 } |
441 return EdgeSelectionModel(CURSOR_RIGHT); | 441 return EdgeSelectionModel(CURSOR_RIGHT); |
442 } | 442 } |
443 | 443 |
444 std::vector<RenderText::FontSpan> RenderTextHarfBuzz::GetFontSpansForTesting() { | 444 std::vector<RenderText::FontSpan> RenderTextHarfBuzz::GetFontSpansForTesting() { |
445 NOTIMPLEMENTED(); | 445 NOTIMPLEMENTED(); |
446 return std::vector<RenderText::FontSpan>(); | 446 return std::vector<RenderText::FontSpan>(); |
447 } | 447 } |
448 | 448 |
(...skipping 13 matching lines...) Expand all Loading... |
462 SelectionModel edge = EdgeSelectionModel(direction); | 462 SelectionModel edge = EdgeSelectionModel(direction); |
463 if (edge.caret_pos() == selection.caret_pos()) | 463 if (edge.caret_pos() == selection.caret_pos()) |
464 return edge; | 464 return edge; |
465 int visual_index = (direction == CURSOR_RIGHT) ? 0 : runs_.size() - 1; | 465 int visual_index = (direction == CURSOR_RIGHT) ? 0 : runs_.size() - 1; |
466 run = runs_[visual_to_logical_[visual_index]]; | 466 run = runs_[visual_to_logical_[visual_index]]; |
467 } else { | 467 } else { |
468 // If the cursor is moving within the current run, just move it by one | 468 // If the cursor is moving within the current run, just move it by one |
469 // grapheme in the appropriate direction. | 469 // grapheme in the appropriate direction. |
470 run = runs_[run_index]; | 470 run = runs_[run_index]; |
471 size_t caret = selection.caret_pos(); | 471 size_t caret = selection.caret_pos(); |
472 bool forward_motion = | 472 bool forward_motion = run->is_rtl == (direction == CURSOR_LEFT); |
473 (run->direction == UBIDI_RTL) == (direction == CURSOR_LEFT); | |
474 if (forward_motion) { | 473 if (forward_motion) { |
475 if (caret < LayoutIndexToTextIndex(run->range.end())) { | 474 if (caret < LayoutIndexToTextIndex(run->range.end())) { |
476 caret = IndexOfAdjacentGrapheme(caret, CURSOR_FORWARD); | 475 caret = IndexOfAdjacentGrapheme(caret, CURSOR_FORWARD); |
477 return SelectionModel(caret, CURSOR_BACKWARD); | 476 return SelectionModel(caret, CURSOR_BACKWARD); |
478 } | 477 } |
479 } else { | 478 } else { |
480 if (caret > LayoutIndexToTextIndex(run->range.start())) { | 479 if (caret > LayoutIndexToTextIndex(run->range.start())) { |
481 caret = IndexOfAdjacentGrapheme(caret, CURSOR_BACKWARD); | 480 caret = IndexOfAdjacentGrapheme(caret, CURSOR_BACKWARD); |
482 return SelectionModel(caret, CURSOR_FORWARD); | 481 return SelectionModel(caret, CURSOR_FORWARD); |
483 } | 482 } |
484 } | 483 } |
485 // The cursor is at the edge of a run; move to the visually adjacent run. | 484 // The cursor is at the edge of a run; move to the visually adjacent run. |
486 int visual_index = logical_to_visual_[run_index]; | 485 int visual_index = logical_to_visual_[run_index]; |
487 visual_index += (direction == CURSOR_LEFT) ? -1 : 1; | 486 visual_index += (direction == CURSOR_LEFT) ? -1 : 1; |
488 if (visual_index < 0 || visual_index >= static_cast<int>(runs_.size())) | 487 if (visual_index < 0 || visual_index >= static_cast<int>(runs_.size())) |
489 return EdgeSelectionModel(direction); | 488 return EdgeSelectionModel(direction); |
490 run = runs_[visual_to_logical_[visual_index]]; | 489 run = runs_[visual_to_logical_[visual_index]]; |
491 } | 490 } |
492 bool forward_motion = | 491 bool forward_motion = run->is_rtl == (direction == CURSOR_LEFT); |
493 (run->direction == UBIDI_RTL) == (direction == CURSOR_LEFT); | |
494 return forward_motion ? FirstSelectionModelInsideRun(run) : | 492 return forward_motion ? FirstSelectionModelInsideRun(run) : |
495 LastSelectionModelInsideRun(run); | 493 LastSelectionModelInsideRun(run); |
496 } | 494 } |
497 | 495 |
498 SelectionModel RenderTextHarfBuzz::AdjacentWordSelectionModel( | 496 SelectionModel RenderTextHarfBuzz::AdjacentWordSelectionModel( |
499 const SelectionModel& selection, | 497 const SelectionModel& selection, |
500 VisualCursorDirection direction) { | 498 VisualCursorDirection direction) { |
501 // TODO(ckocagil): This implementation currently matches RenderTextWin, but it | 499 // TODO(ckocagil): This implementation currently matches RenderTextWin, but it |
502 // should match the native behavior on other platforms. | 500 // should match the native behavior on other platforms. |
503 if (obscured()) | 501 if (obscured()) |
(...skipping 283 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
787 | 785 |
788 SelectionModel RenderTextHarfBuzz::LastSelectionModelInsideRun( | 786 SelectionModel RenderTextHarfBuzz::LastSelectionModelInsideRun( |
789 const internal::TextRunHarfBuzz* run) { | 787 const internal::TextRunHarfBuzz* run) { |
790 size_t position = LayoutIndexToTextIndex(run->range.end()); | 788 size_t position = LayoutIndexToTextIndex(run->range.end()); |
791 position = IndexOfAdjacentGrapheme(position, CURSOR_BACKWARD); | 789 position = IndexOfAdjacentGrapheme(position, CURSOR_BACKWARD); |
792 return SelectionModel(position, CURSOR_FORWARD); | 790 return SelectionModel(position, CURSOR_FORWARD); |
793 } | 791 } |
794 | 792 |
795 void RenderTextHarfBuzz::ItemizeText() { | 793 void RenderTextHarfBuzz::ItemizeText() { |
796 const base::string16& text = GetLayoutText(); | 794 const base::string16& text = GetLayoutText(); |
797 const bool is_rtl = GetTextDirection() == base::i18n::RIGHT_TO_LEFT; | 795 const bool is_text_rtl = GetTextDirection() == base::i18n::RIGHT_TO_LEFT; |
798 DCHECK_NE(0U, text.length()); | 796 DCHECK_NE(0U, text.length()); |
799 | 797 |
800 // If ICU fails to itemize the text, we set |fake_runs| and create a run that | 798 // If ICU fails to itemize the text, we set |fake_runs| and create a run that |
801 // spans the entire text. This is needed because early returning and leaving | 799 // spans the entire text. This is needed because early returning and leaving |
802 // the runs set empty causes some clients to crash/misbehave since they expect | 800 // the runs set empty causes some clients to crash/misbehave since they expect |
803 // non-zero text metrics from a non-empty text. | 801 // non-zero text metrics from a non-empty text. |
804 bool fake_runs = false; | 802 base::i18n::BiDiLineIterator bidi_iterator; |
805 UErrorCode result = U_ZERO_ERROR; | 803 bool fake_runs = !bidi_iterator.Open(text, is_text_rtl, false); |
806 | |
807 UBiDi* line = ubidi_openSized(text.length(), 0, &result); | |
808 if (U_FAILURE(result)) { | |
809 NOTREACHED(); | |
810 fake_runs = true; | |
811 } else { | |
812 ubidi_setPara(line, text.c_str(), text.length(), | |
813 is_rtl ? UBIDI_DEFAULT_RTL : UBIDI_DEFAULT_LTR, NULL, | |
814 &result); | |
815 if (U_FAILURE(result)) { | |
816 NOTREACHED(); | |
817 fake_runs = true; | |
818 } | |
819 } | |
820 | 804 |
821 // Temporarily apply composition underlines and selection colors. | 805 // Temporarily apply composition underlines and selection colors. |
822 ApplyCompositionAndSelectionStyles(); | 806 ApplyCompositionAndSelectionStyles(); |
823 | 807 |
824 // Build the list of runs from the script items and ranged styles. Use an | 808 // Build the list of runs from the script items and ranged styles. Use an |
825 // empty color BreakList to avoid breaking runs at color boundaries. | 809 // empty color BreakList to avoid breaking runs at color boundaries. |
826 BreakList<SkColor> empty_colors; | 810 BreakList<SkColor> empty_colors; |
827 empty_colors.SetMax(text.length()); | 811 empty_colors.SetMax(text.length()); |
828 internal::StyleIterator style(empty_colors, styles()); | 812 internal::StyleIterator style(empty_colors, styles()); |
829 | 813 |
830 for (size_t run_break = 0; run_break < text.length();) { | 814 for (size_t run_break = 0; run_break < text.length();) { |
831 internal::TextRunHarfBuzz* run = new internal::TextRunHarfBuzz; | 815 internal::TextRunHarfBuzz* run = new internal::TextRunHarfBuzz; |
832 run->range.set_start(run_break); | 816 run->range.set_start(run_break); |
833 run->font_style = (style.style(BOLD) ? Font::BOLD : 0) | | 817 run->font_style = (style.style(BOLD) ? Font::BOLD : 0) | |
834 (style.style(ITALIC) ? Font::ITALIC : 0); | 818 (style.style(ITALIC) ? Font::ITALIC : 0); |
835 run->strike = style.style(STRIKE); | 819 run->strike = style.style(STRIKE); |
836 run->diagonal_strike = style.style(DIAGONAL_STRIKE); | 820 run->diagonal_strike = style.style(DIAGONAL_STRIKE); |
837 run->underline = style.style(UNDERLINE); | 821 run->underline = style.style(UNDERLINE); |
838 | 822 |
839 if (fake_runs) { | 823 if (fake_runs) { |
840 run_break = text.length(); | 824 run_break = text.length(); |
841 } else { | 825 } else { |
842 int32 script_item_break = 0; | 826 int32 script_item_break = 0; |
843 ubidi_getLogicalRun(line, run_break, &script_item_break, &run->level); | 827 bidi_iterator.GetLogicalRun(run_break, &script_item_break, &run->level); |
844 // Find the length and script of this script run. | 828 // Find the length and script of this script run. |
845 script_item_break = ScriptInterval(text, run_break, | 829 script_item_break = ScriptInterval(text, run_break, |
846 script_item_break - run_break, &run->script) + run_break; | 830 script_item_break - run_break, &run->script) + run_break; |
847 | 831 |
848 // Find the next break and advance the iterators as needed. | 832 // Find the next break and advance the iterators as needed. |
849 run_break = std::min(static_cast<size_t>(script_item_break), | 833 run_break = std::min(static_cast<size_t>(script_item_break), |
850 TextIndexToLayoutIndex(style.GetRange().end())); | 834 TextIndexToLayoutIndex(style.GetRange().end())); |
851 | 835 |
852 // Break runs adjacent to character substrings in certain code blocks. | 836 // Break runs adjacent to character substrings in certain code blocks. |
853 // This avoids using their fallback fonts for more characters than needed, | 837 // This avoids using their fallback fonts for more characters than needed, |
(...skipping 12 matching lines...) Expand all Loading... |
866 run_break = run_start + iter.array_pos(); | 850 run_break = run_start + iter.array_pos(); |
867 break; | 851 break; |
868 } | 852 } |
869 } | 853 } |
870 } | 854 } |
871 } | 855 } |
872 | 856 |
873 DCHECK(IsValidCodePointIndex(text, run_break)); | 857 DCHECK(IsValidCodePointIndex(text, run_break)); |
874 style.UpdatePosition(LayoutIndexToTextIndex(run_break)); | 858 style.UpdatePosition(LayoutIndexToTextIndex(run_break)); |
875 run->range.set_end(run_break); | 859 run->range.set_end(run_break); |
876 const UChar* uchar_start = ubidi_getText(line); | 860 UBiDiDirection direction = ubidi_getBaseDirection( |
877 // TODO(ckocagil): Add |ubidi_getBaseDirection| to i18n::BiDiLineIterator | 861 text.c_str() + run->range.start(), run->range.length()); |
878 // and remove the bare ICU use here. | 862 if (direction == UBIDI_NEUTRAL) |
879 run->direction = ubidi_getBaseDirection(uchar_start + run->range.start(), | 863 run->is_rtl = is_text_rtl; |
880 run->range.length()); | 864 else |
881 if (run->direction == UBIDI_NEUTRAL) | 865 run->is_rtl = direction == UBIDI_RTL; |
882 run->direction = is_rtl ? UBIDI_RTL : UBIDI_LTR; | |
883 runs_.push_back(run); | 866 runs_.push_back(run); |
884 } | 867 } |
885 | 868 |
886 ubidi_close(line); | |
887 | |
888 // Undo the temporarily applied composition underlines and selection colors. | 869 // Undo the temporarily applied composition underlines and selection colors. |
889 UndoCompositionAndSelectionStyles(); | 870 UndoCompositionAndSelectionStyles(); |
890 | 871 |
891 const size_t num_runs = runs_.size(); | 872 const size_t num_runs = runs_.size(); |
892 std::vector<UBiDiLevel> levels(num_runs); | 873 std::vector<UBiDiLevel> levels(num_runs); |
893 for (size_t i = 0; i < num_runs; ++i) | 874 for (size_t i = 0; i < num_runs; ++i) |
894 levels[i] = runs_[i]->level; | 875 levels[i] = runs_[i]->level; |
895 visual_to_logical_.resize(num_runs); | 876 visual_to_logical_.resize(num_runs); |
896 ubidi_reorderVisual(&levels[0], num_runs, &visual_to_logical_[0]); | 877 ubidi_reorderVisual(&levels[0], num_runs, &visual_to_logical_[0]); |
897 logical_to_visual_.resize(num_runs); | 878 logical_to_visual_.resize(num_runs); |
(...skipping 12 matching lines...) Expand all Loading... |
910 run->font_size); | 891 run->font_size); |
911 | 892 |
912 // Create a HarfBuzz buffer and add the string to be shaped. The HarfBuzz | 893 // Create a HarfBuzz buffer and add the string to be shaped. The HarfBuzz |
913 // buffer holds our text, run information to be used by the shaping engine, | 894 // buffer holds our text, run information to be used by the shaping engine, |
914 // and the resulting glyph data. | 895 // and the resulting glyph data. |
915 hb_buffer_t* buffer = hb_buffer_create(); | 896 hb_buffer_t* buffer = hb_buffer_create(); |
916 hb_buffer_add_utf16(buffer, reinterpret_cast<const uint16*>(text.c_str()), | 897 hb_buffer_add_utf16(buffer, reinterpret_cast<const uint16*>(text.c_str()), |
917 text.length(), run->range.start(), run->range.length()); | 898 text.length(), run->range.start(), run->range.length()); |
918 hb_buffer_set_script(buffer, ICUScriptToHBScript(run->script)); | 899 hb_buffer_set_script(buffer, ICUScriptToHBScript(run->script)); |
919 hb_buffer_set_direction(buffer, | 900 hb_buffer_set_direction(buffer, |
920 run->direction == UBIDI_LTR ? HB_DIRECTION_LTR : HB_DIRECTION_RTL); | 901 run->is_rtl ? HB_DIRECTION_RTL : HB_DIRECTION_LTR); |
921 // TODO(ckocagil): Should we determine the actual language? | 902 // TODO(ckocagil): Should we determine the actual language? |
922 hb_buffer_set_language(buffer, hb_language_get_default()); | 903 hb_buffer_set_language(buffer, hb_language_get_default()); |
923 | 904 |
924 // Shape the text. | 905 // Shape the text. |
925 hb_shape(harfbuzz_font, buffer, NULL, 0); | 906 hb_shape(harfbuzz_font, buffer, NULL, 0); |
926 | 907 |
927 // Populate the run fields with the resulting glyph data in the buffer. | 908 // Populate the run fields with the resulting glyph data in the buffer. |
928 unsigned int glyph_count = 0; | 909 unsigned int glyph_count = 0; |
929 hb_glyph_info_t* infos = hb_buffer_get_glyph_infos(buffer, &glyph_count); | 910 hb_glyph_info_t* infos = hb_buffer_get_glyph_infos(buffer, &glyph_count); |
930 hb_glyph_position_t* hb_positions = hb_buffer_get_glyph_positions(buffer, | 911 hb_glyph_position_t* hb_positions = hb_buffer_get_glyph_positions(buffer, |
(...skipping 12 matching lines...) Expand all Loading... |
943 run->positions[i].set(run->width + x_offset, y_offset); | 924 run->positions[i].set(run->width + x_offset, y_offset); |
944 run->width += | 925 run->width += |
945 SkScalarRoundToInt(SkFixedToScalar(hb_positions[i].x_advance)); | 926 SkScalarRoundToInt(SkFixedToScalar(hb_positions[i].x_advance)); |
946 } | 927 } |
947 | 928 |
948 hb_buffer_destroy(buffer); | 929 hb_buffer_destroy(buffer); |
949 hb_font_destroy(harfbuzz_font); | 930 hb_font_destroy(harfbuzz_font); |
950 } | 931 } |
951 | 932 |
952 } // namespace gfx | 933 } // namespace gfx |
OLD | NEW |