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

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

Issue 891183003: Extract the Skia implementation of HarfBuzz font to its own file (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: rebased Created 5 years, 10 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
« no previous file with comments | « ui/gfx/harfbuzz_font_skia.cc ('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 <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
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
OLDNEW
« no previous file with comments | « ui/gfx/harfbuzz_font_skia.cc ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698