OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2015 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "ui/gfx/harfbuzz_font_skia.h" | |
6 | |
7 #include <limits> | |
8 #include <map> | |
9 | |
10 #include "base/lazy_instance.h" | |
11 #include "base/logging.h" | |
12 #include "third_party/skia/include/core/SkPaint.h" | |
13 #include "third_party/skia/include/core/SkTypeface.h" | |
14 #include "ui/gfx/render_text.h" | |
15 | |
16 // Font data provider for HarfBuzz using Skia. Copied from Blink. | |
msw
2015/02/03 20:50:32
nit: keep this comment with the FontData struct.
ckocagil
2015/02/05 04:56:44
Done.
| |
17 // TODO(ckocagil): Eliminate the duplication. http://crbug.com/368375 | |
18 | |
19 namespace gfx { | |
20 | |
21 namespace { | |
22 | |
23 class HarfBuzzFace; | |
24 | |
25 // Maps from code points to glyph indices in a font. | |
26 typedef std::map<uint32_t, uint16_t> GlyphCache; | |
27 | |
28 typedef std::pair<HarfBuzzFace, GlyphCache> FaceCache; | |
29 | |
30 // TODO(ckocagil): This shouldn't grow indefinitely. Maybe use base::MRUCache? | |
31 static std::map<SkFontID, FaceCache> g_face_caches; | |
msw
2015/02/03 20:50:32
Can this remain a static member of CreateHarfBuzzF
ckocagil
2015/02/05 04:56:44
Done.
| |
32 | |
33 struct FontData { | |
34 FontData(GlyphCache* glyph_cache) : glyph_cache_(glyph_cache) {} | |
35 | |
36 SkPaint paint_; | |
37 GlyphCache* glyph_cache_; | |
38 }; | |
39 | |
40 // Deletes the object at the given pointer after casting it to the given type. | |
41 template<typename Type> | |
42 void DeleteByType(void* data) { | |
43 Type* typed_data = reinterpret_cast<Type*>(data); | |
44 delete typed_data; | |
45 } | |
46 | |
47 template<typename Type> | |
48 void DeleteArrayByType(void* data) { | |
49 Type* typed_data = reinterpret_cast<Type*>(data); | |
50 delete[] typed_data; | |
51 } | |
52 | |
53 // Outputs the |width| and |extents| of the glyph with index |codepoint| in | |
54 // |paint|'s font. | |
55 void GetGlyphWidthAndExtents(SkPaint* paint, | |
56 hb_codepoint_t codepoint, | |
57 hb_position_t* width, | |
58 hb_glyph_extents_t* extents) { | |
59 DCHECK_LE(codepoint, std::numeric_limits<uint16_t>::max()); | |
60 paint->setTextEncoding(SkPaint::kGlyphID_TextEncoding); | |
61 | |
62 SkScalar sk_width; | |
63 SkRect sk_bounds; | |
64 uint16_t glyph = static_cast<uint16_t>(codepoint); | |
65 | |
66 paint->getTextWidths(&glyph, sizeof(glyph), &sk_width, &sk_bounds); | |
67 if (width) | |
68 *width = SkScalarToFixed(sk_width); | |
69 if (extents) { | |
70 // Invert y-axis because Skia is y-grows-down but we set up HarfBuzz to be | |
71 // y-grows-up. | |
72 extents->x_bearing = SkScalarToFixed(sk_bounds.fLeft); | |
73 extents->y_bearing = SkScalarToFixed(-sk_bounds.fTop); | |
74 extents->width = SkScalarToFixed(sk_bounds.width()); | |
75 extents->height = SkScalarToFixed(-sk_bounds.height()); | |
76 } | |
77 } | |
78 | |
79 // Writes the |glyph| index for the given |unicode| code point. Returns whether | |
80 // the glyph exists, i.e. it is not a missing glyph. | |
81 hb_bool_t GetGlyph(hb_font_t* font, | |
82 void* data, | |
83 hb_codepoint_t unicode, | |
84 hb_codepoint_t variation_selector, | |
85 hb_codepoint_t* glyph, | |
86 void* user_data) { | |
87 FontData* font_data = reinterpret_cast<FontData*>(data); | |
88 GlyphCache* cache = font_data->glyph_cache_; | |
89 | |
90 bool exists = cache->count(unicode) != 0; | |
91 if (!exists) { | |
92 SkPaint* paint = &font_data->paint_; | |
93 paint->setTextEncoding(SkPaint::kUTF32_TextEncoding); | |
94 paint->textToGlyphs(&unicode, sizeof(hb_codepoint_t), &(*cache)[unicode]); | |
95 } | |
96 *glyph = (*cache)[unicode]; | |
97 return !!*glyph; | |
98 } | |
99 | |
100 // Returns the horizontal advance value of the |glyph|. | |
101 hb_position_t GetGlyphHorizontalAdvance(hb_font_t* font, | |
102 void* data, | |
103 hb_codepoint_t glyph, | |
104 void* user_data) { | |
105 FontData* font_data = reinterpret_cast<FontData*>(data); | |
106 hb_position_t advance = 0; | |
107 | |
108 GetGlyphWidthAndExtents(&font_data->paint_, glyph, &advance, 0); | |
109 return advance; | |
110 } | |
111 | |
112 hb_bool_t GetGlyphHorizontalOrigin(hb_font_t* font, | |
113 void* data, | |
114 hb_codepoint_t glyph, | |
115 hb_position_t* x, | |
116 hb_position_t* y, | |
117 void* user_data) { | |
118 // Just return true, like the HarfBuzz-FreeType implementation. | |
119 return true; | |
120 } | |
121 | |
122 hb_position_t GetGlyphKerning(FontData* font_data, | |
123 hb_codepoint_t first_glyph, | |
124 hb_codepoint_t second_glyph) { | |
125 SkTypeface* typeface = font_data->paint_.getTypeface(); | |
126 const uint16_t glyphs[2] = { static_cast<uint16_t>(first_glyph), | |
127 static_cast<uint16_t>(second_glyph) }; | |
128 int32_t kerning_adjustments[1] = { 0 }; | |
129 | |
130 if (!typeface->getKerningPairAdjustments(glyphs, 2, kerning_adjustments)) | |
131 return 0; | |
132 | |
133 SkScalar upm = SkIntToScalar(typeface->getUnitsPerEm()); | |
134 SkScalar size = font_data->paint_.getTextSize(); | |
135 return SkScalarToFixed( | |
136 SkScalarMulDiv(SkIntToScalar(kerning_adjustments[0]), size, upm)); | |
137 } | |
138 | |
139 hb_position_t GetGlyphHorizontalKerning(hb_font_t* font, | |
140 void* data, | |
141 hb_codepoint_t left_glyph, | |
142 hb_codepoint_t right_glyph, | |
143 void* user_data) { | |
144 FontData* font_data = reinterpret_cast<FontData*>(data); | |
145 if (font_data->paint_.isVerticalText()) { | |
146 // We don't support cross-stream kerning. | |
147 return 0; | |
148 } | |
149 | |
150 return GetGlyphKerning(font_data, left_glyph, right_glyph); | |
151 } | |
152 | |
153 hb_position_t GetGlyphVerticalKerning(hb_font_t* font, | |
154 void* data, | |
155 hb_codepoint_t top_glyph, | |
156 hb_codepoint_t bottom_glyph, | |
157 void* user_data) { | |
158 FontData* font_data = reinterpret_cast<FontData*>(data); | |
159 if (!font_data->paint_.isVerticalText()) { | |
160 // We don't support cross-stream kerning. | |
161 return 0; | |
162 } | |
163 | |
164 return GetGlyphKerning(font_data, top_glyph, bottom_glyph); | |
165 } | |
166 | |
167 // Writes the |extents| of |glyph|. | |
168 hb_bool_t GetGlyphExtents(hb_font_t* font, | |
169 void* data, | |
170 hb_codepoint_t glyph, | |
171 hb_glyph_extents_t* extents, | |
172 void* user_data) { | |
173 FontData* font_data = reinterpret_cast<FontData*>(data); | |
174 | |
175 GetGlyphWidthAndExtents(&font_data->paint_, glyph, 0, extents); | |
176 return true; | |
177 } | |
178 | |
179 class FontFuncs { | |
180 public: | |
181 FontFuncs() : font_funcs_(hb_font_funcs_create()) { | |
182 hb_font_funcs_set_glyph_func(font_funcs_, GetGlyph, 0, 0); | |
183 hb_font_funcs_set_glyph_h_advance_func( | |
184 font_funcs_, GetGlyphHorizontalAdvance, 0, 0); | |
185 hb_font_funcs_set_glyph_h_kerning_func( | |
186 font_funcs_, GetGlyphHorizontalKerning, 0, 0); | |
187 hb_font_funcs_set_glyph_h_origin_func( | |
188 font_funcs_, GetGlyphHorizontalOrigin, 0, 0); | |
189 hb_font_funcs_set_glyph_v_kerning_func( | |
190 font_funcs_, GetGlyphVerticalKerning, 0, 0); | |
191 hb_font_funcs_set_glyph_extents_func( | |
192 font_funcs_, GetGlyphExtents, 0, 0); | |
193 hb_font_funcs_make_immutable(font_funcs_); | |
194 } | |
195 | |
196 ~FontFuncs() { | |
197 hb_font_funcs_destroy(font_funcs_); | |
198 } | |
199 | |
200 hb_font_funcs_t* get() { return font_funcs_; } | |
201 | |
202 private: | |
203 hb_font_funcs_t* font_funcs_; | |
204 | |
205 DISALLOW_COPY_AND_ASSIGN(FontFuncs); | |
206 }; | |
207 | |
208 base::LazyInstance<FontFuncs>::Leaky g_font_funcs = LAZY_INSTANCE_INITIALIZER; | |
209 | |
210 // Returns the raw data of the font table |tag|. | |
211 hb_blob_t* GetFontTable(hb_face_t* face, hb_tag_t tag, void* user_data) { | |
212 SkTypeface* typeface = reinterpret_cast<SkTypeface*>(user_data); | |
213 | |
214 const size_t table_size = typeface->getTableSize(tag); | |
215 if (!table_size) | |
216 return 0; | |
217 | |
218 scoped_ptr<char[]> buffer(new char[table_size]); | |
219 if (!buffer) | |
220 return 0; | |
221 size_t actual_size = typeface->getTableData(tag, 0, table_size, buffer.get()); | |
222 if (table_size != actual_size) | |
223 return 0; | |
224 | |
225 char* buffer_raw = buffer.release(); | |
226 return hb_blob_create(buffer_raw, table_size, HB_MEMORY_MODE_WRITABLE, | |
227 buffer_raw, DeleteArrayByType<char>); | |
228 } | |
229 | |
230 void UnrefSkTypeface(void* data) { | |
231 SkTypeface* skia_face = reinterpret_cast<SkTypeface*>(data); | |
232 SkSafeUnref(skia_face); | |
233 } | |
234 | |
235 // Wrapper class for a HarfBuzz face created from a given Skia face. | |
236 class HarfBuzzFace { | |
237 public: | |
238 HarfBuzzFace() : face_(NULL) {} | |
239 | |
240 ~HarfBuzzFace() { | |
241 if (face_) | |
242 hb_face_destroy(face_); | |
243 } | |
244 | |
245 void Init(SkTypeface* skia_face) { | |
246 SkSafeRef(skia_face); | |
247 face_ = hb_face_create_for_tables(GetFontTable, skia_face, UnrefSkTypeface); | |
248 DCHECK(face_); | |
249 } | |
250 | |
251 hb_face_t* get() { | |
252 return face_; | |
253 } | |
254 | |
255 private: | |
256 hb_face_t* face_; | |
257 }; | |
258 | |
259 } // namespace | |
260 | |
261 // Creates a HarfBuzz font from the given Skia face and text size. | |
262 hb_font_t* CreateHarfBuzzFont(SkTypeface* skia_face, | |
263 SkScalar text_size, | |
264 const FontRenderParams& params, | |
265 bool background_is_transparent) { | |
266 FaceCache* face_cache = &g_face_caches[skia_face->uniqueID()]; | |
267 if (face_cache->first.get() == NULL) | |
268 face_cache->first.Init(skia_face); | |
269 | |
270 hb_font_t* harfbuzz_font = hb_font_create(face_cache->first.get()); | |
271 const int scale = SkScalarToFixed(text_size); | |
272 hb_font_set_scale(harfbuzz_font, scale, scale); | |
273 FontData* hb_font_data = new FontData(&face_cache->second); | |
274 hb_font_data->paint_.setTypeface(skia_face); | |
275 hb_font_data->paint_.setTextSize(text_size); | |
276 // TODO(ckocagil): Do we need to update these params later? | |
277 internal::ApplyRenderParams(params, background_is_transparent, | |
278 &hb_font_data->paint_); | |
279 hb_font_set_funcs(harfbuzz_font, g_font_funcs.Get().get(), hb_font_data, | |
280 DeleteByType<FontData>); | |
281 hb_font_make_immutable(harfbuzz_font); | |
282 return harfbuzz_font; | |
283 } | |
284 | |
285 } // namespace gfx | |
OLD | NEW |