From e39482d5570719654e7206d74a2c7bee1aa96dae Mon Sep 17 00:00:00 2001 From: Mike Morris Date: Wed, 13 Apr 2016 18:42:49 -0700 Subject: wire in some freetype nonsense --- src/mbgl/geometry/glyph_atlas.cpp | 2 +- src/mbgl/text/font.cpp | 266 ++++++++++++++++++-------------------- src/mbgl/text/font.hpp | 5 +- src/mbgl/text/font_stack.cpp | 26 +--- 4 files changed, 135 insertions(+), 164 deletions(-) diff --git a/src/mbgl/geometry/glyph_atlas.cpp b/src/mbgl/geometry/glyph_atlas.cpp index 24c1b8c8db..39b286a6b7 100644 --- a/src/mbgl/geometry/glyph_atlas.cpp +++ b/src/mbgl/geometry/glyph_atlas.cpp @@ -53,7 +53,7 @@ Rect GlyphAtlas::addGlyph(uintptr_t tileUID, const std::string& stackName, const SDFGlyph& glyph) { - // Use constant value for now. + // TODO: Use constant value for now. const uint8_t buffer = 3; std::map& face = index[stackName]; diff --git a/src/mbgl/text/font.cpp b/src/mbgl/text/font.cpp index 34cfda4501..2943d59b5b 100644 --- a/src/mbgl/text/font.cpp +++ b/src/mbgl/text/font.cpp @@ -1,14 +1,41 @@ #include -#include - #include #include - #include +// freetype2 +extern "C" { +#include FT_GLYPH_H +// #include FT_OUTLINE_H +} + namespace mbgl { +struct ft_library_guard { + ft_library_guard(FT_Library* lib) : + library_(lib) {} + + ~ft_library_guard() + { + if (library_) FT_Done_FreeType(*library_); + } + + FT_Library* library_; +}; + +struct ft_glyph_guard { + ft_glyph_guard(FT_Glyph* glyph) : + glyph_(glyph) {} + + ~ft_glyph_guard() + { + if (glyph_) FT_Done_Glyph(*glyph_); + } + + FT_Glyph* glyph_; +}; + Font::Font(const std::string &filename) : filename_(filename), font_(0), @@ -17,6 +44,12 @@ Font::Font(const std::string &filename) } Font::~Font() { + /* + for (FT_Face ft_face : ft_faces) { + FT_Done_Face(ft_face); + } + */ + hb_buffer_destroy(buffer_); hb_font_destroy(font_); } @@ -25,59 +58,88 @@ void Font::load() { if (font_) return; char *font_data; - unsigned int size; + size_t font_size; std::ifstream file(filename_.c_str(), std::ios::in|std::ios::binary|std::ios::ate); if (file.is_open()) { - size = file.tellg(); - font_data = new char[size]; + font_size = file.tellg(); + font_data = new char[font_size]; file.seekg(0, std::ios::beg); - file.read(font_data, size); + file.read(font_data, font_size); file.close(); } else { std::cerr << "Could not open font!\n"; return; } - hb_blob_t *blob = hb_blob_create(font_data, size, HB_MEMORY_MODE_WRITABLE, font_data, [](void* data) { +/* ------------------------------------------------------------------ */ + + FT_Library library = nullptr; + ft_library_guard library_guard(&library); + FT_Error error = FT_Init_FreeType(&library); + if (error) { + throw std::runtime_error("Could not open FreeType library"); + } + + FT_Face ft_face = 0; + size_t num_faces = 0; + for (size_t i = 0; ft_face == 0 || i < num_faces; ++i) { + FT_Error face_error = FT_New_Memory_Face(library, reinterpret_cast(font_data), static_cast(font_size), i, &ft_face); + if (face_error) { + throw std::runtime_error("Could not open font file"); + } + + if (num_faces == 0) { + num_faces = ft_face->num_faces; + ft_faces.reserve(num_faces); + } + + ft_faces.push_back(ft_face); + } + +/* ------------------------------------------------------------------ */ + + hb_blob_t *blob = hb_blob_create(font_data, font_size, HB_MEMORY_MODE_WRITABLE, font_data, [](void* data) { delete[] static_cast(data); }); hb_face_t *face = hb_face_create(blob, 0 /*face_index*/); hb_blob_destroy(blob); font_ = hb_font_create(face); - // TODO: Font size + // TODO: Font size? upem_ = hb_face_get_upem(face); - hb_font_set_scale(font_, upem_, upem_); + + // TOOD: currently hardcoded in node-fontnik + size = (FT_F26Dot6)(24 * (1<<6)) / (double)upem_; + hb_font_set_scale(font_, size, size); hb_face_destroy(face); hb_ft_font_set_funcs(font_); } -Shaping Font::shape(const std::u32string &text, const float spacing, const vec2 &translate) { +Shaping Font::shape(const std::u32string &text, const float spacing, const vec2 &translate, const std::map &sdfs) { Shaping shaping(translate.x * 24, translate.y * 24, text); // TODO: the y offset *should* be part of the font metadata - // const int32_t yOffset = -17; + const int32_t yOffset = -17; - if (!font_) return shaping; + float x = 0; + const float y = yOffset; - /* - std::wstring_convert, char32_t> cv; - std::cout << cv.to_bytes(text) << std::endl; - */ + if (!font_) return shaping; + // TODO: itemize string here hb_buffer_reset(buffer_); hb_buffer_add_utf32(buffer_, reinterpret_cast(text.c_str()), text.length(), 0, text.length()); - hb_buffer_set_direction(buffer_, HB_DIRECTION_RTL); + hb_buffer_set_direction(buffer_, HB_DIRECTION_LTR); // hb_buffer_set_direction(buffer, hb_direction_from_string (direction, -1)); - hb_buffer_set_script(buffer_, HB_SCRIPT_ARABIC); + hb_buffer_set_script(buffer_, HB_SCRIPT_LATIN); // hb_buffer_set_script(buffer, hb_script_from_string (script, -1)); - hb_buffer_set_language(buffer_, hb_language_from_string("ar", -1)); + hb_buffer_set_language(buffer_, hb_language_from_string("en", -1)); // hb_buffer_set_language(buffer, hb_language_from_string (language, -1)); hb_shape(font_, buffer_, 0 /*features*/, 0 /*num_features*/); @@ -86,135 +148,63 @@ Shaping Font::shape(const std::u32string &text, const float spacing, const vec2< hb_glyph_info_t *hb_glyph = hb_buffer_get_glyph_infos(buffer_, NULL); hb_glyph_position_t *hb_position = hb_buffer_get_glyph_positions(buffer_, NULL); + std::wstring_convert, char32_t> cv; + std::cout << cv.to_bytes(text) << + " num_glyphs: " << num_glyphs << + " upem: " << upem_ << + " size: " << size << + " spacing: " << spacing << std::endl; + // Loop through all characters of this label and shape. + for (uint32_t chr : text) { + auto it = sdfs.find(chr); + if (it != sdfs.end()) { + std::cout << "chr: " << chr << + " x_advance: "<< it->second.metrics.advance + spacing << std::endl; + + shaping.positionedGlyphs.emplace_back(chr, x, y); + x += it->second.metrics.advance + spacing; + } + } + for (int i = 0; i < num_glyphs; i++) { - uint32_t codepoint = hb_glyph[i].codepoint; - shaping.positionedGlyphs.emplace_back(codepoint, - hb_position[i].x_advance + spacing, - hb_position[i].y_advance); + uint32_t glyph = 0; + hb_font_get_glyph(font_, hb_glyph[i].codepoint, 0, &glyph); - /* - std::cout << "glyph codepoint:" << hb_glyph[i].codepoint << + char* name = new char[128]; + hb_font_glyph_to_string(font_, hb_glyph[i].codepoint, name, 128); + + std::cout << "glyph_index: " << hb_glyph[i].codepoint << + " name: " << name << + " x_advance: "<< hb_position[i].x_advance << " cluster: " << hb_glyph[i].cluster << " mask: " << hb_glyph[i].mask << - " x_advance: "<< hb_position[i].x_advance << "\n"; - // " y_advance: "<< hb_position[i].y_advance << - // " x_offset: "<< hb_position[i].x_offset << - // " y_offset: "<< hb_position[i].y_offset << "\n"; - // std::cout << "glyph:" << hb_glyph->codepoint << "\n"; - */ - } + " x_advance: "<< hb_position[i].x_advance << + " y_advance: "<< hb_position[i].y_advance << + " x_offset: "<< hb_position[i].x_offset << + " y_offset: "<< hb_position[i].y_offset << std::endl; - return shaping; + FT_ULong char_code; + FT_UInt glyph_index; + char_code = FT_Get_First_Char(ft_faces[0], &glyph_index); + std::cout << "char_code: " << char_code << std::endl; - /* - auto hb_buffer_deleter = [](hb_buffer_t * buffer) { hb_buffer_destroy(buffer);}; - const std::unique_ptr buffer(hb_buffer_create(),hb_buffer_deleter); - hb_buffer_pre_allocate(buffer.get(), string.length()); + /* + FT_UInt glyph_index = reinterpret_cast(hb_glyph[i].codepoint); - mapnik::value_unicode_string const& text = itemizer.text(); + if (FT_Load_Glyph(ft_faces[0], glyph_index, FT_LOAD_NO_HINTING)) { + throw std::runtime_error("Failed to load glyph"); + } - for (auto const& text_item : list) - { - face_set_ptr face_set = font_manager.get_face_set(text_item.format_->face_name, text_item.format_->fontset); - double size = text_item.format_->text_size * scale_factor; - face_set->set_unscaled_character_sizes(); - std::size_t num_faces = face_set->size(); - std::size_t pos = 0; - font_feature_settings const& ff_settings = text_item.format_->ff_settings; - int ff_count = safe_cast(ff_settings.count()); - - // rendering information for a single glyph - struct glyph_face_info - { - face_ptr face; - hb_glyph_info_t glyph; - hb_glyph_position_t position; - }; - // this table is filled with information for rendering each glyph, so that - // several font faces can be used in a single text_item - std::vector glyphinfos; - unsigned valid_glyphs = 0; - - for (auto const& face : *face_set) - { - ++pos; - hb_buffer_clear_contents(buffer.get()); - hb_buffer_add_utf16(buffer.get(), uchar_to_utf16(text.getBuffer()), text.length(), text_item.start, static_cast(text_item.end - text_item.start)); - hb_buffer_set_direction(buffer.get(), (text_item.dir == UBIDI_RTL)?HB_DIRECTION_RTL:HB_DIRECTION_LTR); - hb_buffer_set_script(buffer.get(), _icu_script_to_script(text_item.script)); - hb_font_t *font(hb_ft_font_create(face->get_face(), nullptr)); - // https://github.com/mapnik/test-data-visual/pull/25 - #if HB_VERSION_MAJOR > 0 - #if HB_VERSION_ATLEAST(1, 0 , 5) - hb_ft_font_set_load_flags(font,FT_LOAD_DEFAULT | FT_LOAD_NO_HINTING); - #endif - #endif - hb_shape(font, buffer.get(), ff_settings.get_features(), ff_count); - hb_font_destroy(font); - - unsigned num_glyphs = hb_buffer_get_length(buffer.get()); - - // if the number of rendered glyphs has increased, we need to resize the table - if (num_glyphs > glyphinfos.size()) - { - glyphinfos.resize(num_glyphs); - } - - hb_glyph_info_t *glyphs = hb_buffer_get_glyph_infos(buffer.get(), nullptr); - hb_glyph_position_t *positions = hb_buffer_get_glyph_positions(buffer.get(), nullptr); - - // Check if all glyphs are valid. - for (unsigned i=0; iglyph_dimensions(g)) - { - g.face = theface; - g.scale_multiplier = size / theface->get_face()->units_per_EM; - //Overwrite default advance with better value provided by HarfBuzz - g.unscaled_advance = gpos.x_advance; - g.offset.set(gpos.x_offset * g.scale_multiplier, gpos.y_offset * g.scale_multiplier); - double tmp_height = g.height(); - if (tmp_height > max_glyph_height) max_glyph_height = tmp_height; - width_map[char_index] += g.advance(); - line.add_glyph(std::move(g), scale_factor); - } - } - line.update_max_char_height(max_glyph_height); - break; //When we reach this point the current font had all glyphs. + FT_Glyph ft_glyph = nullptr; + ft_glyph_guard glyph_guard(&ft_glyph); + if (FT_Get_Glyph(ft_faces[0]->glyph, &ft_glyph)) { + throw std::runtime_error("Failed to get glyph"); } + */ } - */ + + return shaping; } } // end namespace mbgl diff --git a/src/mbgl/text/font.hpp b/src/mbgl/text/font.hpp index 9d34b18df1..65e471ed18 100644 --- a/src/mbgl/text/font.hpp +++ b/src/mbgl/text/font.hpp @@ -7,6 +7,7 @@ #include #include +#include namespace mbgl { @@ -15,15 +16,17 @@ public: Font(const std::string &filename); ~Font(); - Shaping shape(const std::u32string &text, const float spacing, const vec2 &translate); + Shaping shape(const std::u32string &text, const float spacing, const vec2 &translate, const std::map &sdfs); private: void load(); std::string filename_; + std::vector ft_faces; hb_font_t *font_; hb_buffer_t *buffer_; unsigned int upem_; + double size = 0.0; }; } // end namespace mbgl diff --git a/src/mbgl/text/font_stack.cpp b/src/mbgl/text/font_stack.cpp index 4c27a3dc5f..05274be315 100644 --- a/src/mbgl/text/font_stack.cpp +++ b/src/mbgl/text/font_stack.cpp @@ -35,31 +35,9 @@ const Shaping FontStack::getShaping(const std::u32string &string, const float ma const float lineHeight, const float horizontalAlign, const float verticalAlign, const float justify, const float spacing, const vec2 &translate) const { - /* - Shaping shaping(translate.x * 24, translate.y * 24, string); - - // TODO: the y offset *should* be part of the font metadata - const int32_t yOffset = -17; - - float x = 0; - const float y = yOffset; - */ - // Create new Harfbuzz font object - Font font("/Library/Fonts/Arial Unicode.ttf"); - Shaping shaping = font.shape(string, spacing, translate); - - /* - // Loop through all characters of this label and shape. - for (uint32_t chr : string) { - auto it = sdfs.find(chr); - if (it != sdfs.end()) { - // TODO: change this instead of assuming they stay in order - shaping.positionedGlyphs.emplace_back(chr, x, y); - x += it->second.metrics.advance + spacing; - } - } - */ + Font font("/Users/mikemorris/Desktop/Open_Sans/OpenSans-Regular.ttf"); + Shaping shaping = font.shape(string, spacing, translate, sdfs); if (shaping.positionedGlyphs.empty()) return shaping; -- cgit v1.2.1