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.
5 #include "ui/gfx/harfbuzz_font_skia.h"
10 #include "base/lazy_instance.h"
11 #include "base/logging.h"
12 #include "third_party/skia/include/core/SkPaint.h"
13 #include "third_party/skia/include/core/SkTypeface.h"
14 #include "ui/gfx/render_text.h"
22 // Maps from code points to glyph indices in a font.
23 typedef std::map
<uint32_t, uint16_t> GlyphCache
;
25 typedef std::pair
<HarfBuzzFace
, GlyphCache
> FaceCache
;
27 // Font data provider for HarfBuzz using Skia. Copied from Blink.
28 // TODO(ckocagil): Eliminate the duplication. http://crbug.com/368375
30 FontData(GlyphCache
* glyph_cache
) : glyph_cache_(glyph_cache
) {}
33 GlyphCache
* glyph_cache_
;
36 // Deletes the object at the given pointer after casting it to the given type.
37 template<typename Type
>
38 void DeleteByType(void* data
) {
39 Type
* typed_data
= reinterpret_cast<Type
*>(data
);
43 template<typename Type
>
44 void DeleteArrayByType(void* data
) {
45 Type
* typed_data
= reinterpret_cast<Type
*>(data
);
49 // Outputs the |width| and |extents| of the glyph with index |codepoint| in
51 void GetGlyphWidthAndExtents(SkPaint
* paint
,
52 hb_codepoint_t codepoint
,
54 hb_glyph_extents_t
* extents
) {
55 DCHECK_LE(codepoint
, std::numeric_limits
<uint16_t>::max());
56 paint
->setTextEncoding(SkPaint::kGlyphID_TextEncoding
);
60 uint16_t glyph
= static_cast<uint16_t>(codepoint
);
62 paint
->getTextWidths(&glyph
, sizeof(glyph
), &sk_width
, &sk_bounds
);
64 *width
= SkScalarToFixed(sk_width
);
66 // Invert y-axis because Skia is y-grows-down but we set up HarfBuzz to be
68 extents
->x_bearing
= SkScalarToFixed(sk_bounds
.fLeft
);
69 extents
->y_bearing
= SkScalarToFixed(-sk_bounds
.fTop
);
70 extents
->width
= SkScalarToFixed(sk_bounds
.width());
71 extents
->height
= SkScalarToFixed(-sk_bounds
.height());
75 // Writes the |glyph| index for the given |unicode| code point. Returns whether
76 // the glyph exists, i.e. it is not a missing glyph.
77 hb_bool_t
GetGlyph(hb_font_t
* font
,
79 hb_codepoint_t unicode
,
80 hb_codepoint_t variation_selector
,
81 hb_codepoint_t
* glyph
,
83 FontData
* font_data
= reinterpret_cast<FontData
*>(data
);
84 GlyphCache
* cache
= font_data
->glyph_cache_
;
86 bool exists
= cache
->count(unicode
) != 0;
88 SkPaint
* paint
= &font_data
->paint_
;
89 paint
->setTextEncoding(SkPaint::kUTF32_TextEncoding
);
90 paint
->textToGlyphs(&unicode
, sizeof(hb_codepoint_t
), &(*cache
)[unicode
]);
92 *glyph
= (*cache
)[unicode
];
96 // Returns the horizontal advance value of the |glyph|.
97 hb_position_t
GetGlyphHorizontalAdvance(hb_font_t
* font
,
101 FontData
* font_data
= reinterpret_cast<FontData
*>(data
);
102 hb_position_t advance
= 0;
104 GetGlyphWidthAndExtents(&font_data
->paint_
, glyph
, &advance
, 0);
108 hb_bool_t
GetGlyphHorizontalOrigin(hb_font_t
* font
,
110 hb_codepoint_t glyph
,
114 // Just return true, like the HarfBuzz-FreeType implementation.
118 hb_position_t
GetGlyphKerning(FontData
* font_data
,
119 hb_codepoint_t first_glyph
,
120 hb_codepoint_t second_glyph
) {
121 SkTypeface
* typeface
= font_data
->paint_
.getTypeface();
122 const uint16_t glyphs
[2] = { static_cast<uint16_t>(first_glyph
),
123 static_cast<uint16_t>(second_glyph
) };
124 int32_t kerning_adjustments
[1] = { 0 };
126 if (!typeface
->getKerningPairAdjustments(glyphs
, 2, kerning_adjustments
))
129 SkScalar upm
= SkIntToScalar(typeface
->getUnitsPerEm());
130 SkScalar size
= font_data
->paint_
.getTextSize();
131 return SkScalarToFixed(
132 SkScalarMulDiv(SkIntToScalar(kerning_adjustments
[0]), size
, upm
));
135 hb_position_t
GetGlyphHorizontalKerning(hb_font_t
* font
,
137 hb_codepoint_t left_glyph
,
138 hb_codepoint_t right_glyph
,
140 FontData
* font_data
= reinterpret_cast<FontData
*>(data
);
141 if (font_data
->paint_
.isVerticalText()) {
142 // We don't support cross-stream kerning.
146 return GetGlyphKerning(font_data
, left_glyph
, right_glyph
);
149 hb_position_t
GetGlyphVerticalKerning(hb_font_t
* font
,
151 hb_codepoint_t top_glyph
,
152 hb_codepoint_t bottom_glyph
,
154 FontData
* font_data
= reinterpret_cast<FontData
*>(data
);
155 if (!font_data
->paint_
.isVerticalText()) {
156 // We don't support cross-stream kerning.
160 return GetGlyphKerning(font_data
, top_glyph
, bottom_glyph
);
163 // Writes the |extents| of |glyph|.
164 hb_bool_t
GetGlyphExtents(hb_font_t
* font
,
166 hb_codepoint_t glyph
,
167 hb_glyph_extents_t
* extents
,
169 FontData
* font_data
= reinterpret_cast<FontData
*>(data
);
171 GetGlyphWidthAndExtents(&font_data
->paint_
, glyph
, 0, extents
);
177 FontFuncs() : font_funcs_(hb_font_funcs_create()) {
178 hb_font_funcs_set_glyph_func(font_funcs_
, GetGlyph
, 0, 0);
179 hb_font_funcs_set_glyph_h_advance_func(
180 font_funcs_
, GetGlyphHorizontalAdvance
, 0, 0);
181 hb_font_funcs_set_glyph_h_kerning_func(
182 font_funcs_
, GetGlyphHorizontalKerning
, 0, 0);
183 hb_font_funcs_set_glyph_h_origin_func(
184 font_funcs_
, GetGlyphHorizontalOrigin
, 0, 0);
185 hb_font_funcs_set_glyph_v_kerning_func(
186 font_funcs_
, GetGlyphVerticalKerning
, 0, 0);
187 hb_font_funcs_set_glyph_extents_func(
188 font_funcs_
, GetGlyphExtents
, 0, 0);
189 hb_font_funcs_make_immutable(font_funcs_
);
193 hb_font_funcs_destroy(font_funcs_
);
196 hb_font_funcs_t
* get() { return font_funcs_
; }
199 hb_font_funcs_t
* font_funcs_
;
201 DISALLOW_COPY_AND_ASSIGN(FontFuncs
);
204 base::LazyInstance
<FontFuncs
>::Leaky g_font_funcs
= LAZY_INSTANCE_INITIALIZER
;
206 // Returns the raw data of the font table |tag|.
207 hb_blob_t
* GetFontTable(hb_face_t
* face
, hb_tag_t tag
, void* user_data
) {
208 SkTypeface
* typeface
= reinterpret_cast<SkTypeface
*>(user_data
);
210 const size_t table_size
= typeface
->getTableSize(tag
);
214 scoped_ptr
<char[]> buffer(new char[table_size
]);
217 size_t actual_size
= typeface
->getTableData(tag
, 0, table_size
, buffer
.get());
218 if (table_size
!= actual_size
)
221 char* buffer_raw
= buffer
.release();
222 return hb_blob_create(buffer_raw
, table_size
, HB_MEMORY_MODE_WRITABLE
,
223 buffer_raw
, DeleteArrayByType
<char>);
226 void UnrefSkTypeface(void* data
) {
227 SkTypeface
* skia_face
= reinterpret_cast<SkTypeface
*>(data
);
228 SkSafeUnref(skia_face
);
231 // Wrapper class for a HarfBuzz face created from a given Skia face.
234 HarfBuzzFace() : face_(NULL
) {}
238 hb_face_destroy(face_
);
241 void Init(SkTypeface
* skia_face
) {
242 SkSafeRef(skia_face
);
243 face_
= hb_face_create_for_tables(GetFontTable
, skia_face
, UnrefSkTypeface
);
257 // Creates a HarfBuzz font from the given Skia face and text size.
258 hb_font_t
* CreateHarfBuzzFont(SkTypeface
* skia_face
,
260 const FontRenderParams
& params
,
261 bool background_is_transparent
) {
262 // TODO(ckocagil): This shouldn't grow indefinitely. Maybe use base::MRUCache?
263 static std::map
<SkFontID
, FaceCache
> face_caches
;
265 FaceCache
* face_cache
= &face_caches
[skia_face
->uniqueID()];
266 if (face_cache
->first
.get() == NULL
)
267 face_cache
->first
.Init(skia_face
);
269 hb_font_t
* harfbuzz_font
= hb_font_create(face_cache
->first
.get());
270 const int scale
= SkScalarToFixed(text_size
);
271 hb_font_set_scale(harfbuzz_font
, scale
, scale
);
272 FontData
* hb_font_data
= new FontData(&face_cache
->second
);
273 hb_font_data
->paint_
.setTypeface(skia_face
);
274 hb_font_data
->paint_
.setTextSize(text_size
);
275 // TODO(ckocagil): Do we need to update these params later?
276 internal::ApplyRenderParams(params
, background_is_transparent
,
277 &hb_font_data
->paint_
);
278 hb_font_set_funcs(harfbuzz_font
, g_font_funcs
.Get().get(), hb_font_data
,
279 DeleteByType
<FontData
>);
280 hb_font_make_immutable(harfbuzz_font
);
281 return harfbuzz_font
;