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 <stddef.h>
8 #include <stdint.h>
9
10 #include <limits>
11 #include <map>
12
13 #include "base/containers/mru_cache.h"
14 #include "base/lazy_instance.h"
15 #include "base/logging.h"
16 #include "base/macros.h"
17 #include "base/no_destructor.h"
18 #include "build/build_config.h"
19 #include "third_party/skia/include/core/SkFont.h"
20 #include "third_party/skia/include/core/SkTypeface.h"
21 #include "ui/gfx/render_text.h"
22 #include "ui/gfx/skia_util.h"
23
24 namespace gfx {
25
26 namespace {
27
28 class TypefaceData;
29
30 // Maps from code points to glyph indices in a font.
31 using GlyphCache = std::map<uint32_t, uint16_t>;
32
33 // Wraps a custom user data attached to a hb_font object. Font data provider for
34 // HarfBuzz using Skia. Copied from Blink.
35 // TODO(ckocagil): Eliminate the duplication. http://crbug.com/368375
36 struct FontData {
FontDatagfx::__anon8f85943e0111::FontData37 explicit FontData(GlyphCache* glyph_cache) : glyph_cache_(glyph_cache) {}
38
39 SkFont font_;
40 GlyphCache* glyph_cache_;
41 };
42
43 // Deletes the object at the given pointer after casting it to the given type.
44 template<typename Type>
DeleteByType(void * data)45 void DeleteByType(void* data) {
46 Type* typed_data = reinterpret_cast<Type*>(data);
47 delete typed_data;
48 }
49
50 template<typename Type>
DeleteArrayByType(void * data)51 void DeleteArrayByType(void* data) {
52 Type* typed_data = reinterpret_cast<Type*>(data);
53 delete[] typed_data;
54 }
55
56 // Outputs the |width| and |extents| of the glyph with index |codepoint| in
57 // |paint|'s font.
GetGlyphWidthAndExtents(const SkFont & font,hb_codepoint_t codepoint,hb_position_t * width,hb_glyph_extents_t * extents)58 void GetGlyphWidthAndExtents(const SkFont& font,
59 hb_codepoint_t codepoint,
60 hb_position_t* width,
61 hb_glyph_extents_t* extents) {
62 DCHECK_LE(codepoint, std::numeric_limits<uint16_t>::max());
63
64 SkScalar sk_width;
65 SkRect sk_bounds;
66 uint16_t glyph = static_cast<uint16_t>(codepoint);
67
68 font.getWidths(&glyph, 1, &sk_width, &sk_bounds);
69 if (width)
70 *width = SkiaScalarToHarfBuzzUnits(sk_width);
71 if (extents) {
72 // Invert y-axis because Skia is y-grows-down but we set up HarfBuzz to be
73 // y-grows-up.
74 extents->x_bearing = SkiaScalarToHarfBuzzUnits(sk_bounds.fLeft);
75 extents->y_bearing = SkiaScalarToHarfBuzzUnits(-sk_bounds.fTop);
76 extents->width = SkiaScalarToHarfBuzzUnits(sk_bounds.width());
77 extents->height = SkiaScalarToHarfBuzzUnits(-sk_bounds.height());
78 }
79 }
80
81 // Writes the |glyph| index for the given |unicode| code point. Returns whether
82 // the glyph exists, i.e. it is not a missing glyph.
GetGlyph(hb_font_t * font,void * data,hb_codepoint_t unicode,hb_codepoint_t variation_selector,hb_codepoint_t * glyph,void * user_data)83 hb_bool_t GetGlyph(hb_font_t* font,
84 void* data,
85 hb_codepoint_t unicode,
86 hb_codepoint_t variation_selector,
87 hb_codepoint_t* glyph,
88 void* user_data) {
89 FontData* font_data = reinterpret_cast<FontData*>(data);
90 GlyphCache* cache = font_data->glyph_cache_;
91
92 GlyphCache::iterator iter = cache->find(unicode);
93 if (iter == cache->end()) {
94 auto result = cache->insert(
95 std::make_pair(unicode, font_data->font_.unicharToGlyph(unicode)));
96 DCHECK(result.second);
97 iter = result.first;
98 }
99
100 *glyph = iter->second;
101 return !!*glyph;
102 }
103
GetNominalGlyph(hb_font_t * font,void * data,hb_codepoint_t unicode,hb_codepoint_t * glyph,void * user_data)104 hb_bool_t GetNominalGlyph(hb_font_t* font,
105 void* data,
106 hb_codepoint_t unicode,
107 hb_codepoint_t* glyph,
108 void* user_data) {
109 return GetGlyph(font, data, unicode, 0, glyph, user_data);
110 }
111
112 // Returns the horizontal advance value of the |glyph|.
GetGlyphHorizontalAdvance(hb_font_t * font,void * data,hb_codepoint_t glyph,void * user_data)113 hb_position_t GetGlyphHorizontalAdvance(hb_font_t* font,
114 void* data,
115 hb_codepoint_t glyph,
116 void* user_data) {
117 FontData* font_data = reinterpret_cast<FontData*>(data);
118 hb_position_t advance = 0;
119
120 GetGlyphWidthAndExtents(font_data->font_, glyph, &advance, 0);
121 return advance;
122 }
123
GetGlyphHorizontalOrigin(hb_font_t * font,void * data,hb_codepoint_t glyph,hb_position_t * x,hb_position_t * y,void * user_data)124 hb_bool_t GetGlyphHorizontalOrigin(hb_font_t* font,
125 void* data,
126 hb_codepoint_t glyph,
127 hb_position_t* x,
128 hb_position_t* y,
129 void* user_data) {
130 // Just return true, like the HarfBuzz-FreeType implementation.
131 return true;
132 }
133
GetGlyphKerning(FontData * font_data,hb_codepoint_t first_glyph,hb_codepoint_t second_glyph)134 hb_position_t GetGlyphKerning(FontData* font_data,
135 hb_codepoint_t first_glyph,
136 hb_codepoint_t second_glyph) {
137 SkTypeface* typeface = font_data->font_.getTypeface();
138 const uint16_t glyphs[2] = { static_cast<uint16_t>(first_glyph),
139 static_cast<uint16_t>(second_glyph) };
140 int32_t kerning_adjustments[1] = { 0 };
141
142 if (!typeface->getKerningPairAdjustments(glyphs, 2, kerning_adjustments))
143 return 0;
144
145 SkScalar upm = SkIntToScalar(typeface->getUnitsPerEm());
146 SkScalar size = font_data->font_.getSize();
147 return SkiaScalarToHarfBuzzUnits(SkIntToScalar(kerning_adjustments[0]) *
148 size / upm);
149 }
150
GetGlyphHorizontalKerning(hb_font_t * font,void * data,hb_codepoint_t left_glyph,hb_codepoint_t right_glyph,void * user_data)151 hb_position_t GetGlyphHorizontalKerning(hb_font_t* font,
152 void* data,
153 hb_codepoint_t left_glyph,
154 hb_codepoint_t right_glyph,
155 void* user_data) {
156 FontData* font_data = reinterpret_cast<FontData*>(data);
157 return GetGlyphKerning(font_data, left_glyph, right_glyph);
158 }
159
GetGlyphVerticalKerning(hb_font_t * font,void * data,hb_codepoint_t top_glyph,hb_codepoint_t bottom_glyph,void * user_data)160 hb_position_t GetGlyphVerticalKerning(hb_font_t* font,
161 void* data,
162 hb_codepoint_t top_glyph,
163 hb_codepoint_t bottom_glyph,
164 void* user_data) {
165 FontData* font_data = reinterpret_cast<FontData*>(data);
166 return GetGlyphKerning(font_data, top_glyph, bottom_glyph);
167 }
168
169 // Writes the |extents| of |glyph|.
GetGlyphExtents(hb_font_t * font,void * data,hb_codepoint_t glyph,hb_glyph_extents_t * extents,void * user_data)170 hb_bool_t GetGlyphExtents(hb_font_t* font,
171 void* data,
172 hb_codepoint_t glyph,
173 hb_glyph_extents_t* extents,
174 void* user_data) {
175 FontData* font_data = reinterpret_cast<FontData*>(data);
176
177 GetGlyphWidthAndExtents(font_data->font_, glyph, 0, extents);
178 return true;
179 }
180
181 class FontFuncs {
182 public:
FontFuncs()183 FontFuncs() : font_funcs_(hb_font_funcs_create()) {
184 hb_font_funcs_set_variation_glyph_func(font_funcs_, GetGlyph, 0, 0);
185 hb_font_funcs_set_nominal_glyph_func(font_funcs_, GetNominalGlyph, 0, 0);
186 hb_font_funcs_set_glyph_h_advance_func(
187 font_funcs_, GetGlyphHorizontalAdvance, 0, 0);
188 hb_font_funcs_set_glyph_h_kerning_func(
189 font_funcs_, GetGlyphHorizontalKerning, 0, 0);
190 hb_font_funcs_set_glyph_h_origin_func(
191 font_funcs_, GetGlyphHorizontalOrigin, 0, 0);
192 hb_font_funcs_set_glyph_v_kerning_func(
193 font_funcs_, GetGlyphVerticalKerning, 0, 0);
194 hb_font_funcs_set_glyph_extents_func(
195 font_funcs_, GetGlyphExtents, 0, 0);
196 hb_font_funcs_make_immutable(font_funcs_);
197 }
198
~FontFuncs()199 ~FontFuncs() {
200 hb_font_funcs_destroy(font_funcs_);
201 }
202
get()203 hb_font_funcs_t* get() { return font_funcs_; }
204
205 private:
206 hb_font_funcs_t* font_funcs_;
207
208 DISALLOW_COPY_AND_ASSIGN(FontFuncs);
209 };
210
211 base::LazyInstance<FontFuncs>::Leaky g_font_funcs = LAZY_INSTANCE_INITIALIZER;
212
213 // Returns the raw data of the font table |tag|.
GetFontTable(hb_face_t * face,hb_tag_t tag,void * user_data)214 hb_blob_t* GetFontTable(hb_face_t* face, hb_tag_t tag, void* user_data) {
215 SkTypeface* typeface = reinterpret_cast<SkTypeface*>(user_data);
216
217 const size_t table_size = typeface->getTableSize(tag);
218 if (!table_size)
219 return 0;
220
221 std::unique_ptr<char[]> buffer(new char[table_size]);
222 if (!buffer)
223 return 0;
224 size_t actual_size = typeface->getTableData(tag, 0, table_size, buffer.get());
225 if (table_size != actual_size)
226 return 0;
227
228 char* buffer_raw = buffer.release();
229 return hb_blob_create(buffer_raw, static_cast<uint32_t>(table_size),
230 HB_MEMORY_MODE_WRITABLE, buffer_raw,
231 DeleteArrayByType<char>);
232 }
233
234 // For a given skia typeface, maps to its harfbuzz face and its glyphs cache.
235 class TypefaceData {
236 public:
TypefaceData(sk_sp<SkTypeface> skia_face)237 explicit TypefaceData(sk_sp<SkTypeface> skia_face) : sk_typeface_(skia_face) {
238 face_ = hb_face_create_for_tables(GetFontTable, skia_face.get(), nullptr);
239 DCHECK(face_);
240 }
241
TypefaceData(TypefaceData && data)242 TypefaceData(TypefaceData&& data) {
243 face_ = data.face_;
244 glyphs_ = std::move(data.glyphs_);
245 data.face_ = nullptr;
246 }
247
~TypefaceData()248 ~TypefaceData() { hb_face_destroy(face_); }
249
face()250 hb_face_t* face() { return face_; }
glyphs()251 GlyphCache* glyphs() { return &glyphs_; }
252
253 private:
254 TypefaceData() = delete;
255
256 GlyphCache glyphs_;
257 hb_face_t* face_ = nullptr;
258
259 // The skia typeface must outlive |face_| since it's being used by harfbuzz.
260 sk_sp<SkTypeface> sk_typeface_;
261
262 DISALLOW_COPY_AND_ASSIGN(TypefaceData);
263 };
264
265 } // namespace
266
267 // Creates a HarfBuzz font from the given Skia face and text size.
CreateHarfBuzzFont(sk_sp<SkTypeface> skia_face,SkScalar text_size,const FontRenderParams & params,bool subpixel_rendering_suppressed)268 hb_font_t* CreateHarfBuzzFont(sk_sp<SkTypeface> skia_face,
269 SkScalar text_size,
270 const FontRenderParams& params,
271 bool subpixel_rendering_suppressed) {
272 // A cache from Skia font to harfbuzz typeface information.
273 using TypefaceCache = base::MRUCache<SkFontID, TypefaceData>;
274
275 constexpr int kTypefaceCacheSize = 64;
276 static base::NoDestructor<TypefaceCache> face_caches(kTypefaceCacheSize);
277
278 TypefaceCache* typeface_cache = face_caches.get();
279 TypefaceCache::iterator typeface_data =
280 typeface_cache->Get(skia_face->uniqueID());
281 if (typeface_data == typeface_cache->end()) {
282 TypefaceData new_typeface_data(skia_face);
283 typeface_data = typeface_cache->Put(skia_face->uniqueID(),
284 std::move(new_typeface_data));
285 }
286
287 DCHECK(typeface_data->second.face());
288 hb_font_t* harfbuzz_font = hb_font_create(typeface_data->second.face());
289
290 const int scale = SkiaScalarToHarfBuzzUnits(text_size);
291 hb_font_set_scale(harfbuzz_font, scale, scale);
292 FontData* hb_font_data = new FontData(typeface_data->second.glyphs());
293 hb_font_data->font_.setTypeface(std::move(skia_face));
294 hb_font_data->font_.setSize(text_size);
295 // TODO(ckocagil): Do we need to update these params later?
296 internal::ApplyRenderParams(params, subpixel_rendering_suppressed,
297 &hb_font_data->font_);
298 hb_font_set_funcs(harfbuzz_font, g_font_funcs.Get().get(), hb_font_data,
299 DeleteByType<FontData>);
300 hb_font_make_immutable(harfbuzz_font);
301 return harfbuzz_font;
302 }
303
304 } // namespace gfx
305