diff options
author | Mike Morris <mikemorris@users.noreply.github.com> | 2016-04-12 14:22:23 -0700 |
---|---|---|
committer | Mike Morris <mikemorris@users.noreply.github.com> | 2016-04-12 14:22:23 -0700 |
commit | c6a0f61853718d80c112e449b667a0c35e719951 (patch) | |
tree | 233b1dc98e6b4481b0c79e98e057e8ee177db8ac | |
parent | 432d9d81288e84c75906da9a976f3151f1ef074c (diff) | |
download | qtlocation-mapboxgl-c6a0f61853718d80c112e449b667a0c35e719951.tar.gz |
first draft of mbgl::Font harfbuzz label shaping
-rw-r--r-- | src/mbgl/renderer/symbol_bucket.cpp | 1 | ||||
-rw-r--r-- | src/mbgl/text/font.cpp | 208 | ||||
-rw-r--r-- | src/mbgl/text/font.hpp | 27 | ||||
-rw-r--r-- | src/mbgl/text/font_stack.cpp | 120 | ||||
-rw-r--r-- | src/mbgl/text/font_stack.hpp | 2 |
5 files changed, 241 insertions, 117 deletions
diff --git a/src/mbgl/renderer/symbol_bucket.cpp b/src/mbgl/renderer/symbol_bucket.cpp index d12ddb8368..e189104490 100644 --- a/src/mbgl/renderer/symbol_bucket.cpp +++ b/src/mbgl/renderer/symbol_bucket.cpp @@ -238,7 +238,6 @@ void SymbolBucket::addFeatures(uintptr_t tileUID, if (feature.label.length()) { shapedText = fontStack->getShaping( /* string */ feature.label, - /* fontstack: string */ layout.text.font, /* maxWidth: ems */ layout.placement != PlacementType::Line ? layout.text.maxWidth * 24 : 0, /* lineHeight: ems */ layout.text.lineHeight * 24, diff --git a/src/mbgl/text/font.cpp b/src/mbgl/text/font.cpp new file mode 100644 index 0000000000..3ee3b5f33a --- /dev/null +++ b/src/mbgl/text/font.cpp @@ -0,0 +1,208 @@ +#include <mbgl/text/font.hpp> + +#include <harfbuzz/hb-ft.h> + +#include <fstream> +#include <iostream> + +#include <cstdint> +#include <codecvt> + +namespace mbgl { + +Font::Font(const std::string &filename) + : filename_(filename), + font_(0), + buffer_(hb_buffer_create()) { + load(); +} + +Font::~Font() { + hb_buffer_destroy(buffer_); + hb_font_destroy(font_); +} + +void Font::load() { + if (font_) return; + + char *font_data; + unsigned int 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]; + file.seekg(0, std::ios::beg); + file.read(font_data, 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) { + delete[] static_cast<char*>(data); + }); + hb_face_t *face = hb_face_create(blob, 0 /*face_index*/); + hb_blob_destroy(blob); + font_ = hb_font_create(face); + +#if 1 + // TODO: Font size + unsigned int upem = hb_face_get_upem(face); + hb_font_set_scale(font_, upem, upem); +#endif + hb_face_destroy(face); + hb_ft_font_set_funcs(font_); +} + +void Font::shape(const std::u32string &text) +{ + if (!font_) return; + + std::wstring_convert<std::codecvt_utf8<char32_t>, char32_t> cv; + std::cout << cv.to_bytes(text) << std::endl; + + hb_buffer_reset(buffer_); + + hb_buffer_add_utf32(buffer_, reinterpret_cast<const uint32_t*>(text.c_str()), text.length(), 0, text.length()); + + hb_buffer_set_direction(buffer_, HB_DIRECTION_RTL); + hb_buffer_set_script(buffer_, HB_SCRIPT_ARABIC); + hb_buffer_set_language(buffer_, hb_language_from_string("ar", -1)); + +#if 0 + hb_buffer_set_direction(buffer, hb_direction_from_string (direction, -1)); + hb_buffer_set_script(buffer, hb_script_from_string (script, -1)); + hb_buffer_set_language(buffer, hb_language_from_string (language, -1)); +#endif + + hb_shape(font_, buffer_, 0 /*features*/, 0 /*num_features*/); + + int num_glyphs = hb_buffer_get_length(buffer_); + 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); + + for (int i = 0; i < num_glyphs; i++) { + std::cout << "glyph codepoint:" << hb_glyph[i].codepoint << + " 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"; + } + + /* + auto hb_buffer_deleter = [](hb_buffer_t * buffer) { hb_buffer_destroy(buffer);}; + const std::unique_ptr<hb_buffer_t, decltype(hb_buffer_deleter)> buffer(hb_buffer_create(),hb_buffer_deleter); + hb_buffer_pre_allocate(buffer.get(), string.length()); + + mapnik::value_unicode_string const& text = itemizer.text(); + + 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<int>(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<glyph_face_info> 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<int>(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; i<num_glyphs; ++i) + { + // if we have a valid codepoint, save rendering info. + if (glyphs[i].codepoint) + { + if (!glyphinfos[i].glyph.codepoint) + { + ++valid_glyphs; + } + glyphinfos[i] = { face, glyphs[i], positions[i] }; + } + } + if (valid_glyphs < num_glyphs && (pos < num_faces)) + { + //Try next font in fontset + continue; + } + + double max_glyph_height = 0; + for (unsigned i=0; i<num_glyphs; ++i) + { + auto& gpos = positions[i]; + auto& glyph = glyphs[i]; + face_ptr theface = face; + if (glyphinfos[i].glyph.codepoint) + { + gpos = glyphinfos[i].position; + glyph = glyphinfos[i].glyph; + theface = glyphinfos[i].face; + } + unsigned char_index = glyph.cluster; + glyph_info g(glyph.codepoint,char_index,text_item.format_); + if (theface->glyph_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. + } + } + */ +} + +} // end namespace mbgl diff --git a/src/mbgl/text/font.hpp b/src/mbgl/text/font.hpp new file mode 100644 index 0000000000..afb3248888 --- /dev/null +++ b/src/mbgl/text/font.hpp @@ -0,0 +1,27 @@ +#ifndef MBGL_TEXT_FONT +#define MBGL_TEXT_FONT + +#include <string> + +#include <harfbuzz/hb.h> + +namespace mbgl { + +class Font { +public: + Font(const std::string &filename); + ~Font(); + + void shape(const std::u32string &text); + +private: + void load(); + + std::string filename_; + hb_font_t *font_; + hb_buffer_t *buffer_; +}; + +} // end namespace mbgl + +#endif diff --git a/src/mbgl/text/font_stack.cpp b/src/mbgl/text/font_stack.cpp index 8b2604973e..01e5aa58e1 100644 --- a/src/mbgl/text/font_stack.cpp +++ b/src/mbgl/text/font_stack.cpp @@ -3,6 +3,7 @@ #include <mbgl/platform/log.hpp> #include <mbgl/util/math.hpp> +#include <mbgl/text/font.hpp> #include <harfbuzz/hb.h> #include <harfbuzz/hb-ft.h> #include <iostream> @@ -35,7 +36,7 @@ const std::map<uint32_t, SDFGlyph> &FontStack::getSDFs() const { } const Shaping FontStack::getShaping(const std::u32string &string, - const std::string &font, const float maxWidth, + const float maxWidth, const float lineHeight, const float horizontalAlign, const float verticalAlign, const float justify, const float spacing, const vec2<float> &translate) const { @@ -47,120 +48,9 @@ const Shaping FontStack::getShaping(const std::u32string &string, float x = 0; const float y = yOffset; - // TODO: pass string through harfbuzz - auto hb_buffer_deleter = [](hb_buffer_t * buffer) { hb_buffer_destroy(buffer);}; - const std::unique_ptr<hb_buffer_t, decltype(hb_buffer_deleter)> buffer(hb_buffer_create(),hb_buffer_deleter); - hb_buffer_pre_allocate(buffer.get(), string.length()); - - std::wstring_convert<std::codecvt_utf8<char32_t>, char32_t> cv; - std::cout << cv.to_bytes(string) << std::endl; - std::cout << font << std::endl; - - /* - mapnik::value_unicode_string const& text = itemizer.text(); - - 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<int>(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<glyph_face_info> 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<int>(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; i<num_glyphs; ++i) - { - // if we have a valid codepoint, save rendering info. - if (glyphs[i].codepoint) - { - if (!glyphinfos[i].glyph.codepoint) - { - ++valid_glyphs; - } - glyphinfos[i] = { face, glyphs[i], positions[i] }; - } - } - if (valid_glyphs < num_glyphs && (pos < num_faces)) - { - //Try next font in fontset - continue; - } - - double max_glyph_height = 0; - for (unsigned i=0; i<num_glyphs; ++i) - { - auto& gpos = positions[i]; - auto& glyph = glyphs[i]; - face_ptr theface = face; - if (glyphinfos[i].glyph.codepoint) - { - gpos = glyphinfos[i].position; - glyph = glyphinfos[i].glyph; - theface = glyphinfos[i].face; - } - unsigned char_index = glyph.cluster; - glyph_info g(glyph.codepoint,char_index,text_item.format_); - if (theface->glyph_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. - } - } - */ - + // Create new Harfbuzz font object + Font font("/Library/Fonts/Arial Unicode.ttf"); + font.shape(string); // Loop through all characters of this label and shape. for (uint32_t chr : string) { diff --git a/src/mbgl/text/font_stack.hpp b/src/mbgl/text/font_stack.hpp index 2ceeceb7c2..d81460a534 100644 --- a/src/mbgl/text/font_stack.hpp +++ b/src/mbgl/text/font_stack.hpp @@ -10,7 +10,7 @@ class FontStack { public: void insert(uint32_t id, const SDFGlyph &glyph); const std::map<uint32_t, SDFGlyph> &getSDFs() const; - const Shaping getShaping(const std::u32string &string, const std::string &font, float maxWidth, float lineHeight, + const Shaping getShaping(const std::u32string &string, float maxWidth, float lineHeight, float horizontalAlign, float verticalAlign, float justify, float spacing, const vec2<float> &translate) const; void lineWrap(Shaping &shaping, float lineHeight, float maxWidth, float horizontalAlign, |