// Copyright 2015 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "ui/gfx/harfbuzz_font_skia.h" #include #include #include #include #include "base/check_op.h" #include "base/containers/mru_cache.h" #include "base/lazy_instance.h" #include "base/macros.h" #include "base/no_destructor.h" #include "build/build_config.h" #include "third_party/skia/include/core/SkFont.h" #include "third_party/skia/include/core/SkTypeface.h" #include "ui/gfx/render_text.h" #include "ui/gfx/skia_util.h" namespace gfx { namespace { class TypefaceData; // Maps from code points to glyph indices in a font. using GlyphCache = std::map; // Wraps a custom user data attached to a hb_font object. Font data provider for // HarfBuzz using Skia. Copied from Blink. // TODO(ckocagil): Eliminate the duplication. http://crbug.com/368375 struct FontData { explicit FontData(GlyphCache* glyph_cache) : glyph_cache_(glyph_cache) {} SkFont font_; GlyphCache* glyph_cache_; }; // Deletes the object at the given pointer after casting it to the given type. template void DeleteByType(void* data) { Type* typed_data = reinterpret_cast(data); delete typed_data; } template void DeleteArrayByType(void* data) { Type* typed_data = reinterpret_cast(data); delete[] typed_data; } // Outputs the |width| and |extents| of the glyph with index |codepoint| in // |paint|'s font. void GetGlyphWidthAndExtents(const SkFont& font, hb_codepoint_t codepoint, hb_position_t* width, hb_glyph_extents_t* extents) { DCHECK_LE(codepoint, std::numeric_limits::max()); SkScalar sk_width; SkRect sk_bounds; uint16_t glyph = static_cast(codepoint); font.getWidths(&glyph, 1, &sk_width, &sk_bounds); if (width) *width = SkiaScalarToHarfBuzzUnits(sk_width); if (extents) { // Invert y-axis because Skia is y-grows-down but we set up HarfBuzz to be // y-grows-up. extents->x_bearing = SkiaScalarToHarfBuzzUnits(sk_bounds.fLeft); extents->y_bearing = SkiaScalarToHarfBuzzUnits(-sk_bounds.fTop); extents->width = SkiaScalarToHarfBuzzUnits(sk_bounds.width()); extents->height = SkiaScalarToHarfBuzzUnits(-sk_bounds.height()); } } // Writes the |glyph| index for the given |unicode| code point. Returns whether // the glyph exists, i.e. it is not a missing glyph. hb_bool_t GetGlyph(hb_font_t* font, void* data, hb_codepoint_t unicode, hb_codepoint_t variation_selector, hb_codepoint_t* glyph, void* user_data) { FontData* font_data = reinterpret_cast(data); GlyphCache* cache = font_data->glyph_cache_; GlyphCache::iterator iter = cache->find(unicode); if (iter == cache->end()) { auto result = cache->insert( std::make_pair(unicode, font_data->font_.unicharToGlyph(unicode))); DCHECK(result.second); iter = result.first; } *glyph = iter->second; return !!*glyph; } hb_bool_t GetNominalGlyph(hb_font_t* font, void* data, hb_codepoint_t unicode, hb_codepoint_t* glyph, void* user_data) { return GetGlyph(font, data, unicode, 0, glyph, user_data); } // Returns the horizontal advance value of the |glyph|. hb_position_t GetGlyphHorizontalAdvance(hb_font_t* font, void* data, hb_codepoint_t glyph, void* user_data) { FontData* font_data = reinterpret_cast(data); hb_position_t advance = 0; GetGlyphWidthAndExtents(font_data->font_, glyph, &advance, 0); return advance; } hb_bool_t GetGlyphHorizontalOrigin(hb_font_t* font, void* data, hb_codepoint_t glyph, hb_position_t* x, hb_position_t* y, void* user_data) { // Just return true, like the HarfBuzz-FreeType implementation. return true; } hb_position_t GetGlyphKerning(FontData* font_data, hb_codepoint_t first_glyph, hb_codepoint_t second_glyph) { SkTypeface* typeface = font_data->font_.getTypeface(); const uint16_t glyphs[2] = { static_cast(first_glyph), static_cast(second_glyph) }; int32_t kerning_adjustments[1] = { 0 }; if (!typeface->getKerningPairAdjustments(glyphs, 2, kerning_adjustments)) return 0; SkScalar upm = SkIntToScalar(typeface->getUnitsPerEm()); SkScalar size = font_data->font_.getSize(); return SkiaScalarToHarfBuzzUnits(SkIntToScalar(kerning_adjustments[0]) * size / upm); } hb_position_t GetGlyphHorizontalKerning(hb_font_t* font, void* data, hb_codepoint_t left_glyph, hb_codepoint_t right_glyph, void* user_data) { FontData* font_data = reinterpret_cast(data); return GetGlyphKerning(font_data, left_glyph, right_glyph); } hb_position_t GetGlyphVerticalKerning(hb_font_t* font, void* data, hb_codepoint_t top_glyph, hb_codepoint_t bottom_glyph, void* user_data) { FontData* font_data = reinterpret_cast(data); return GetGlyphKerning(font_data, top_glyph, bottom_glyph); } // Writes the |extents| of |glyph|. hb_bool_t GetGlyphExtents(hb_font_t* font, void* data, hb_codepoint_t glyph, hb_glyph_extents_t* extents, void* user_data) { FontData* font_data = reinterpret_cast(data); GetGlyphWidthAndExtents(font_data->font_, glyph, 0, extents); return true; } class FontFuncs { public: FontFuncs() : font_funcs_(hb_font_funcs_create()) { hb_font_funcs_set_variation_glyph_func(font_funcs_, GetGlyph, 0, 0); hb_font_funcs_set_nominal_glyph_func(font_funcs_, GetNominalGlyph, 0, 0); hb_font_funcs_set_glyph_h_advance_func( font_funcs_, GetGlyphHorizontalAdvance, 0, 0); hb_font_funcs_set_glyph_h_kerning_func( font_funcs_, GetGlyphHorizontalKerning, 0, 0); hb_font_funcs_set_glyph_h_origin_func( font_funcs_, GetGlyphHorizontalOrigin, 0, 0); hb_font_funcs_set_glyph_v_kerning_func( font_funcs_, GetGlyphVerticalKerning, 0, 0); hb_font_funcs_set_glyph_extents_func( font_funcs_, GetGlyphExtents, 0, 0); hb_font_funcs_make_immutable(font_funcs_); } ~FontFuncs() { hb_font_funcs_destroy(font_funcs_); } hb_font_funcs_t* get() { return font_funcs_; } private: hb_font_funcs_t* font_funcs_; DISALLOW_COPY_AND_ASSIGN(FontFuncs); }; base::LazyInstance::Leaky g_font_funcs = LAZY_INSTANCE_INITIALIZER; // Returns the raw data of the font table |tag|. hb_blob_t* GetFontTable(hb_face_t* face, hb_tag_t tag, void* user_data) { SkTypeface* typeface = reinterpret_cast(user_data); const size_t table_size = typeface->getTableSize(tag); if (!table_size) return 0; std::unique_ptr buffer(new char[table_size]); if (!buffer) return 0; size_t actual_size = typeface->getTableData(tag, 0, table_size, buffer.get()); if (table_size != actual_size) return 0; char* buffer_raw = buffer.release(); return hb_blob_create(buffer_raw, static_cast(table_size), HB_MEMORY_MODE_WRITABLE, buffer_raw, DeleteArrayByType); } // For a given skia typeface, maps to its harfbuzz face and its glyphs cache. class TypefaceData { public: explicit TypefaceData(sk_sp skia_face) : sk_typeface_(skia_face) { face_ = hb_face_create_for_tables(GetFontTable, skia_face.get(), nullptr); DCHECK(face_); } TypefaceData(TypefaceData&& data) { face_ = data.face_; glyphs_ = std::move(data.glyphs_); data.face_ = nullptr; } ~TypefaceData() { hb_face_destroy(face_); } hb_face_t* face() { return face_; } GlyphCache* glyphs() { return &glyphs_; } private: TypefaceData() = delete; GlyphCache glyphs_; hb_face_t* face_ = nullptr; // The skia typeface must outlive |face_| since it's being used by harfbuzz. sk_sp sk_typeface_; DISALLOW_COPY_AND_ASSIGN(TypefaceData); }; } // namespace // Creates a HarfBuzz font from the given Skia face and text size. hb_font_t* CreateHarfBuzzFont(sk_sp skia_face, SkScalar text_size, const FontRenderParams& params, bool subpixel_rendering_suppressed) { // A cache from Skia font to harfbuzz typeface information. using TypefaceCache = base::MRUCache; constexpr int kTypefaceCacheSize = 64; static base::NoDestructor face_caches(kTypefaceCacheSize); TypefaceCache* typeface_cache = face_caches.get(); TypefaceCache::iterator typeface_data = typeface_cache->Get(skia_face->uniqueID()); if (typeface_data == typeface_cache->end()) { TypefaceData new_typeface_data(skia_face); typeface_data = typeface_cache->Put(skia_face->uniqueID(), std::move(new_typeface_data)); } DCHECK(typeface_data->second.face()); hb_font_t* harfbuzz_font = hb_font_create(typeface_data->second.face()); const int scale = SkiaScalarToHarfBuzzUnits(text_size); hb_font_set_scale(harfbuzz_font, scale, scale); FontData* hb_font_data = new FontData(typeface_data->second.glyphs()); hb_font_data->font_.setTypeface(std::move(skia_face)); hb_font_data->font_.setSize(text_size); // TODO(ckocagil): Do we need to update these params later? internal::ApplyRenderParams(params, subpixel_rendering_suppressed, &hb_font_data->font_); hb_font_set_funcs(harfbuzz_font, g_font_funcs.Get().get(), hb_font_data, DeleteByType); hb_font_make_immutable(harfbuzz_font); return harfbuzz_font; } } // namespace gfx