Commit 42a3f824 authored by ckocagil's avatar ckocagil Committed by Commit bot

Extract the Skia implementation of HarfBuzz font to its own file

BUG=
R=msw

Review URL: https://codereview.chromium.org/891183003

Cr-Commit-Position: refs/heads/master@{#314975}
parent b985af1c
...@@ -246,6 +246,8 @@ component("gfx") { ...@@ -246,6 +246,8 @@ component("gfx") {
} else { } else {
# Mac doesn't use RenderTextHarfBuzz by default yet. # Mac doesn't use RenderTextHarfBuzz by default yet.
sources += [ sources += [
"harfbuzz_font_skia.cc",
"harfbuzz_font_skia.h",
"render_text.cc", "render_text.cc",
"render_text.h", "render_text.h",
"render_text_harfbuzz.cc", "render_text_harfbuzz.cc",
......
...@@ -182,6 +182,8 @@ ...@@ -182,6 +182,8 @@
'gfx_paths.h', 'gfx_paths.h',
'gpu_memory_buffer.cc', 'gpu_memory_buffer.cc',
'gpu_memory_buffer.h', 'gpu_memory_buffer.h',
'harfbuzz_font_skia.cc',
'harfbuzz_font_skia.h',
'hud_font.cc', 'hud_font.cc',
'hud_font.h', 'hud_font.h',
'image/canvas_image_source.cc', 'image/canvas_image_source.cc',
...@@ -385,6 +387,8 @@ ...@@ -385,6 +387,8 @@
}], }],
['OS=="android" or OS=="ios"', { ['OS=="android" or OS=="ios"', {
'sources!': [ 'sources!': [
'harfbuzz_font_skia.cc',
'harfbuzz_font_skia.h',
'render_text.cc', 'render_text.cc',
'render_text.h', 'render_text.h',
'render_text_harfbuzz.cc', 'render_text_harfbuzz.cc',
......
// 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
// 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.
#ifndef UI_GFX_HARFBUZZ_FONT_SKIA_H_
#define UI_GFX_HARFBUZZ_FONT_SKIA_H_
#include "third_party/harfbuzz-ng/src/hb.h"
#include "third_party/skia/include/core/SkScalar.h"
#include "ui/gfx/font_render_params.h"
class SkTypeface;
namespace gfx {
hb_font_t* CreateHarfBuzzFont(SkTypeface* skia_face,
SkScalar text_size,
const FontRenderParams& params,
bool background_is_transparent);
} // namespace gfx
#endif // UI_GFX_HARFBUZZ_FONT_SKIA_H_
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment