|  | // 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/lazy_instance.h" | 
|  | #include "base/logging.h" | 
|  | #include "base/macros.h" | 
|  | #include "third_party/skia/include/core/SkPaint.h" | 
|  | #include "third_party/skia/include/core/SkTypeface.h" | 
|  | #include "ui/gfx/render_text.h" | 
|  |  | 
|  | namespace gfx { | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | class HarfBuzzFace; | 
|  |  | 
|  | // Maps from code points to glyph indices in a font. | 
|  | typedef std::map<uint32_t, uint16_t> GlyphCache; | 
|  |  | 
|  | typedef std::pair<HarfBuzzFace, GlyphCache> FaceCache; | 
|  |  | 
|  | // Font data provider for HarfBuzz using Skia. Copied from Blink. | 
|  | // TODO(ckocagil): Eliminate the duplication. http://crbug.com/368375 | 
|  | struct FontData { | 
|  | FontData(GlyphCache* glyph_cache) : glyph_cache_(glyph_cache) {} | 
|  |  | 
|  | SkPaint paint_; | 
|  | 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(SkPaint* paint, | 
|  | hb_codepoint_t codepoint, | 
|  | hb_position_t* width, | 
|  | hb_glyph_extents_t* extents) { | 
|  | DCHECK_LE(codepoint, std::numeric_limits<uint16_t>::max()); | 
|  | paint->setTextEncoding(SkPaint::kGlyphID_TextEncoding); | 
|  |  | 
|  | SkScalar sk_width; | 
|  | SkRect sk_bounds; | 
|  | uint16_t glyph = static_cast<uint16_t>(codepoint); | 
|  |  | 
|  | paint->getTextWidths(&glyph, sizeof(glyph), &sk_width, &sk_bounds); | 
|  | if (width) | 
|  | *width = SkScalarToFixed(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 = SkScalarToFixed(sk_bounds.fLeft); | 
|  | extents->y_bearing = SkScalarToFixed(-sk_bounds.fTop); | 
|  | extents->width = SkScalarToFixed(sk_bounds.width()); | 
|  | extents->height = SkScalarToFixed(-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_; | 
|  |  | 
|  | bool exists = cache->count(unicode) != 0; | 
|  | if (!exists) { | 
|  | SkPaint* paint = &font_data->paint_; | 
|  | paint->setTextEncoding(SkPaint::kUTF32_TextEncoding); | 
|  | paint->textToGlyphs(&unicode, sizeof(hb_codepoint_t), &(*cache)[unicode]); | 
|  | } | 
|  | *glyph = (*cache)[unicode]; | 
|  | return !!*glyph; | 
|  | } | 
|  |  | 
|  | // 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->paint_, 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->paint_.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->paint_.getTextSize(); | 
|  | return SkScalarToFixed( | 
|  | SkScalarMulDiv(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); | 
|  | if (font_data->paint_.isVerticalText()) { | 
|  | // We don't support cross-stream kerning. | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | 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); | 
|  | if (!font_data->paint_.isVerticalText()) { | 
|  | // We don't support cross-stream kerning. | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | 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->paint_, glyph, 0, extents); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | class FontFuncs { | 
|  | public: | 
|  | FontFuncs() : font_funcs_(hb_font_funcs_create()) { | 
|  | hb_font_funcs_set_glyph_func(font_funcs_, GetGlyph, 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<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; | 
|  |  | 
|  | scoped_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, table_size, HB_MEMORY_MODE_WRITABLE, | 
|  | buffer_raw, DeleteArrayByType<char>); | 
|  | } | 
|  |  | 
|  | void UnrefSkTypeface(void* data) { | 
|  | SkTypeface* skia_face = reinterpret_cast<SkTypeface*>(data); | 
|  | SkSafeUnref(skia_face); | 
|  | } | 
|  |  | 
|  | // Wrapper class for a HarfBuzz face created from a given Skia face. | 
|  | class HarfBuzzFace { | 
|  | public: | 
|  | HarfBuzzFace() : face_(NULL) {} | 
|  |  | 
|  | ~HarfBuzzFace() { | 
|  | if (face_) | 
|  | hb_face_destroy(face_); | 
|  | } | 
|  |  | 
|  | void Init(SkTypeface* skia_face) { | 
|  | SkSafeRef(skia_face); | 
|  | face_ = hb_face_create_for_tables(GetFontTable, skia_face, UnrefSkTypeface); | 
|  | DCHECK(face_); | 
|  | } | 
|  |  | 
|  | hb_face_t* get() { | 
|  | return face_; | 
|  | } | 
|  |  | 
|  | private: | 
|  | hb_face_t* face_; | 
|  | }; | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | // Creates a HarfBuzz font from the given Skia face and text size. | 
|  | hb_font_t* CreateHarfBuzzFont(SkTypeface* skia_face, | 
|  | SkScalar text_size, | 
|  | const FontRenderParams& params, | 
|  | bool subpixel_rendering_suppressed) { | 
|  | // TODO(ckocagil): This shouldn't grow indefinitely. Maybe use base::MRUCache? | 
|  | static std::map<SkFontID, FaceCache> face_caches; | 
|  |  | 
|  | FaceCache* face_cache = &face_caches[skia_face->uniqueID()]; | 
|  | if (face_cache->first.get() == NULL) | 
|  | face_cache->first.Init(skia_face); | 
|  |  | 
|  | hb_font_t* harfbuzz_font = hb_font_create(face_cache->first.get()); | 
|  | const int scale = SkScalarToFixed(text_size); | 
|  | hb_font_set_scale(harfbuzz_font, scale, scale); | 
|  | FontData* hb_font_data = new FontData(&face_cache->second); | 
|  | hb_font_data->paint_.setTypeface(skia_face); | 
|  | hb_font_data->paint_.setTextSize(text_size); | 
|  | // TODO(ckocagil): Do we need to update these params later? | 
|  | internal::ApplyRenderParams(params, subpixel_rendering_suppressed, | 
|  | &hb_font_data->paint_); | 
|  | hb_font_set_funcs(harfbuzz_font, g_font_funcs.Get().get(), hb_font_data, | 
|  | DeleteByType<FontData>); | 
|  | hb_font_make_immutable(harfbuzz_font); | 
|  | return harfbuzz_font; | 
|  | } | 
|  |  | 
|  | }  // namespace gfx |