From 22b83102793f9f8a9faacebbed2aaaaf92bb2063 Mon Sep 17 00:00:00 2001 From: Mike Morris Date: Thu, 8 May 2014 17:02:01 -0400 Subject: [PATCH] move glyphrange into AsyncRange function --- src/tile.cpp | 178 ++++++++++++++++++++++++++++++++++++++++++++++++++- src/tile.hpp | 1 + 2 files changed, 176 insertions(+), 3 deletions(-) diff --git a/src/tile.cpp b/src/tile.cpp index 2782bc4..71620c2 100644 --- a/src/tile.cpp +++ b/src/tile.cpp @@ -6,7 +6,7 @@ // node #include -// stl +#include #include #include #include @@ -124,6 +124,180 @@ v8::Handle Tile::Shape(const v8::Arguments& args) { void Tile::AsyncShape(uv_work_t* req) { ShapeBaton* baton = static_cast(req->data); + // Maps char index (UTF-16) to width. If multiple glyphs map to the + // same char the sum of all widths is used. + // Note: this probably isn't the best solution. it would be better + // to have an object for each cluster, but it needs to be + // implemented with no overhead. + std::map width_map_; + fontserver::freetype_engine font_engine_; + fontserver::face_manager_freetype font_manager(font_engine_); + fontserver::text_itemizer itemizer; + + fontserver::font_set fset(baton->fontstack); + fset.add_fontstack(baton->fontstack, ','); + + fontserver::face_set_ptr face_set = font_manager.get_face_set(fset); + if (!face_set->size()) return; + + std::map face_map; + std::vector tile_faces; + + llmr::vector::tile& tile = baton->tile->tile; + + // for every label + for (int i = 0; i < tile.layers_size(); i++) { + const llmr::vector::layer& layer = tile.layers(i); + + typedef std::set Strings; + Strings strings; + + // Compile a set of all strings we need to shape. + for (int j = 0; j < layer.features_size(); j++) { + const llmr::vector::feature& feature = layer.features(j); + + for (int k = 1; k < feature.tags_size(); k += 2) { + const std::string& key = layer.keys(feature.tags(k - 1)); + if (key == "name") { + // TODO: handle multiple fonts stacks + strings.insert(feature.tags(k)); + } + // TODO: extract all keys we need to shape + } + } + + llmr::vector::layer* mutable_layer = tile.mutable_layers(i); + + fontserver::text_format format(baton->fontstack, 24); + fontserver::text_format_ptr format_ptr = + std::make_shared(format); + + // Process strings per layer. + for (auto const& key : strings) { + const llmr::vector::value& value = layer.values(key); + std::string text; + if (value.has_string_value()) { + text = value.string_value(); + } + + if (!text.empty()) { + // Clear cluster widths. + width_map_.clear(); + + UnicodeString const& str = text.data(); + + fontserver::text_line line(0, str.length() - 1); + + itemizer.add_text(str, format_ptr); + + const double scale_factor = 1.0; + + // Shape the text. + fontserver::harfbuzz_shaper shaper; + shaper.shape_text(line, + itemizer, + width_map_, + face_set, + // font_manager, + scale_factor); + + llmr::vector::label *label = mutable_layer->add_labels(); + label->set_text(key); + + // TODO: support multiple font stacks + label->set_stack(0); + + // Add all glyphs for this labels and add new font + // faces as they appear. + for (auto const& glyph : line) { + if (!glyph.face) { + std::cout << text << ' ' << + line.size() << " glyphs\n" << + " codepoint: " << glyph.glyph_index << + " char_index: " << glyph.char_index << + " face_ptr: " << glyph.face << + '\n'; + continue; + } + + // Try to find whether this font has already been + // used in this tile. + std::map::iterator face_map_itr = face_map.find(glyph.face); + if (face_map_itr == face_map.end()) { + fontserver::tile_face *face = + new fontserver::tile_face(glyph.face); + std::pair keyed(glyph.face, face); + face_map_itr = face_map.insert(keyed).first; + + // Add to shared face cache if not found. + fontserver::font_face_set::iterator face_itr = std::find(face_set->begin(), face_set->end(), glyph.face); + if (face_itr == face_set->end()) { + face_set->add(glyph.face); + } + } + + fontserver::tile_face *face = face_map_itr->second; + + // Find out whether this font has been used in + // this tile before and get its position. + std::vector::iterator tile_itr = std::find(tile_faces.begin(), tile_faces.end(), face); + if (tile_itr == tile_faces.end()) { + tile_faces.push_back(face); + tile_itr = tile_faces.end() - 1; + } + + int tile_face_id = tile_itr - tile_faces.begin(); + + // Add glyph to tile_face. + face->add_glyph(glyph); + + label->add_faces(tile_face_id); + label->add_glyphs(glyph.glyph_index); + label->add_x(width_map_[glyph.char_index]); + label->add_y(glyph.offset.y); + } + + itemizer.clear(); + } + } + + // Add a textual representation of the font so that we can figure out + // later what font we need to use. + for (auto const& face : tile_faces) { + std::string name = face->family + " " + face->style; + mutable_layer->add_faces(name); + // We don't delete the TileFace objects here because + // they are 'owned' by the global faces map and deleted + // later on. + } + + // Insert FAKE stacks + mutable_layer->add_stacks(baton->fontstack); + } + + // Insert SDF glyphs + bitmaps + for (auto const& face : tile_faces) { + llmr::vector::face *mutable_face = tile.add_faces(); + mutable_face->set_family(face->family); + mutable_face->set_style(face->style); + + for (auto const& glyph : face->glyphs) { + llmr::vector::glyph *mutable_glyph = mutable_face->add_glyphs(); + mutable_glyph->set_id(glyph.glyph_index); + mutable_glyph->set_width(glyph.width); + mutable_glyph->set_height(glyph.height); + mutable_glyph->set_left(glyph.left); + mutable_glyph->set_top(glyph.top); + mutable_glyph->set_advance(glyph.advance); + if (glyph.width > 0) { + mutable_glyph->set_bitmap(glyph.bitmap); + } + } + } +} + +void Tile::AsyncRange(uv_work_t* req) { + ShapeBaton* baton = static_cast(req->data); fontserver::freetype_engine font_engine_; fontserver::face_manager_freetype font_manager(font_engine_); @@ -192,8 +366,6 @@ void Tile::AsyncShape(uv_work_t* req) { mutable_glyph->set_bitmap(glyph.bitmap); } } - - delete face; } } diff --git a/src/tile.hpp b/src/tile.hpp index dbb9ea1..b484003 100644 --- a/src/tile.hpp +++ b/src/tile.hpp @@ -19,6 +19,7 @@ protected: static v8::Handle Shape(const v8::Arguments& args); static void AsyncShape(uv_work_t* req); + static void AsyncRange(uv_work_t* req); static void ShapeAfter(uv_work_t* req); public: llmr::vector::tile tile;