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

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

Issue 323403002: Re-land RenderTextHarfBuzz: Decide run direction by BiDi embedding level (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: annotate intentional leaks Created 6 years, 6 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') | ui/gfx/render_text_unittest.cc » ('j') | 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/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
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
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 // HarfBuzz faces created here live indefinitely and are intentionally
msw 2014/06/11 17:18:00 nit: s/"HarfBuzz faces created here"/"These HarfBu
259 // leaked.
260 ANNOTATE_SCOPED_MEMORY_LEAK;
256 hb_face_t* harfbuzz_face = CreateHarfBuzzFace(skia_face); 261 hb_face_t* harfbuzz_face = CreateHarfBuzzFace(skia_face);
257 *face_cache = FaceCache(harfbuzz_face, GlyphCache()); 262 *face_cache = FaceCache(harfbuzz_face, GlyphCache());
258 } 263 }
259 264
260 hb_font_t* harfbuzz_font = hb_font_create(face_cache->first); 265 hb_font_t* harfbuzz_font = hb_font_create(face_cache->first);
261 // TODO(ckocagil): Investigate whether disabling hinting here has any effect 266 // TODO(ckocagil): Investigate whether disabling hinting here has any effect
262 // on text quality. 267 // on text quality.
263 int upem = hb_face_get_upem(face_cache->first); 268 int upem = hb_face_get_upem(face_cache->first);
264 hb_font_set_scale(harfbuzz_font, upem, upem); 269 hb_font_set_scale(harfbuzz_font, upem, upem);
265 FontData* hb_font_data = new FontData(&face_cache->second); 270 FontData* hb_font_data = new FontData(&face_cache->second);
(...skipping 590 matching lines...) Expand 10 before | Expand all | Expand 10 after
856 size_t position = LayoutIndexToTextIndex(run->range.end()); 861 size_t position = LayoutIndexToTextIndex(run->range.end());
857 position = IndexOfAdjacentGrapheme(position, CURSOR_BACKWARD); 862 position = IndexOfAdjacentGrapheme(position, CURSOR_BACKWARD);
858 return SelectionModel(position, CURSOR_FORWARD); 863 return SelectionModel(position, CURSOR_FORWARD);
859 } 864 }
860 865
861 void RenderTextHarfBuzz::ItemizeText() { 866 void RenderTextHarfBuzz::ItemizeText() {
862 const base::string16& text = GetLayoutText(); 867 const base::string16& text = GetLayoutText();
863 const bool is_text_rtl = GetTextDirection() == base::i18n::RIGHT_TO_LEFT; 868 const bool is_text_rtl = GetTextDirection() == base::i18n::RIGHT_TO_LEFT;
864 DCHECK_NE(0U, text.length()); 869 DCHECK_NE(0U, text.length());
865 870
866 // If ICU fails to itemize the text, we set |fake_runs| and create a run that 871 // 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 872 // 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 873 // 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; 874 base::i18n::BiDiLineIterator bidi_iterator;
871 bool fake_runs = !bidi_iterator.Open(text, is_text_rtl, false); 875 if (!bidi_iterator.Open(text, is_text_rtl, false)) {
876 internal::TextRunHarfBuzz* run = new internal::TextRunHarfBuzz;
877 run->range = Range(0, text.length());
878 runs_.push_back(run);
879 visual_to_logical_ = logical_to_visual_ = std::vector<int32_t>(1, 0);
880 return;
881 }
872 882
873 // Temporarily apply composition underlines and selection colors. 883 // Temporarily apply composition underlines and selection colors.
874 ApplyCompositionAndSelectionStyles(); 884 ApplyCompositionAndSelectionStyles();
875 885
876 // Build the list of runs from the script items and ranged styles. Use an 886 // 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. 887 // empty color BreakList to avoid breaking runs at color boundaries.
878 BreakList<SkColor> empty_colors; 888 BreakList<SkColor> empty_colors;
879 empty_colors.SetMax(text.length()); 889 empty_colors.SetMax(text.length());
880 internal::StyleIterator style(empty_colors, styles()); 890 internal::StyleIterator style(empty_colors, styles());
881 891
882 for (size_t run_break = 0; run_break < text.length();) { 892 for (size_t run_break = 0; run_break < text.length();) {
883 internal::TextRunHarfBuzz* run = new internal::TextRunHarfBuzz; 893 internal::TextRunHarfBuzz* run = new internal::TextRunHarfBuzz;
884 run->range.set_start(run_break); 894 run->range.set_start(run_break);
885 run->font_style = (style.style(BOLD) ? Font::BOLD : 0) | 895 run->font_style = (style.style(BOLD) ? Font::BOLD : 0) |
886 (style.style(ITALIC) ? Font::ITALIC : 0); 896 (style.style(ITALIC) ? Font::ITALIC : 0);
887 run->strike = style.style(STRIKE); 897 run->strike = style.style(STRIKE);
888 run->diagonal_strike = style.style(DIAGONAL_STRIKE); 898 run->diagonal_strike = style.style(DIAGONAL_STRIKE);
889 run->underline = style.style(UNDERLINE); 899 run->underline = style.style(UNDERLINE);
890 900
891 if (fake_runs) { 901 int32 script_item_break = 0;
892 run_break = text.length(); 902 bidi_iterator.GetLogicalRun(run_break, &script_item_break, &run->level);
893 } else { 903 // Odd BiDi embedding levels correspond to RTL runs.
894 int32 script_item_break = 0; 904 run->is_rtl = (run->level % 2) == 1;
895 bidi_iterator.GetLogicalRun(run_break, &script_item_break, &run->level); 905 // Find the length and script of this script run.
896 // Find the length and script of this script run. 906 script_item_break = ScriptInterval(text, run_break,
897 script_item_break = ScriptInterval(text, run_break, 907 script_item_break - run_break, &run->script) + run_break;
898 script_item_break - run_break, &run->script) + run_break;
899 908
900 // Find the next break and advance the iterators as needed. 909 // Find the next break and advance the iterators as needed.
901 run_break = std::min(static_cast<size_t>(script_item_break), 910 run_break = std::min(static_cast<size_t>(script_item_break),
902 TextIndexToLayoutIndex(style.GetRange().end())); 911 TextIndexToLayoutIndex(style.GetRange().end()));
903 912
904 // Break runs adjacent to character substrings in certain code blocks. 913 // Break runs adjacent to character substrings in certain code blocks.
905 // This avoids using their fallback fonts for more characters than needed, 914 // This avoids using their fallback fonts for more characters than needed,
906 // in cases like "\x25B6 Media Title", etc. http://crbug.com/278913 915 // in cases like "\x25B6 Media Title", etc. http://crbug.com/278913
907 if (run_break > run->range.start()) { 916 if (run_break > run->range.start()) {
908 const size_t run_start = run->range.start(); 917 const size_t run_start = run->range.start();
909 const int32 run_length = static_cast<int32>(run_break - run_start); 918 const int32 run_length = static_cast<int32>(run_break - run_start);
910 base::i18n::UTF16CharIterator iter(text.c_str() + run_start, 919 base::i18n::UTF16CharIterator iter(text.c_str() + run_start,
911 run_length); 920 run_length);
912 const UBlockCode first_block_code = ublock_getCode(iter.get()); 921 const UBlockCode first_block_code = ublock_getCode(iter.get());
913 const bool first_block_unusual = IsUnusualBlockCode(first_block_code); 922 const bool first_block_unusual = IsUnusualBlockCode(first_block_code);
914 while (iter.Advance() && iter.array_pos() < run_length) { 923 while (iter.Advance() && iter.array_pos() < run_length) {
915 const UBlockCode current_block_code = ublock_getCode(iter.get()); 924 const UBlockCode current_block_code = ublock_getCode(iter.get());
916 if (current_block_code != first_block_code && 925 if (current_block_code != first_block_code &&
917 (first_block_unusual || IsUnusualBlockCode(current_block_code))) { 926 (first_block_unusual || IsUnusualBlockCode(current_block_code))) {
918 run_break = run_start + iter.array_pos(); 927 run_break = run_start + iter.array_pos();
919 break; 928 break;
920 }
921 } 929 }
922 } 930 }
923 } 931 }
924 932
925 DCHECK(IsValidCodePointIndex(text, run_break)); 933 DCHECK(IsValidCodePointIndex(text, run_break));
926 style.UpdatePosition(LayoutIndexToTextIndex(run_break)); 934 style.UpdatePosition(LayoutIndexToTextIndex(run_break));
927 run->range.set_end(run_break); 935 run->range.set_end(run_break);
928 UBiDiDirection direction = ubidi_getBaseDirection( 936
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); 937 runs_.push_back(run);
935 } 938 }
936 939
937 // Undo the temporarily applied composition underlines and selection colors. 940 // Undo the temporarily applied composition underlines and selection colors.
938 UndoCompositionAndSelectionStyles(); 941 UndoCompositionAndSelectionStyles();
939 942
940 const size_t num_runs = runs_.size(); 943 const size_t num_runs = runs_.size();
941 std::vector<UBiDiLevel> levels(num_runs); 944 std::vector<UBiDiLevel> levels(num_runs);
942 for (size_t i = 0; i < num_runs; ++i) 945 for (size_t i = 0; i < num_runs; ++i)
943 levels[i] = runs_[i]->level; 946 levels[i] = runs_[i]->level;
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after
992 run->positions[i].set(run->width + x_offset, y_offset); 995 run->positions[i].set(run->width + x_offset, y_offset);
993 run->width += 996 run->width +=
994 SkScalarRoundToInt(SkFixedToScalar(hb_positions[i].x_advance)); 997 SkScalarRoundToInt(SkFixedToScalar(hb_positions[i].x_advance));
995 } 998 }
996 999
997 hb_buffer_destroy(buffer); 1000 hb_buffer_destroy(buffer);
998 hb_font_destroy(harfbuzz_font); 1001 hb_font_destroy(harfbuzz_font);
999 } 1002 }
1000 1003
1001 } // namespace gfx 1004 } // namespace gfx
OLDNEW
« no previous file with comments | « ui/gfx/render_text_harfbuzz.h ('k') | ui/gfx/render_text_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698