| 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/i18n/bidi_line_iterator.h" | 10 #include "base/i18n/bidi_line_iterator.h" |
| 10 #include "base/i18n/break_iterator.h" | 11 #include "base/i18n/break_iterator.h" |
| 11 #include "base/i18n/char_iterator.h" | 12 #include "base/i18n/char_iterator.h" |
| 12 #include "third_party/harfbuzz-ng/src/hb.h" | 13 #include "third_party/harfbuzz-ng/src/hb.h" |
| 13 #include "third_party/icu/source/common/unicode/ubidi.h" | 14 #include "third_party/icu/source/common/unicode/ubidi.h" |
| 14 #include "third_party/skia/include/core/SkColor.h" | 15 #include "third_party/skia/include/core/SkColor.h" |
| 15 #include "third_party/skia/include/core/SkTypeface.h" | 16 #include "third_party/skia/include/core/SkTypeface.h" |
| 16 #include "ui/gfx/canvas.h" | 17 #include "ui/gfx/canvas.h" |
| 17 #include "ui/gfx/utf16_indexing.h" | 18 #include "ui/gfx/utf16_indexing.h" |
| 18 | 19 |
| (...skipping 167 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 186 | 187 |
| 187 // Returns a HarfBuzz font data provider that uses Skia. | 188 // Returns a HarfBuzz font data provider that uses Skia. |
| 188 hb_font_funcs_t* GetFontFuncs() { | 189 hb_font_funcs_t* GetFontFuncs() { |
| 189 static hb_font_funcs_t* font_funcs = 0; | 190 static hb_font_funcs_t* font_funcs = 0; |
| 190 | 191 |
| 191 // We don't set callback functions which we can't support. | 192 // We don't set callback functions which we can't support. |
| 192 // HarfBuzz will use the fallback implementation if they aren't set. | 193 // HarfBuzz will use the fallback implementation if they aren't set. |
| 193 if (!font_funcs) { | 194 if (!font_funcs) { |
| 194 // The object created by |hb_font_funcs_create()| below lives indefinitely | 195 // The object created by |hb_font_funcs_create()| below lives indefinitely |
| 195 // and is intentionally leaked. | 196 // and is intentionally leaked. |
| 197 ANNOTATE_SCOPED_MEMORY_LEAK; |
| 196 font_funcs = hb_font_funcs_create(); | 198 font_funcs = hb_font_funcs_create(); |
| 197 hb_font_funcs_set_glyph_func(font_funcs, GetGlyph, 0, 0); | 199 hb_font_funcs_set_glyph_func(font_funcs, GetGlyph, 0, 0); |
| 198 hb_font_funcs_set_glyph_h_advance_func( | 200 hb_font_funcs_set_glyph_h_advance_func( |
| 199 font_funcs, GetGlyphHorizontalAdvance, 0, 0); | 201 font_funcs, GetGlyphHorizontalAdvance, 0, 0); |
| 200 hb_font_funcs_set_glyph_h_kerning_func( | 202 hb_font_funcs_set_glyph_h_kerning_func( |
| 201 font_funcs, GetGlyphHorizontalKerning, 0, 0); | 203 font_funcs, GetGlyphHorizontalKerning, 0, 0); |
| 202 hb_font_funcs_set_glyph_h_origin_func( | 204 hb_font_funcs_set_glyph_h_origin_func( |
| 203 font_funcs, GetGlyphHorizontalOrigin, 0, 0); | 205 font_funcs, GetGlyphHorizontalOrigin, 0, 0); |
| 204 hb_font_funcs_set_glyph_v_kerning_func( | 206 hb_font_funcs_set_glyph_v_kerning_func( |
| 205 font_funcs, GetGlyphVerticalKerning, 0, 0); | 207 font_funcs, GetGlyphVerticalKerning, 0, 0); |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 246 | 248 |
| 247 // Creates a HarfBuzz font from the given Skia face and text size. | 249 // Creates a HarfBuzz font from the given Skia face and text size. |
| 248 hb_font_t* CreateHarfBuzzFont(SkTypeface* skia_face, int text_size) { | 250 hb_font_t* CreateHarfBuzzFont(SkTypeface* skia_face, int text_size) { |
| 249 typedef std::pair<hb_face_t*, GlyphCache> FaceCache; | 251 typedef std::pair<hb_face_t*, GlyphCache> FaceCache; |
| 250 | 252 |
| 251 // TODO(ckocagil): This shouldn't grow indefinitely. Maybe use base::MRUCache? | 253 // TODO(ckocagil): This shouldn't grow indefinitely. Maybe use base::MRUCache? |
| 252 static std::map<SkFontID, FaceCache> face_caches; | 254 static std::map<SkFontID, FaceCache> face_caches; |
| 253 | 255 |
| 254 FaceCache* face_cache = &face_caches[skia_face->uniqueID()]; | 256 FaceCache* face_cache = &face_caches[skia_face->uniqueID()]; |
| 255 if (face_cache->first == 0) { | 257 if (face_cache->first == 0) { |
| 258 // These HarfBuzz faces live indefinitely and are intentionally leaked. |
| 259 ANNOTATE_SCOPED_MEMORY_LEAK; |
| 256 hb_face_t* harfbuzz_face = CreateHarfBuzzFace(skia_face); | 260 hb_face_t* harfbuzz_face = CreateHarfBuzzFace(skia_face); |
| 257 *face_cache = FaceCache(harfbuzz_face, GlyphCache()); | 261 *face_cache = FaceCache(harfbuzz_face, GlyphCache()); |
| 258 } | 262 } |
| 259 | 263 |
| 260 hb_font_t* harfbuzz_font = hb_font_create(face_cache->first); | 264 hb_font_t* harfbuzz_font = hb_font_create(face_cache->first); |
| 261 // TODO(ckocagil): Investigate whether disabling hinting here has any effect | 265 // TODO(ckocagil): Investigate whether disabling hinting here has any effect |
| 262 // on text quality. | 266 // on text quality. |
| 263 int upem = hb_face_get_upem(face_cache->first); | 267 int upem = hb_face_get_upem(face_cache->first); |
| 264 hb_font_set_scale(harfbuzz_font, upem, upem); | 268 hb_font_set_scale(harfbuzz_font, upem, upem); |
| 265 FontData* hb_font_data = new FontData(&face_cache->second); | 269 FontData* hb_font_data = new FontData(&face_cache->second); |
| (...skipping 590 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 856 size_t position = LayoutIndexToTextIndex(run->range.end()); | 860 size_t position = LayoutIndexToTextIndex(run->range.end()); |
| 857 position = IndexOfAdjacentGrapheme(position, CURSOR_BACKWARD); | 861 position = IndexOfAdjacentGrapheme(position, CURSOR_BACKWARD); |
| 858 return SelectionModel(position, CURSOR_FORWARD); | 862 return SelectionModel(position, CURSOR_FORWARD); |
| 859 } | 863 } |
| 860 | 864 |
| 861 void RenderTextHarfBuzz::ItemizeText() { | 865 void RenderTextHarfBuzz::ItemizeText() { |
| 862 const base::string16& text = GetLayoutText(); | 866 const base::string16& text = GetLayoutText(); |
| 863 const bool is_text_rtl = GetTextDirection() == base::i18n::RIGHT_TO_LEFT; | 867 const bool is_text_rtl = GetTextDirection() == base::i18n::RIGHT_TO_LEFT; |
| 864 DCHECK_NE(0U, text.length()); | 868 DCHECK_NE(0U, text.length()); |
| 865 | 869 |
| 866 // If ICU fails to itemize the text, we set |fake_runs| and create a run that | 870 // If ICU fails to itemize the text, we create a run that spans the entire |
| 867 // spans the entire text. This is needed because early returning and leaving | 871 // text. This is needed because leaving the runs set empty causes some clients |
| 868 // the runs set empty causes some clients to crash/misbehave since they expect | 872 // to misbehave since they expect non-zero text metrics from a non-empty text. |
| 869 // non-zero text metrics from a non-empty text. | |
| 870 base::i18n::BiDiLineIterator bidi_iterator; | 873 base::i18n::BiDiLineIterator bidi_iterator; |
| 871 bool fake_runs = !bidi_iterator.Open(text, is_text_rtl, false); | 874 if (!bidi_iterator.Open(text, is_text_rtl, false)) { |
| 875 internal::TextRunHarfBuzz* run = new internal::TextRunHarfBuzz; |
| 876 run->range = Range(0, text.length()); |
| 877 runs_.push_back(run); |
| 878 visual_to_logical_ = logical_to_visual_ = std::vector<int32_t>(1, 0); |
| 879 return; |
| 880 } |
| 872 | 881 |
| 873 // Temporarily apply composition underlines and selection colors. | 882 // Temporarily apply composition underlines and selection colors. |
| 874 ApplyCompositionAndSelectionStyles(); | 883 ApplyCompositionAndSelectionStyles(); |
| 875 | 884 |
| 876 // Build the list of runs from the script items and ranged styles. Use an | 885 // Build the list of runs from the script items and ranged styles. Use an |
| 877 // empty color BreakList to avoid breaking runs at color boundaries. | 886 // empty color BreakList to avoid breaking runs at color boundaries. |
| 878 BreakList<SkColor> empty_colors; | 887 BreakList<SkColor> empty_colors; |
| 879 empty_colors.SetMax(text.length()); | 888 empty_colors.SetMax(text.length()); |
| 880 internal::StyleIterator style(empty_colors, styles()); | 889 internal::StyleIterator style(empty_colors, styles()); |
| 881 | 890 |
| 882 for (size_t run_break = 0; run_break < text.length();) { | 891 for (size_t run_break = 0; run_break < text.length();) { |
| 883 internal::TextRunHarfBuzz* run = new internal::TextRunHarfBuzz; | 892 internal::TextRunHarfBuzz* run = new internal::TextRunHarfBuzz; |
| 884 run->range.set_start(run_break); | 893 run->range.set_start(run_break); |
| 885 run->font_style = (style.style(BOLD) ? Font::BOLD : 0) | | 894 run->font_style = (style.style(BOLD) ? Font::BOLD : 0) | |
| 886 (style.style(ITALIC) ? Font::ITALIC : 0); | 895 (style.style(ITALIC) ? Font::ITALIC : 0); |
| 887 run->strike = style.style(STRIKE); | 896 run->strike = style.style(STRIKE); |
| 888 run->diagonal_strike = style.style(DIAGONAL_STRIKE); | 897 run->diagonal_strike = style.style(DIAGONAL_STRIKE); |
| 889 run->underline = style.style(UNDERLINE); | 898 run->underline = style.style(UNDERLINE); |
| 890 | 899 |
| 891 if (fake_runs) { | 900 int32 script_item_break = 0; |
| 892 run_break = text.length(); | 901 bidi_iterator.GetLogicalRun(run_break, &script_item_break, &run->level); |
| 893 } else { | 902 // Odd BiDi embedding levels correspond to RTL runs. |
| 894 int32 script_item_break = 0; | 903 run->is_rtl = (run->level % 2) == 1; |
| 895 bidi_iterator.GetLogicalRun(run_break, &script_item_break, &run->level); | 904 // Find the length and script of this script run. |
| 896 // Find the length and script of this script run. | 905 script_item_break = ScriptInterval(text, run_break, |
| 897 script_item_break = ScriptInterval(text, run_break, | 906 script_item_break - run_break, &run->script) + run_break; |
| 898 script_item_break - run_break, &run->script) + run_break; | |
| 899 | 907 |
| 900 // Find the next break and advance the iterators as needed. | 908 // Find the next break and advance the iterators as needed. |
| 901 run_break = std::min(static_cast<size_t>(script_item_break), | 909 run_break = std::min(static_cast<size_t>(script_item_break), |
| 902 TextIndexToLayoutIndex(style.GetRange().end())); | 910 TextIndexToLayoutIndex(style.GetRange().end())); |
| 903 | 911 |
| 904 // Break runs adjacent to character substrings in certain code blocks. | 912 // Break runs adjacent to character substrings in certain code blocks. |
| 905 // This avoids using their fallback fonts for more characters than needed, | 913 // This avoids using their fallback fonts for more characters than needed, |
| 906 // in cases like "\x25B6 Media Title", etc. http://crbug.com/278913 | 914 // in cases like "\x25B6 Media Title", etc. http://crbug.com/278913 |
| 907 if (run_break > run->range.start()) { | 915 if (run_break > run->range.start()) { |
| 908 const size_t run_start = run->range.start(); | 916 const size_t run_start = run->range.start(); |
| 909 const int32 run_length = static_cast<int32>(run_break - run_start); | 917 const int32 run_length = static_cast<int32>(run_break - run_start); |
| 910 base::i18n::UTF16CharIterator iter(text.c_str() + run_start, | 918 base::i18n::UTF16CharIterator iter(text.c_str() + run_start, |
| 911 run_length); | 919 run_length); |
| 912 const UBlockCode first_block_code = ublock_getCode(iter.get()); | 920 const UBlockCode first_block_code = ublock_getCode(iter.get()); |
| 913 const bool first_block_unusual = IsUnusualBlockCode(first_block_code); | 921 const bool first_block_unusual = IsUnusualBlockCode(first_block_code); |
| 914 while (iter.Advance() && iter.array_pos() < run_length) { | 922 while (iter.Advance() && iter.array_pos() < run_length) { |
| 915 const UBlockCode current_block_code = ublock_getCode(iter.get()); | 923 const UBlockCode current_block_code = ublock_getCode(iter.get()); |
| 916 if (current_block_code != first_block_code && | 924 if (current_block_code != first_block_code && |
| 917 (first_block_unusual || IsUnusualBlockCode(current_block_code))) { | 925 (first_block_unusual || IsUnusualBlockCode(current_block_code))) { |
| 918 run_break = run_start + iter.array_pos(); | 926 run_break = run_start + iter.array_pos(); |
| 919 break; | 927 break; |
| 920 } | |
| 921 } | 928 } |
| 922 } | 929 } |
| 923 } | 930 } |
| 924 | 931 |
| 925 DCHECK(IsValidCodePointIndex(text, run_break)); | 932 DCHECK(IsValidCodePointIndex(text, run_break)); |
| 926 style.UpdatePosition(LayoutIndexToTextIndex(run_break)); | 933 style.UpdatePosition(LayoutIndexToTextIndex(run_break)); |
| 927 run->range.set_end(run_break); | 934 run->range.set_end(run_break); |
| 928 UBiDiDirection direction = ubidi_getBaseDirection( | 935 |
| 929 text.c_str() + run->range.start(), run->range.length()); | |
| 930 if (direction == UBIDI_NEUTRAL) | |
| 931 run->is_rtl = is_text_rtl; | |
| 932 else | |
| 933 run->is_rtl = direction == UBIDI_RTL; | |
| 934 runs_.push_back(run); | 936 runs_.push_back(run); |
| 935 } | 937 } |
| 936 | 938 |
| 937 // Undo the temporarily applied composition underlines and selection colors. | 939 // Undo the temporarily applied composition underlines and selection colors. |
| 938 UndoCompositionAndSelectionStyles(); | 940 UndoCompositionAndSelectionStyles(); |
| 939 | 941 |
| 940 const size_t num_runs = runs_.size(); | 942 const size_t num_runs = runs_.size(); |
| 941 std::vector<UBiDiLevel> levels(num_runs); | 943 std::vector<UBiDiLevel> levels(num_runs); |
| 942 for (size_t i = 0; i < num_runs; ++i) | 944 for (size_t i = 0; i < num_runs; ++i) |
| 943 levels[i] = runs_[i]->level; | 945 levels[i] = runs_[i]->level; |
| (...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 992 run->positions[i].set(run->width + x_offset, y_offset); | 994 run->positions[i].set(run->width + x_offset, y_offset); |
| 993 run->width += | 995 run->width += |
| 994 SkScalarRoundToInt(SkFixedToScalar(hb_positions[i].x_advance)); | 996 SkScalarRoundToInt(SkFixedToScalar(hb_positions[i].x_advance)); |
| 995 } | 997 } |
| 996 | 998 |
| 997 hb_buffer_destroy(buffer); | 999 hb_buffer_destroy(buffer); |
| 998 hb_font_destroy(harfbuzz_font); | 1000 hb_font_destroy(harfbuzz_font); |
| 999 } | 1001 } |
| 1000 | 1002 |
| 1001 } // namespace gfx | 1003 } // namespace gfx |
| OLD | NEW |