diff options
Diffstat (limited to 'src/mbgl/text/glyph_store.cpp')
-rw-r--r-- | src/mbgl/text/glyph_store.cpp | 309 |
1 files changed, 44 insertions, 265 deletions
diff --git a/src/mbgl/text/glyph_store.cpp b/src/mbgl/text/glyph_store.cpp index 37e26b193c..9520b63c06 100644 --- a/src/mbgl/text/glyph_store.cpp +++ b/src/mbgl/text/glyph_store.cpp @@ -1,264 +1,19 @@ #include <mbgl/text/glyph_store.hpp> +#include <mbgl/text/glyph_pbf.hpp> +#include <mbgl/text/font_stack.hpp> -#include <mbgl/map/environment.hpp> -#include <mbgl/util/std.hpp> -#include <mbgl/util/string.hpp> -#include <mbgl/util/utf.hpp> -#include <mbgl/util/pbf.hpp> -#include <mbgl/util/url.hpp> -#include <mbgl/util/constants.hpp> -#include <mbgl/util/token.hpp> -#include <mbgl/util/math.hpp> +#include <mbgl/util/exception.hpp> #include <mbgl/util/uv_detail.hpp> -#include <mbgl/storage/file_source.hpp> -#include <mbgl/platform/log.hpp> -#include <mbgl/platform/platform.hpp> -#include <mbgl/util/uv_detail.hpp> -#include <algorithm> namespace mbgl { - -void FontStack::insert(uint32_t id, const SDFGlyph &glyph) { - std::lock_guard<std::mutex> lock(mtx); - metrics.emplace(id, glyph.metrics); - bitmaps.emplace(id, glyph.bitmap); - sdfs.emplace(id, glyph); -} - -const std::map<uint32_t, GlyphMetrics> &FontStack::getMetrics() const { - std::lock_guard<std::mutex> lock(mtx); - return metrics; -} - -const std::map<uint32_t, SDFGlyph> &FontStack::getSDFs() const { - std::lock_guard<std::mutex> lock(mtx); - return sdfs; -} - -const Shaping FontStack::getShaping(const std::u32string &string, const float maxWidth, - const float lineHeight, const float horizontalAlign, - const float verticalAlign, const float justify, - const float spacing, const vec2<float> &translate) const { - std::lock_guard<std::mutex> lock(mtx); - - Shaping shaping(translate.x * 24, translate.y * 24); - - // the y offset *should* be part of the font metadata - const int32_t yOffset = -17; - - int32_t x = std::round(translate.x * 24); // one em - const int32_t y = std::round(translate.y * 24) + yOffset; // one em - - // Loop through all characters of this label and shape. - for (uint32_t chr : string) { - shaping.positionedGlyphs.emplace_back(chr, x, y); - auto metric = metrics.find(chr); - if (metric != metrics.end()) { - x += metric->second.advance + spacing; - } - } - - if (!shaping.positionedGlyphs.size()) - return shaping; - - lineWrap(shaping, lineHeight, maxWidth, horizontalAlign, verticalAlign, justify); - - return shaping; -} - -void align(Shaping &shaping, const float justify, const float horizontalAlign, - const float verticalAlign, const uint32_t maxLineLength, const float lineHeight, - const uint32_t line) { - const float shiftX = (justify - horizontalAlign) * maxLineLength; - const float shiftY = (-verticalAlign * (line + 1) + 0.5) * lineHeight; - - for (auto& glyph : shaping.positionedGlyphs) { - glyph.x += shiftX; - glyph.y += shiftY; - } -} - -void justifyLine(std::vector<PositionedGlyph> &positionedGlyphs, const std::map<uint32_t, GlyphMetrics> &metrics, uint32_t start, - uint32_t end, float justify) { - PositionedGlyph &glyph = positionedGlyphs[end]; - auto metric = metrics.find(glyph.glyph); - if (metric != metrics.end()) { - const uint32_t lastAdvance = metric->second.advance; - const float lineIndent = float(glyph.x + lastAdvance) * justify; - - for (uint32_t j = start; j <= end; j++) { - positionedGlyphs[j].x -= lineIndent; - } - } -} - -void FontStack::lineWrap(Shaping &shaping, const float lineHeight, const float maxWidth, - const float horizontalAlign, const float verticalAlign, - const float justify) const { - uint32_t lastSafeBreak = 0; - - uint32_t lengthBeforeCurrentLine = 0; - uint32_t lineStartIndex = 0; - uint32_t line = 0; - - uint32_t maxLineLength = 0; - - std::vector<PositionedGlyph> &positionedGlyphs = shaping.positionedGlyphs; - - if (maxWidth) { - for (uint32_t i = 0; i < positionedGlyphs.size(); i++) { - PositionedGlyph &shape = positionedGlyphs[i]; - - shape.x -= lengthBeforeCurrentLine; - shape.y += lineHeight * line; - - if (shape.x > maxWidth && lastSafeBreak > 0) { - - uint32_t lineLength = positionedGlyphs[lastSafeBreak + 1].x; - maxLineLength = util::max(lineLength, maxLineLength); - - for (uint32_t k = lastSafeBreak + 1; k <= i; k++) { - positionedGlyphs[k].y += lineHeight; - positionedGlyphs[k].x -= lineLength; - } - - if (justify) { - justifyLine(positionedGlyphs, metrics, lineStartIndex, lastSafeBreak - 1, justify); - } - - lineStartIndex = lastSafeBreak + 1; - lastSafeBreak = 0; - lengthBeforeCurrentLine += lineLength; - line++; - } - - if (shape.glyph == 32) { - lastSafeBreak = i; - } - } - } - - const PositionedGlyph& lastPositionedGlyph = positionedGlyphs.back(); - const auto lastGlyphMetric = metrics.find(lastPositionedGlyph.glyph); - assert(lastGlyphMetric != metrics.end()); - const uint32_t lastLineLength = lastPositionedGlyph.x + lastGlyphMetric->second.advance; - maxLineLength = std::max(maxLineLength, lastLineLength); - - const uint32_t height = (line + 1) * lineHeight; - - justifyLine(positionedGlyphs, metrics, lineStartIndex, uint32_t(positionedGlyphs.size()) - 1, justify); - align(shaping, justify, horizontalAlign, verticalAlign, maxLineLength, lineHeight, line); - - // Calculate the bounding box - shaping.top += -verticalAlign * height; - shaping.bottom = shaping.top + height; - shaping.left += -horizontalAlign * maxLineLength; - shaping.right = shaping.left + maxLineLength; -} - -GlyphPBF::GlyphPBF(const std::string& glyphURL, - const std::string& fontStack, - GlyphRange glyphRange, - Environment& env_, - const GlyphLoadedCallback& callback) - : parsed(false), env(env_) { - // Load the glyph set URL - std::string url = util::replaceTokens(glyphURL, [&](const std::string &name) -> std::string { - if (name == "fontstack") return util::percentEncode(fontStack); - if (name == "range") return util::toString(glyphRange.first) + "-" + util::toString(glyphRange.second); - return ""; - }); - - // The prepare call jumps back to the main thread. - req = env.request({ Resource::Kind::Glyphs, url }, [&, url, callback](const Response &res) { - req = nullptr; - - if (res.status != Response::Successful) { - // Something went wrong with loading the glyph pbf. - const std::string msg = std::string { "[ERROR] failed to load glyphs: " } + url + " message: " + res.message; - Log::Error(Event::HttpRequest, msg); - } else { - // Transfer the data to the GlyphSet and signal its availability. - // Once it is available, the caller will need to call parse() to actually - // parse the data we received. We are not doing this here since this callback is being - // called from another (unknown) thread. - data = res.data; - parsed = true; - callback(this); - } - }); -} - -GlyphPBF::~GlyphPBF() { - if (req) { - env.cancelRequest(req); - } -} - -void GlyphPBF::parse(FontStack &stack) { - std::lock_guard<std::mutex> lock(mtx); - - if (!data.size()) { - // If there is no data, this means we either haven't received any data, or - // we have already parsed the data. - return; - } - - // Parse the glyph PBF - pbf glyphs_pbf(reinterpret_cast<const uint8_t *>(data.data()), data.size()); - - while (glyphs_pbf.next()) { - if (glyphs_pbf.tag == 1) { // stacks - pbf fontstack_pbf = glyphs_pbf.message(); - while (fontstack_pbf.next()) { - if (fontstack_pbf.tag == 3) { // glyphs - pbf glyph_pbf = fontstack_pbf.message(); - - SDFGlyph glyph; - - while (glyph_pbf.next()) { - if (glyph_pbf.tag == 1) { // id - glyph.id = glyph_pbf.varint(); - } else if (glyph_pbf.tag == 2) { // bitmap - glyph.bitmap = glyph_pbf.string(); - } else if (glyph_pbf.tag == 3) { // width - glyph.metrics.width = glyph_pbf.varint(); - } else if (glyph_pbf.tag == 4) { // height - glyph.metrics.height = glyph_pbf.varint(); - } else if (glyph_pbf.tag == 5) { // left - glyph.metrics.left = glyph_pbf.svarint(); - } else if (glyph_pbf.tag == 6) { // top - glyph.metrics.top = glyph_pbf.svarint(); - } else if (glyph_pbf.tag == 7) { // advance - glyph.metrics.advance = glyph_pbf.varint(); - } else { - glyph_pbf.skip(); - } - } - - stack.insert(glyph.id, glyph); - } else { - fontstack_pbf.skip(); - } - } - } else { - glyphs_pbf.skip(); - } - } - - data.clear(); -} - -bool GlyphPBF::isParsed() const { - return parsed; -} - GlyphStore::GlyphStore(uv_loop_t* loop, Environment& env_) : env(env_), - asyncEmitGlyphRangeLoaded(util::make_unique<uv::async>(loop, [this] { emitGlyphRangeLoaded(); })), + asyncEmitGlyphRangeLoaded(std::make_unique<uv::async>(loop, [this] { emitGlyphRangeLoaded(); })), + asyncEmitGlyphRangeLoadedingFailed(std::make_unique<uv::async>(loop, [this] { emitGlyphRangeLoadingFailed(); })), observer(nullptr) { asyncEmitGlyphRangeLoaded->unref(); + asyncEmitGlyphRangeLoadedingFailed->unref(); } GlyphStore::~GlyphStore() { @@ -269,7 +24,7 @@ void GlyphStore::setURL(const std::string &url) { glyphURL = url; } -bool GlyphStore::requestGlyphRangesIfNeeded(const std::string& fontStack, +bool GlyphStore::requestGlyphRangesIfNeeded(const std::string& fontStackName, const std::set<GlyphRange>& glyphRanges) { bool requestIsNeeded = false; @@ -277,18 +32,32 @@ bool GlyphStore::requestGlyphRangesIfNeeded(const std::string& fontStack, return requestIsNeeded; } - auto callback = [this, fontStack](GlyphPBF* glyph) { - glyph->parse(*createFontStack(fontStack)); - asyncEmitGlyphRangeLoaded->send(); + auto successCallback = [this, fontStackName](GlyphPBF* glyph) { + auto fontStack = createFontStack(fontStackName); + try { + glyph->parse(**fontStack); + asyncEmitGlyphRangeLoaded->send(); + } catch (const std::exception&) { + std::lock_guard<std::mutex> lock(errorMessageMutex); + errorMessage = "Failed to parse [" + glyph->getURL() + "]"; + asyncEmitGlyphRangeLoadedingFailed->send(); + } + }; + + auto failureCallback = [this](const std::string& message) { + std::lock_guard<std::mutex> lock(errorMessageMutex); + errorMessage = message; + asyncEmitGlyphRangeLoadedingFailed->send(); }; std::lock_guard<std::mutex> lock(rangesMutex); - auto& rangeSets = ranges[fontStack]; + auto& rangeSets = ranges[fontStackName]; for (const auto& range : glyphRanges) { const auto& rangeSets_it = rangeSets.find(range); if (rangeSets_it == rangeSets.end()) { - auto glyph = util::make_unique<GlyphPBF>(glyphURL, fontStack, range, env, callback); + auto glyph = std::make_unique<GlyphPBF>(glyphURL, fontStackName, range, env, + successCallback, failureCallback); rangeSets.emplace(range, std::move(glyph)); requestIsNeeded = true; continue; @@ -302,26 +71,26 @@ bool GlyphStore::requestGlyphRangesIfNeeded(const std::string& fontStack, return requestIsNeeded; } -FontStack* GlyphStore::createFontStack(const std::string &fontStack) { - std::lock_guard<std::mutex> lock(stacksMutex); +util::exclusive<FontStack> GlyphStore::createFontStack(const std::string &fontStack) { + auto lock = std::make_unique<std::lock_guard<std::mutex>>(stacksMutex); auto stack_it = stacks.find(fontStack); if (stack_it == stacks.end()) { - stack_it = stacks.emplace(fontStack, util::make_unique<FontStack>()).first; + stack_it = stacks.emplace(fontStack, std::make_unique<FontStack>()).first; } - return stack_it->second.get(); + return { stack_it->second.get(), std::move(lock) }; } -FontStack* GlyphStore::getFontStack(const std::string &fontStack) { - std::lock_guard<std::mutex> lock(stacksMutex); +util::exclusive<FontStack> GlyphStore::getFontStack(const std::string &fontStack) { + auto lock = std::make_unique<std::lock_guard<std::mutex>>(stacksMutex); const auto& stack_it = stacks.find(fontStack); if (stack_it == stacks.end()) { - return nullptr; + return { nullptr, nullptr }; } - return stack_it->second.get(); + return { stack_it->second.get(), std::move(lock) }; } void GlyphStore::setObserver(Observer* observer_) { @@ -334,4 +103,14 @@ void GlyphStore::emitGlyphRangeLoaded() { } } +void GlyphStore::emitGlyphRangeLoadingFailed() { + if (!observer) { + return; + } + + std::lock_guard<std::mutex> lock(errorMessageMutex); + auto error = std::make_exception_ptr(util::GlyphRangeLoadingException(errorMessage)); + observer->onGlyphRangeLoadingFailed(error); +} + } |