Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(177)

Side by Side Diff: ui/gfx/render_text_harfbuzz.cc

Issue 299313005: Replace bare ICU run iterator with base::i18n::BiDiLineIterator (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 6 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « ui/gfx/render_text_harfbuzz.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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
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
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
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
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
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
OLDNEW
« no previous file with comments | « ui/gfx/render_text_harfbuzz.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698