Index: ui/gfx/harfbuzz_font_skia.cc |
diff --git a/ui/gfx/harfbuzz_font_skia.cc b/ui/gfx/harfbuzz_font_skia.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..2eaa0b0f0aeaca287cd45a2014eb1f792177f735 |
--- /dev/null |
+++ b/ui/gfx/harfbuzz_font_skia.cc |
@@ -0,0 +1,284 @@ |
+// 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 <limits> |
+#include <map> |
+ |
+#include "base/lazy_instance.h" |
+#include "base/logging.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 background_is_transparent) { |
+ // 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, background_is_transparent, |
+ &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 |