blob: 6ee856c11ba3554835d3674c5f1ae2eed9df49b6 [file] [log] [blame]
// 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 <stddef.h>
#include <stdint.h>
#include <limits>
#include <map>
#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<uint32_t, uint16_t>;
// 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.
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<typename Type>
void DeleteByType(void* data) {
Type* typed_data = reinterpret_cast<Type*>(data);
delete typed_data;
template<typename Type>
void DeleteArrayByType(void* data) {
Type* typed_data = reinterpret_cast<Type*>(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<uint16_t>::max());
SkScalar sk_width;
SkRect sk_bounds;
uint16_t glyph = static_cast<uint16_t>(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<FontData*>(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)));
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<FontData*>(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<uint16_t>(first_glyph),
static_cast<uint16_t>(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<FontData*>(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<FontData*>(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<FontData*>(data);
GetGlyphWidthAndExtents(font_data->font_, glyph, 0, extents);
return true;
class FontFuncs {
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);
font_funcs_, GetGlyphHorizontalAdvance, 0, 0);
font_funcs_, GetGlyphHorizontalKerning, 0, 0);
font_funcs_, GetGlyphHorizontalOrigin, 0, 0);
font_funcs_, GetGlyphVerticalKerning, 0, 0);
font_funcs_, GetGlyphExtents, 0, 0);
~FontFuncs() {
hb_font_funcs_t* get() { return font_funcs_; }
hb_font_funcs_t* font_funcs_;
base::LazyInstance<FontFuncs>::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<SkTypeface*>(user_data);
const size_t table_size = typeface->getTableSize(tag);
if (!table_size)
return 0;
std::unique_ptr<char[]> 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<uint32_t>(table_size),
// For a given skia typeface, maps to its harfbuzz face and its glyphs cache.
class TypefaceData {
explicit TypefaceData(sk_sp<SkTypeface> skia_face) : sk_typeface_(skia_face) {
face_ = hb_face_create_for_tables(GetFontTable, skia_face.get(), nullptr);
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_; }
TypefaceData() = delete;
GlyphCache glyphs_;
hb_face_t* face_ = nullptr;
// The skia typeface must outlive |face_| since it's being used by harfbuzz.
sk_sp<SkTypeface> sk_typeface_;
} // namespace
// Creates a HarfBuzz font from the given Skia face and text size.
hb_font_t* CreateHarfBuzzFont(sk_sp<SkTypeface> 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<SkFontID, TypefaceData>;
constexpr int kTypefaceCacheSize = 64;
static base::NoDestructor<TypefaceCache> face_caches(kTypefaceCacheSize);
TypefaceCache* typeface_cache = face_caches.get();
TypefaceCache::iterator typeface_data =
if (typeface_data == typeface_cache->end()) {
TypefaceData new_typeface_data(skia_face);
typeface_data = typeface_cache->Put(skia_face->uniqueID(),
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());
// TODO(ckocagil): Do we need to update these params later?
internal::ApplyRenderParams(params, subpixel_rendering_suppressed,
hb_font_set_funcs(harfbuzz_font, g_font_funcs.Get().get(), hb_font_data,
return harfbuzz_font;
} // namespace gfx