OLD | NEW |
(Empty) | |
| 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. |
| 4 |
| 5 #include "ui/gfx/harfbuzz_font_skia.h" |
| 6 |
| 7 #include <limits> |
| 8 #include <map> |
| 9 |
| 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" |
| 15 |
| 16 namespace gfx { |
| 17 |
| 18 namespace { |
| 19 |
| 20 class HarfBuzzFace; |
| 21 |
| 22 // Maps from code points to glyph indices in a font. |
| 23 typedef std::map<uint32_t, uint16_t> GlyphCache; |
| 24 |
| 25 typedef std::pair<HarfBuzzFace, GlyphCache> FaceCache; |
| 26 |
| 27 // Font data provider for HarfBuzz using Skia. Copied from Blink. |
| 28 // TODO(ckocagil): Eliminate the duplication. http://crbug.com/368375 |
| 29 struct FontData { |
| 30 FontData(GlyphCache* glyph_cache) : glyph_cache_(glyph_cache) {} |
| 31 |
| 32 SkPaint paint_; |
| 33 GlyphCache* glyph_cache_; |
| 34 }; |
| 35 |
| 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); |
| 40 delete typed_data; |
| 41 } |
| 42 |
| 43 template<typename Type> |
| 44 void DeleteArrayByType(void* data) { |
| 45 Type* typed_data = reinterpret_cast<Type*>(data); |
| 46 delete[] typed_data; |
| 47 } |
| 48 |
| 49 // Outputs the |width| and |extents| of the glyph with index |codepoint| in |
| 50 // |paint|'s font. |
| 51 void GetGlyphWidthAndExtents(SkPaint* paint, |
| 52 hb_codepoint_t codepoint, |
| 53 hb_position_t* width, |
| 54 hb_glyph_extents_t* extents) { |
| 55 DCHECK_LE(codepoint, std::numeric_limits<uint16_t>::max()); |
| 56 paint->setTextEncoding(SkPaint::kGlyphID_TextEncoding); |
| 57 |
| 58 SkScalar sk_width; |
| 59 SkRect sk_bounds; |
| 60 uint16_t glyph = static_cast<uint16_t>(codepoint); |
| 61 |
| 62 paint->getTextWidths(&glyph, sizeof(glyph), &sk_width, &sk_bounds); |
| 63 if (width) |
| 64 *width = SkScalarToFixed(sk_width); |
| 65 if (extents) { |
| 66 // Invert y-axis because Skia is y-grows-down but we set up HarfBuzz to be |
| 67 // y-grows-up. |
| 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()); |
| 72 } |
| 73 } |
| 74 |
| 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, |
| 78 void* data, |
| 79 hb_codepoint_t unicode, |
| 80 hb_codepoint_t variation_selector, |
| 81 hb_codepoint_t* glyph, |
| 82 void* user_data) { |
| 83 FontData* font_data = reinterpret_cast<FontData*>(data); |
| 84 GlyphCache* cache = font_data->glyph_cache_; |
| 85 |
| 86 bool exists = cache->count(unicode) != 0; |
| 87 if (!exists) { |
| 88 SkPaint* paint = &font_data->paint_; |
| 89 paint->setTextEncoding(SkPaint::kUTF32_TextEncoding); |
| 90 paint->textToGlyphs(&unicode, sizeof(hb_codepoint_t), &(*cache)[unicode]); |
| 91 } |
| 92 *glyph = (*cache)[unicode]; |
| 93 return !!*glyph; |
| 94 } |
| 95 |
| 96 // Returns the horizontal advance value of the |glyph|. |
| 97 hb_position_t GetGlyphHorizontalAdvance(hb_font_t* font, |
| 98 void* data, |
| 99 hb_codepoint_t glyph, |
| 100 void* user_data) { |
| 101 FontData* font_data = reinterpret_cast<FontData*>(data); |
| 102 hb_position_t advance = 0; |
| 103 |
| 104 GetGlyphWidthAndExtents(&font_data->paint_, glyph, &advance, 0); |
| 105 return advance; |
| 106 } |
| 107 |
| 108 hb_bool_t GetGlyphHorizontalOrigin(hb_font_t* font, |
| 109 void* data, |
| 110 hb_codepoint_t glyph, |
| 111 hb_position_t* x, |
| 112 hb_position_t* y, |
| 113 void* user_data) { |
| 114 // Just return true, like the HarfBuzz-FreeType implementation. |
| 115 return true; |
| 116 } |
| 117 |
| 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 }; |
| 125 |
| 126 if (!typeface->getKerningPairAdjustments(glyphs, 2, kerning_adjustments)) |
| 127 return 0; |
| 128 |
| 129 SkScalar upm = SkIntToScalar(typeface->getUnitsPerEm()); |
| 130 SkScalar size = font_data->paint_.getTextSize(); |
| 131 return SkScalarToFixed( |
| 132 SkScalarMulDiv(SkIntToScalar(kerning_adjustments[0]), size, upm)); |
| 133 } |
| 134 |
| 135 hb_position_t GetGlyphHorizontalKerning(hb_font_t* font, |
| 136 void* data, |
| 137 hb_codepoint_t left_glyph, |
| 138 hb_codepoint_t right_glyph, |
| 139 void* user_data) { |
| 140 FontData* font_data = reinterpret_cast<FontData*>(data); |
| 141 if (font_data->paint_.isVerticalText()) { |
| 142 // We don't support cross-stream kerning. |
| 143 return 0; |
| 144 } |
| 145 |
| 146 return GetGlyphKerning(font_data, left_glyph, right_glyph); |
| 147 } |
| 148 |
| 149 hb_position_t GetGlyphVerticalKerning(hb_font_t* font, |
| 150 void* data, |
| 151 hb_codepoint_t top_glyph, |
| 152 hb_codepoint_t bottom_glyph, |
| 153 void* user_data) { |
| 154 FontData* font_data = reinterpret_cast<FontData*>(data); |
| 155 if (!font_data->paint_.isVerticalText()) { |
| 156 // We don't support cross-stream kerning. |
| 157 return 0; |
| 158 } |
| 159 |
| 160 return GetGlyphKerning(font_data, top_glyph, bottom_glyph); |
| 161 } |
| 162 |
| 163 // Writes the |extents| of |glyph|. |
| 164 hb_bool_t GetGlyphExtents(hb_font_t* font, |
| 165 void* data, |
| 166 hb_codepoint_t glyph, |
| 167 hb_glyph_extents_t* extents, |
| 168 void* user_data) { |
| 169 FontData* font_data = reinterpret_cast<FontData*>(data); |
| 170 |
| 171 GetGlyphWidthAndExtents(&font_data->paint_, glyph, 0, extents); |
| 172 return true; |
| 173 } |
| 174 |
| 175 class FontFuncs { |
| 176 public: |
| 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_); |
| 190 } |
| 191 |
| 192 ~FontFuncs() { |
| 193 hb_font_funcs_destroy(font_funcs_); |
| 194 } |
| 195 |
| 196 hb_font_funcs_t* get() { return font_funcs_; } |
| 197 |
| 198 private: |
| 199 hb_font_funcs_t* font_funcs_; |
| 200 |
| 201 DISALLOW_COPY_AND_ASSIGN(FontFuncs); |
| 202 }; |
| 203 |
| 204 base::LazyInstance<FontFuncs>::Leaky g_font_funcs = LAZY_INSTANCE_INITIALIZER; |
| 205 |
| 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); |
| 209 |
| 210 const size_t table_size = typeface->getTableSize(tag); |
| 211 if (!table_size) |
| 212 return 0; |
| 213 |
| 214 scoped_ptr<char[]> buffer(new char[table_size]); |
| 215 if (!buffer) |
| 216 return 0; |
| 217 size_t actual_size = typeface->getTableData(tag, 0, table_size, buffer.get()); |
| 218 if (table_size != actual_size) |
| 219 return 0; |
| 220 |
| 221 char* buffer_raw = buffer.release(); |
| 222 return hb_blob_create(buffer_raw, table_size, HB_MEMORY_MODE_WRITABLE, |
| 223 buffer_raw, DeleteArrayByType<char>); |
| 224 } |
| 225 |
| 226 void UnrefSkTypeface(void* data) { |
| 227 SkTypeface* skia_face = reinterpret_cast<SkTypeface*>(data); |
| 228 SkSafeUnref(skia_face); |
| 229 } |
| 230 |
| 231 // Wrapper class for a HarfBuzz face created from a given Skia face. |
| 232 class HarfBuzzFace { |
| 233 public: |
| 234 HarfBuzzFace() : face_(NULL) {} |
| 235 |
| 236 ~HarfBuzzFace() { |
| 237 if (face_) |
| 238 hb_face_destroy(face_); |
| 239 } |
| 240 |
| 241 void Init(SkTypeface* skia_face) { |
| 242 SkSafeRef(skia_face); |
| 243 face_ = hb_face_create_for_tables(GetFontTable, skia_face, UnrefSkTypeface); |
| 244 DCHECK(face_); |
| 245 } |
| 246 |
| 247 hb_face_t* get() { |
| 248 return face_; |
| 249 } |
| 250 |
| 251 private: |
| 252 hb_face_t* face_; |
| 253 }; |
| 254 |
| 255 } // namespace |
| 256 |
| 257 // Creates a HarfBuzz font from the given Skia face and text size. |
| 258 hb_font_t* CreateHarfBuzzFont(SkTypeface* skia_face, |
| 259 SkScalar text_size, |
| 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; |
| 264 |
| 265 FaceCache* face_cache = &face_caches[skia_face->uniqueID()]; |
| 266 if (face_cache->first.get() == NULL) |
| 267 face_cache->first.Init(skia_face); |
| 268 |
| 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; |
| 282 } |
| 283 |
| 284 } // namespace gfx |
OLD | NEW |