summaryrefslogtreecommitdiff
path: root/src/mbgl/text/glyph_store.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mbgl/text/glyph_store.cpp')
-rw-r--r--src/mbgl/text/glyph_store.cpp309
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);
+}
+
}