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 <limits> | 7 #include <limits> |
8 #include <map> | |
9 | 8 |
10 #include "base/i18n/bidi_line_iterator.h" | 9 #include "base/i18n/bidi_line_iterator.h" |
11 #include "base/i18n/break_iterator.h" | 10 #include "base/i18n/break_iterator.h" |
12 #include "base/i18n/char_iterator.h" | 11 #include "base/i18n/char_iterator.h" |
13 #include "base/lazy_instance.h" | |
14 #include "base/profiler/scoped_tracker.h" | 12 #include "base/profiler/scoped_tracker.h" |
15 #include "third_party/harfbuzz-ng/src/hb.h" | 13 #include "third_party/harfbuzz-ng/src/hb.h" |
16 #include "third_party/icu/source/common/unicode/ubidi.h" | 14 #include "third_party/icu/source/common/unicode/ubidi.h" |
17 #include "third_party/skia/include/core/SkColor.h" | 15 #include "third_party/skia/include/core/SkColor.h" |
18 #include "third_party/skia/include/core/SkTypeface.h" | 16 #include "third_party/skia/include/core/SkTypeface.h" |
19 #include "ui/gfx/canvas.h" | 17 #include "ui/gfx/canvas.h" |
20 #include "ui/gfx/font_fallback.h" | 18 #include "ui/gfx/font_fallback.h" |
21 #include "ui/gfx/font_render_params.h" | 19 #include "ui/gfx/font_render_params.h" |
| 20 #include "ui/gfx/harfbuzz_font_skia.h" |
22 #include "ui/gfx/utf16_indexing.h" | 21 #include "ui/gfx/utf16_indexing.h" |
23 | 22 |
24 #if defined(OS_WIN) | 23 #if defined(OS_WIN) |
25 #include "ui/gfx/font_fallback_win.h" | 24 #include "ui/gfx/font_fallback_win.h" |
26 #endif | 25 #endif |
27 | 26 |
28 using gfx::internal::RangeF; | 27 using gfx::internal::RangeF; |
29 using gfx::internal::RoundRangeF; | 28 using gfx::internal::RoundRangeF; |
30 | 29 |
31 namespace gfx { | 30 namespace gfx { |
32 | 31 |
33 namespace { | 32 namespace { |
34 | 33 |
35 // Text length limit. Longer strings are slow and not fully tested. | 34 // Text length limit. Longer strings are slow and not fully tested. |
36 const size_t kMaxTextLength = 10000; | 35 const size_t kMaxTextLength = 10000; |
37 | 36 |
38 // The maximum number of scripts a Unicode character can belong to. This value | 37 // The maximum number of scripts a Unicode character can belong to. This value |
39 // is arbitrarily chosen to be a good limit because it is unlikely for a single | 38 // is arbitrarily chosen to be a good limit because it is unlikely for a single |
40 // character to belong to more scripts. | 39 // character to belong to more scripts. |
41 const size_t kMaxScripts = 5; | 40 const size_t kMaxScripts = 5; |
42 | 41 |
43 // Maps from code points to glyph indices in a font. | |
44 typedef std::map<uint32_t, uint16_t> GlyphCache; | |
45 | |
46 // Font data provider for HarfBuzz using Skia. Copied from Blink. | |
47 // TODO(ckocagil): Eliminate the duplication. http://crbug.com/368375 | |
48 struct FontData { | |
49 FontData(GlyphCache* glyph_cache) : glyph_cache_(glyph_cache) {} | |
50 | |
51 SkPaint paint_; | |
52 GlyphCache* glyph_cache_; | |
53 }; | |
54 | |
55 hb_position_t SkiaScalarToHarfBuzzPosition(SkScalar value) { | |
56 return SkScalarToFixed(value); | |
57 } | |
58 | |
59 // Deletes the object at the given pointer after casting it to the given type. | |
60 template<typename Type> | |
61 void DeleteByType(void* data) { | |
62 Type* typed_data = reinterpret_cast<Type*>(data); | |
63 delete typed_data; | |
64 } | |
65 | |
66 template<typename Type> | |
67 void DeleteArrayByType(void* data) { | |
68 Type* typed_data = reinterpret_cast<Type*>(data); | |
69 delete[] typed_data; | |
70 } | |
71 | |
72 // Outputs the |width| and |extents| of the glyph with index |codepoint| in | |
73 // |paint|'s font. | |
74 void GetGlyphWidthAndExtents(SkPaint* paint, | |
75 hb_codepoint_t codepoint, | |
76 hb_position_t* width, | |
77 hb_glyph_extents_t* extents) { | |
78 DCHECK_LE(codepoint, std::numeric_limits<uint16>::max()); | |
79 paint->setTextEncoding(SkPaint::kGlyphID_TextEncoding); | |
80 | |
81 SkScalar sk_width; | |
82 SkRect sk_bounds; | |
83 uint16_t glyph = static_cast<uint16_t>(codepoint); | |
84 | |
85 paint->getTextWidths(&glyph, sizeof(glyph), &sk_width, &sk_bounds); | |
86 if (width) | |
87 *width = SkiaScalarToHarfBuzzPosition(sk_width); | |
88 if (extents) { | |
89 // Invert y-axis because Skia is y-grows-down but we set up HarfBuzz to be | |
90 // y-grows-up. | |
91 extents->x_bearing = SkiaScalarToHarfBuzzPosition(sk_bounds.fLeft); | |
92 extents->y_bearing = SkiaScalarToHarfBuzzPosition(-sk_bounds.fTop); | |
93 extents->width = SkiaScalarToHarfBuzzPosition(sk_bounds.width()); | |
94 extents->height = SkiaScalarToHarfBuzzPosition(-sk_bounds.height()); | |
95 } | |
96 } | |
97 | |
98 // Writes the |glyph| index for the given |unicode| code point. Returns whether | |
99 // the glyph exists, i.e. it is not a missing glyph. | |
100 hb_bool_t GetGlyph(hb_font_t* font, | |
101 void* data, | |
102 hb_codepoint_t unicode, | |
103 hb_codepoint_t variation_selector, | |
104 hb_codepoint_t* glyph, | |
105 void* user_data) { | |
106 FontData* font_data = reinterpret_cast<FontData*>(data); | |
107 GlyphCache* cache = font_data->glyph_cache_; | |
108 | |
109 bool exists = cache->count(unicode) != 0; | |
110 if (!exists) { | |
111 SkPaint* paint = &font_data->paint_; | |
112 paint->setTextEncoding(SkPaint::kUTF32_TextEncoding); | |
113 paint->textToGlyphs(&unicode, sizeof(hb_codepoint_t), &(*cache)[unicode]); | |
114 } | |
115 *glyph = (*cache)[unicode]; | |
116 return !!*glyph; | |
117 } | |
118 | |
119 // Returns the horizontal advance value of the |glyph|. | |
120 hb_position_t GetGlyphHorizontalAdvance(hb_font_t* font, | |
121 void* data, | |
122 hb_codepoint_t glyph, | |
123 void* user_data) { | |
124 FontData* font_data = reinterpret_cast<FontData*>(data); | |
125 hb_position_t advance = 0; | |
126 | |
127 GetGlyphWidthAndExtents(&font_data->paint_, glyph, &advance, 0); | |
128 return advance; | |
129 } | |
130 | |
131 hb_bool_t GetGlyphHorizontalOrigin(hb_font_t* font, | |
132 void* data, | |
133 hb_codepoint_t glyph, | |
134 hb_position_t* x, | |
135 hb_position_t* y, | |
136 void* user_data) { | |
137 // Just return true, like the HarfBuzz-FreeType implementation. | |
138 return true; | |
139 } | |
140 | |
141 hb_position_t GetGlyphKerning(FontData* font_data, | |
142 hb_codepoint_t first_glyph, | |
143 hb_codepoint_t second_glyph) { | |
144 SkTypeface* typeface = font_data->paint_.getTypeface(); | |
145 const uint16_t glyphs[2] = { static_cast<uint16_t>(first_glyph), | |
146 static_cast<uint16_t>(second_glyph) }; | |
147 int32_t kerning_adjustments[1] = { 0 }; | |
148 | |
149 if (!typeface->getKerningPairAdjustments(glyphs, 2, kerning_adjustments)) | |
150 return 0; | |
151 | |
152 SkScalar upm = SkIntToScalar(typeface->getUnitsPerEm()); | |
153 SkScalar size = font_data->paint_.getTextSize(); | |
154 return SkiaScalarToHarfBuzzPosition( | |
155 SkScalarMulDiv(SkIntToScalar(kerning_adjustments[0]), size, upm)); | |
156 } | |
157 | |
158 hb_position_t GetGlyphHorizontalKerning(hb_font_t* font, | |
159 void* data, | |
160 hb_codepoint_t left_glyph, | |
161 hb_codepoint_t right_glyph, | |
162 void* user_data) { | |
163 FontData* font_data = reinterpret_cast<FontData*>(data); | |
164 if (font_data->paint_.isVerticalText()) { | |
165 // We don't support cross-stream kerning. | |
166 return 0; | |
167 } | |
168 | |
169 return GetGlyphKerning(font_data, left_glyph, right_glyph); | |
170 } | |
171 | |
172 hb_position_t GetGlyphVerticalKerning(hb_font_t* font, | |
173 void* data, | |
174 hb_codepoint_t top_glyph, | |
175 hb_codepoint_t bottom_glyph, | |
176 void* user_data) { | |
177 FontData* font_data = reinterpret_cast<FontData*>(data); | |
178 if (!font_data->paint_.isVerticalText()) { | |
179 // We don't support cross-stream kerning. | |
180 return 0; | |
181 } | |
182 | |
183 return GetGlyphKerning(font_data, top_glyph, bottom_glyph); | |
184 } | |
185 | |
186 // Writes the |extents| of |glyph|. | |
187 hb_bool_t GetGlyphExtents(hb_font_t* font, | |
188 void* data, | |
189 hb_codepoint_t glyph, | |
190 hb_glyph_extents_t* extents, | |
191 void* user_data) { | |
192 FontData* font_data = reinterpret_cast<FontData*>(data); | |
193 | |
194 GetGlyphWidthAndExtents(&font_data->paint_, glyph, 0, extents); | |
195 return true; | |
196 } | |
197 | |
198 class FontFuncs { | |
199 public: | |
200 FontFuncs() : font_funcs_(hb_font_funcs_create()) { | |
201 hb_font_funcs_set_glyph_func(font_funcs_, GetGlyph, 0, 0); | |
202 hb_font_funcs_set_glyph_h_advance_func( | |
203 font_funcs_, GetGlyphHorizontalAdvance, 0, 0); | |
204 hb_font_funcs_set_glyph_h_kerning_func( | |
205 font_funcs_, GetGlyphHorizontalKerning, 0, 0); | |
206 hb_font_funcs_set_glyph_h_origin_func( | |
207 font_funcs_, GetGlyphHorizontalOrigin, 0, 0); | |
208 hb_font_funcs_set_glyph_v_kerning_func( | |
209 font_funcs_, GetGlyphVerticalKerning, 0, 0); | |
210 hb_font_funcs_set_glyph_extents_func( | |
211 font_funcs_, GetGlyphExtents, 0, 0); | |
212 hb_font_funcs_make_immutable(font_funcs_); | |
213 } | |
214 | |
215 ~FontFuncs() { | |
216 hb_font_funcs_destroy(font_funcs_); | |
217 } | |
218 | |
219 hb_font_funcs_t* get() { return font_funcs_; } | |
220 | |
221 private: | |
222 hb_font_funcs_t* font_funcs_; | |
223 | |
224 DISALLOW_COPY_AND_ASSIGN(FontFuncs); | |
225 }; | |
226 | |
227 base::LazyInstance<FontFuncs>::Leaky g_font_funcs = LAZY_INSTANCE_INITIALIZER; | |
228 | |
229 // Returns the raw data of the font table |tag|. | |
230 hb_blob_t* GetFontTable(hb_face_t* face, hb_tag_t tag, void* user_data) { | |
231 SkTypeface* typeface = reinterpret_cast<SkTypeface*>(user_data); | |
232 | |
233 const size_t table_size = typeface->getTableSize(tag); | |
234 if (!table_size) | |
235 return 0; | |
236 | |
237 scoped_ptr<char[]> buffer(new char[table_size]); | |
238 if (!buffer) | |
239 return 0; | |
240 size_t actual_size = typeface->getTableData(tag, 0, table_size, buffer.get()); | |
241 if (table_size != actual_size) | |
242 return 0; | |
243 | |
244 char* buffer_raw = buffer.release(); | |
245 return hb_blob_create(buffer_raw, table_size, HB_MEMORY_MODE_WRITABLE, | |
246 buffer_raw, DeleteArrayByType<char>); | |
247 } | |
248 | |
249 void UnrefSkTypeface(void* data) { | |
250 SkTypeface* skia_face = reinterpret_cast<SkTypeface*>(data); | |
251 SkSafeUnref(skia_face); | |
252 } | |
253 | |
254 // Wrapper class for a HarfBuzz face created from a given Skia face. | |
255 class HarfBuzzFace { | |
256 public: | |
257 HarfBuzzFace() : face_(NULL) {} | |
258 | |
259 ~HarfBuzzFace() { | |
260 if (face_) | |
261 hb_face_destroy(face_); | |
262 } | |
263 | |
264 void Init(SkTypeface* skia_face) { | |
265 SkSafeRef(skia_face); | |
266 face_ = hb_face_create_for_tables(GetFontTable, skia_face, UnrefSkTypeface); | |
267 DCHECK(face_); | |
268 } | |
269 | |
270 hb_face_t* get() { | |
271 return face_; | |
272 } | |
273 | |
274 private: | |
275 hb_face_t* face_; | |
276 }; | |
277 | |
278 // Creates a HarfBuzz font from the given Skia face and text size. | |
279 hb_font_t* CreateHarfBuzzFont(SkTypeface* skia_face, | |
280 SkScalar text_size, | |
281 const FontRenderParams& params, | |
282 bool background_is_transparent) { | |
283 typedef std::pair<HarfBuzzFace, GlyphCache> FaceCache; | |
284 | |
285 // TODO(ckocagil): This shouldn't grow indefinitely. Maybe use base::MRUCache? | |
286 static std::map<SkFontID, FaceCache> face_caches; | |
287 | |
288 FaceCache* face_cache = &face_caches[skia_face->uniqueID()]; | |
289 if (face_cache->first.get() == NULL) | |
290 face_cache->first.Init(skia_face); | |
291 | |
292 hb_font_t* harfbuzz_font = hb_font_create(face_cache->first.get()); | |
293 const int scale = SkScalarToFixed(text_size); | |
294 hb_font_set_scale(harfbuzz_font, scale, scale); | |
295 FontData* hb_font_data = new FontData(&face_cache->second); | |
296 hb_font_data->paint_.setTypeface(skia_face); | |
297 hb_font_data->paint_.setTextSize(text_size); | |
298 // TODO(ckocagil): Do we need to update these params later? | |
299 internal::ApplyRenderParams(params, background_is_transparent, | |
300 &hb_font_data->paint_); | |
301 hb_font_set_funcs(harfbuzz_font, g_font_funcs.Get().get(), hb_font_data, | |
302 DeleteByType<FontData>); | |
303 hb_font_make_immutable(harfbuzz_font); | |
304 return harfbuzz_font; | |
305 } | |
306 | |
307 // Returns true if characters of |block_code| may trigger font fallback. | 42 // Returns true if characters of |block_code| may trigger font fallback. |
308 // Dingbats and emoticons can be rendered through the color emoji font file, | 43 // Dingbats and emoticons can be rendered through the color emoji font file, |
309 // therefore it needs to be trigerred as fallbacks. See crbug.com/448909 | 44 // therefore it needs to be trigerred as fallbacks. See crbug.com/448909 |
310 bool IsUnusualBlockCode(UBlockCode block_code) { | 45 bool IsUnusualBlockCode(UBlockCode block_code) { |
311 return block_code == UBLOCK_GEOMETRIC_SHAPES || | 46 return block_code == UBLOCK_GEOMETRIC_SHAPES || |
312 block_code == UBLOCK_MISCELLANEOUS_SYMBOLS || | 47 block_code == UBLOCK_MISCELLANEOUS_SYMBOLS || |
313 block_code == UBLOCK_DINGBATS || | 48 block_code == UBLOCK_DINGBATS || |
314 block_code == UBLOCK_EMOTICONS; | 49 block_code == UBLOCK_EMOTICONS; |
315 } | 50 } |
316 | 51 |
(...skipping 1016 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1333 if (!run->render_params.subpixel_positioning) | 1068 if (!run->render_params.subpixel_positioning) |
1334 run->width = std::floor(run->width + 0.5f); | 1069 run->width = std::floor(run->width + 0.5f); |
1335 } | 1070 } |
1336 | 1071 |
1337 hb_buffer_destroy(buffer); | 1072 hb_buffer_destroy(buffer); |
1338 hb_font_destroy(harfbuzz_font); | 1073 hb_font_destroy(harfbuzz_font); |
1339 return true; | 1074 return true; |
1340 } | 1075 } |
1341 | 1076 |
1342 } // namespace gfx | 1077 } // namespace gfx |
OLD | NEW |