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