summaryrefslogtreecommitdiff
path: root/src/mbgl/text
diff options
context:
space:
mode:
authorJohn Firebaugh <john.firebaugh@gmail.com>2017-06-01 10:30:49 -0700
committerJohn Firebaugh <john.firebaugh@gmail.com>2017-06-13 10:18:43 -0700
commit92252849c1a2ddf7887d1908841fa3c90dd59766 (patch)
tree9d192d0e60b46479122ea2e4a606b598287d49d7 /src/mbgl/text
parentb201a7900f989af432aaea500a0e6c5ea5bbba53 (diff)
downloadqtlocation-mapboxgl-92252849c1a2ddf7887d1908841fa3c90dd59766.tar.gz
[core] Per-bucket glyph atlases
Diffstat (limited to 'src/mbgl/text')
-rw-r--r--src/mbgl/text/glyph.hpp20
-rw-r--r--src/mbgl/text/glyph_atlas.cpp280
-rw-r--r--src/mbgl/text/glyph_atlas.hpp105
-rw-r--r--src/mbgl/text/glyph_manager.cpp145
-rw-r--r--src/mbgl/text/glyph_manager.hpp68
-rw-r--r--src/mbgl/text/glyph_manager_observer.hpp (renamed from src/mbgl/text/glyph_atlas_observer.hpp)4
-rw-r--r--src/mbgl/text/glyph_pbf.cpp10
-rw-r--r--src/mbgl/text/glyph_pbf.hpp18
-rw-r--r--src/mbgl/text/quads.cpp4
-rw-r--r--src/mbgl/text/quads.hpp2
-rw-r--r--src/mbgl/text/shaping.cpp18
-rw-r--r--src/mbgl/text/shaping.hpp3
12 files changed, 304 insertions, 373 deletions
diff --git a/src/mbgl/text/glyph.hpp b/src/mbgl/text/glyph.hpp
index 7cfb9aa0d9..b9eaedd302 100644
--- a/src/mbgl/text/glyph.hpp
+++ b/src/mbgl/text/glyph.hpp
@@ -5,6 +5,8 @@
#include <mbgl/util/rect.hpp>
#include <mbgl/util/traits.hpp>
#include <mbgl/util/optional.hpp>
+#include <mbgl/util/immutable.hpp>
+#include <mbgl/util/image.hpp>
#include <cstdint>
#include <vector>
@@ -35,13 +37,23 @@ inline bool operator==(const GlyphMetrics& lhs, const GlyphMetrics& rhs) {
lhs.advance == rhs.advance;
}
-struct Glyph {
- Rect<uint16_t> rect;
+class Glyph {
+public:
+ // We're using this value throughout the Mapbox GL ecosystem. If this is different, the glyphs
+ // also need to be reencoded.
+ static constexpr const uint8_t borderSize = 3;
+
+ GlyphID id = 0;
+
+ // A signed distance field of the glyph with a border (see above).
+ AlphaImage bitmap;
+
+ // Glyph metrics
GlyphMetrics metrics;
};
-using GlyphPositions = std::map<GlyphID, optional<Glyph>>;
-using GlyphPositionMap = std::map<FontStack, GlyphPositions>;
+using Glyphs = std::map<GlyphID, optional<Immutable<Glyph>>>;
+using GlyphMap = std::map<FontStack, Glyphs>;
class PositionedGlyph {
public:
diff --git a/src/mbgl/text/glyph_atlas.cpp b/src/mbgl/text/glyph_atlas.cpp
index c6083fe0cc..a08455ec63 100644
--- a/src/mbgl/text/glyph_atlas.cpp
+++ b/src/mbgl/text/glyph_atlas.cpp
@@ -1,256 +1,64 @@
#include <mbgl/text/glyph_atlas.hpp>
-#include <mbgl/text/glyph_atlas_observer.hpp>
-#include <mbgl/text/glyph_pbf.hpp>
-#include <mbgl/gl/context.hpp>
-#include <mbgl/util/logging.hpp>
-#include <mbgl/util/platform.hpp>
-#include <mbgl/storage/file_source.hpp>
-#include <mbgl/storage/resource.hpp>
-#include <mbgl/storage/response.hpp>
-#include <cassert>
-#include <algorithm>
+#include <mapbox/shelf-pack.hpp>
namespace mbgl {
-static GlyphAtlasObserver nullObserver;
+static constexpr uint32_t padding = 1;
-GlyphAtlas::GlyphAtlas(const Size size, FileSource& fileSource_)
- : fileSource(fileSource_),
- observer(&nullObserver),
- bin(size.width, size.height),
- image(size),
- dirty(true) {
-}
-
-GlyphAtlas::~GlyphAtlas() = default;
+GlyphAtlas makeGlyphAtlas(const Glyphs& glyphs) {
+ GlyphAtlas result;
-void GlyphAtlas::getGlyphs(GlyphRequestor& requestor, GlyphDependencies glyphDependencies) {
- auto dependencies = std::make_shared<GlyphDependencies>(std::move(glyphDependencies));
+ mapbox::ShelfPack::ShelfPackOptions options;
+ options.autoResize = true;
+ mapbox::ShelfPack pack(0, 0, options);
- // Figure out which glyph ranges need to be fetched. For each range that does need to
- // be fetched, record an entry mapping the requestor to a shared pointer containing the
- // dependencies. When the shared pointer becomes unique, we know that all the dependencies
- // for that requestor have been fetched, and can notify it of completion.
- for (const auto& dependency : *dependencies) {
- const FontStack& fontStack = dependency.first;
- Entry& entry = entries[fontStack];
+ std::vector<mapbox::Bin> bins;
+ bins.reserve(glyphs.size());
- const GlyphIDs& glyphIDs = dependency.second;
- GlyphRangeSet ranges;
- for (const auto& glyphID : glyphIDs) {
- ranges.insert(getGlyphRange(glyphID));
- }
-
- for (const auto& range : ranges) {
- auto it = entry.ranges.find(range);
- if (it == entry.ranges.end() || !it->second.parsed) {
- GlyphRequest& request = requestRange(entry, fontStack, range);
- request.requestors[&requestor] = dependencies;
- }
+ for (const auto& entry : glyphs) {
+ if (entry.second && (*entry.second)->bitmap.valid()) {
+ const Glyph& glyph = **entry.second;
+ bins.emplace_back(glyph.id,
+ glyph.bitmap.size.width + 2 * padding,
+ glyph.bitmap.size.height + 2 * padding);
}
}
- // If the shared dependencies pointer is already unique, then all dependent glyph ranges
- // have already been loaded. Send a notification immediately.
- if (dependencies.unique()) {
- addGlyphs(requestor, *dependencies);
- }
-}
-
-GlyphAtlas::GlyphRequest& GlyphAtlas::requestRange(Entry& entry, const FontStack& fontStack, const GlyphRange& range) {
- GlyphRequest& request = entry.ranges[range];
-
- if (request.req) {
- return request;
- }
+ mapbox::ShelfPack::PackOptions packOptions;
+ packOptions.inPlace = true;
+ pack.pack(bins, packOptions);
- request.req = fileSource.request(Resource::glyphs(glyphURL, fontStack, range), [this, fontStack, range](Response res) {
- processResponse(res, fontStack, range);
+ result.image = AlphaImage({
+ static_cast<uint32_t>(pack.width()),
+ static_cast<uint32_t>(pack.height())
});
- return request;
-}
-
-void GlyphAtlas::processResponse(const Response& res, const FontStack& fontStack, const GlyphRange& range) {
- if (res.error) {
- observer->onGlyphsError(fontStack, range, std::make_exception_ptr(std::runtime_error(res.error->message)));
- return;
- }
-
- if (res.notModified) {
- return;
- }
-
- Entry& entry = entries[fontStack];
- GlyphRequest& request = entry.ranges[range];
-
- if (!res.noContent) {
- std::vector<SDFGlyph> glyphs;
-
- try {
- glyphs = parseGlyphPBF(range, *res.data);
- } catch (...) {
- observer->onGlyphsError(fontStack, range, std::current_exception());
- return;
- }
-
- for (auto& glyph : glyphs) {
- auto it = entry.glyphs.find(glyph.id);
- if (it == entry.glyphs.end()) {
- // Glyph doesn't exist yet.
- entry.glyphs.emplace(glyph.id, GlyphValue {
- std::move(glyph.bitmap),
- std::move(glyph.metrics),
- {}, {}
- });
- } else if (it->second.metrics == glyph.metrics) {
- if (it->second.bitmap != glyph.bitmap) {
- // The actual bitmap was updated; this is unsupported.
- Log::Warning(Event::Glyph, "Modified glyph changed bitmap represenation");
- }
- // At least try to update it in case it's currently unused.
- // If it is already used, we won't attempt to update the glyph atlas texture.
- it->second.bitmap = std::move(glyph.bitmap);
- } else {
- // The metrics were updated; this is unsupported.
- Log::Warning(Event::Glyph, "Modified glyph has different metrics");
- return;
- }
- }
+ for (const auto& bin : bins) {
+ const Glyph& glyph = **glyphs.at(bin.id);
+
+ AlphaImage::copy(glyph.bitmap,
+ result.image,
+ { 0, 0 },
+ {
+ bin.x + padding,
+ bin.y + padding
+ },
+ glyph.bitmap.size);
+
+ result.positions.emplace(glyph.id,
+ GlyphPosition {
+ Rect<uint16_t> {
+ static_cast<uint16_t>(bin.x),
+ static_cast<uint16_t>(bin.y),
+ static_cast<uint16_t>(bin.w),
+ static_cast<uint16_t>(bin.h)
+ },
+ glyph.metrics
+ });
}
- request.parsed = true;
-
- for (auto& pair : request.requestors) {
- GlyphRequestor& requestor = *pair.first;
- const std::shared_ptr<GlyphDependencies>& dependencies = pair.second;
- if (dependencies.unique()) {
- addGlyphs(requestor, *dependencies);
- }
- }
-
- request.requestors.clear();
-
- observer->onGlyphsLoaded(fontStack, range);
-}
-
-void GlyphAtlas::setObserver(GlyphAtlasObserver* observer_) {
- observer = observer_ ? observer_ : &nullObserver;
-}
-
-void GlyphAtlas::addGlyphs(GlyphRequestor& requestor, const GlyphDependencies& glyphDependencies) {
- GlyphPositionMap glyphPositions;
-
- for (const auto& dependency : glyphDependencies) {
- const FontStack& fontStack = dependency.first;
- const GlyphIDs& glyphIDs = dependency.second;
-
- GlyphPositions& positions = glyphPositions[fontStack];
- Entry& entry = entries[fontStack];
-
- for (const auto& glyphID : glyphIDs) {
- // Make a glyph position entry even if we didn't get an SDF for the glyph. During layout,
- // an empty optional is treated as "loaded but nothing to show", wheras no entry in the
- // positions map means "not loaded yet".
- optional<Glyph>& glyph = positions[glyphID];
-
- auto it = entry.glyphs.find(glyphID);
- if (it == entry.glyphs.end())
- continue;
-
- it->second.ids.insert(&requestor);
-
- glyph = Glyph {
- addGlyph(it->second),
- it->second.metrics
- };
- }
- }
-
- requestor.onGlyphsAvailable(glyphPositions);
-}
-
-Rect<uint16_t> GlyphAtlas::addGlyph(GlyphValue& value) {
- // The glyph is already in this texture.
- if (value.rect) {
- return *value.rect;
- }
-
- // We don't need to add glyphs without a bitmap (e.g. whitespace).
- if (!value.bitmap.valid()) {
- return {};
- }
-
- // Add a 1px border around every image.
- const uint32_t padding = 1;
- const uint16_t width = value.bitmap.size.width + 2 * padding;
- const uint16_t height = value.bitmap.size.height + 2 * padding;
-
- Rect<uint16_t> rect = bin.allocate(width, height);
- if (rect.w == 0) {
- Log::Error(Event::OpenGL, "glyph bitmap overflow");
- return {};
- }
-
- AlphaImage::copy(value.bitmap, image, { 0, 0 }, { rect.x + padding, rect.y + padding }, value.bitmap.size);
- value.rect = rect;
- dirty = true;
-
- return rect;
-}
-
-void GlyphAtlas::removeGlyphValues(GlyphRequestor& requestor, std::map<GlyphID, GlyphValue>& values) {
- for (auto& it : values) {
- GlyphValue& value = it.second;
- if (value.ids.erase(&requestor) && value.ids.empty() && value.rect) {
- const Rect<uint16_t>& rect = *value.rect;
-
- // Clear out the bitmap.
- uint8_t *target = image.data.get();
- for (uint32_t y = 0; y < rect.h; y++) {
- uint32_t y1 = image.size.width * (rect.y + y) + rect.x;
- for (uint32_t x = 0; x < rect.w; x++) {
- target[y1 + x] = 0;
- }
- }
-
- bin.release(rect);
- value.rect = {};
- }
- }
-}
-
-void GlyphAtlas::removePendingRanges(mbgl::GlyphRequestor& requestor, std::map<GlyphRange, GlyphRequest>& ranges) {
- for (auto& range : ranges) {
- range.second.requestors.erase(&requestor);
- }
-}
-
-void GlyphAtlas::removeGlyphs(GlyphRequestor& requestor) {
- for (auto& entry : entries) {
- removeGlyphValues(requestor, entry.second.glyphs);
- removePendingRanges(requestor, entry.second.ranges);
- }
-}
-
-Size GlyphAtlas::getSize() const {
- return image.size;
-}
-
-void GlyphAtlas::upload(gl::Context& context, gl::TextureUnit unit) {
- if (!texture) {
- texture = context.createTexture(image, unit);
- } else if (dirty) {
- context.updateTexture(*texture, image, unit);
- }
-
- dirty = false;
-}
-
-void GlyphAtlas::bind(gl::Context& context, gl::TextureUnit unit) {
- upload(context, unit);
- context.bindTexture(*texture, unit, gl::TextureFilter::Linear);
+ return result;
}
} // namespace mbgl
diff --git a/src/mbgl/text/glyph_atlas.hpp b/src/mbgl/text/glyph_atlas.hpp
index cd6f57d57f..7a90085642 100644
--- a/src/mbgl/text/glyph_atlas.hpp
+++ b/src/mbgl/text/glyph_atlas.hpp
@@ -1,109 +1,24 @@
#pragma once
#include <mbgl/text/glyph.hpp>
-#include <mbgl/text/glyph_atlas_observer.hpp>
-#include <mbgl/text/glyph_range.hpp>
-#include <mbgl/geometry/binpack.hpp>
-#include <mbgl/util/noncopyable.hpp>
-#include <mbgl/util/optional.hpp>
-#include <mbgl/util/font_stack.hpp>
-#include <mbgl/util/image.hpp>
-#include <mbgl/gl/texture.hpp>
-#include <mbgl/gl/object.hpp>
-#include <string>
-#include <unordered_set>
-#include <unordered_map>
-
-class GlyphAtlasTest;
+#include <mapbox/shelf-pack.hpp>
namespace mbgl {
-class FileSource;
-class AsyncRequest;
-class Response;
-
-namespace gl {
-class Context;
-} // namespace gl
-
-class GlyphRequestor {
-public:
- virtual ~GlyphRequestor() = default;
- virtual void onGlyphsAvailable(GlyphPositionMap) = 0;
+struct GlyphPosition {
+ Rect<uint16_t> rect;
+ GlyphMetrics metrics;
};
-
-class GlyphAtlas : public util::noncopyable {
-public:
- GlyphAtlas(Size, FileSource&);
- ~GlyphAtlas();
-
- // Workers send a `getGlyphs` message to the main thread once they have determined
- // which glyphs they will need. Invoking this method will increment reference
- // counts for all the glyphs in `GlyphDependencies`. If all glyphs are already
- // locally available, the observer will be notified that the glyphs are available
- // immediately. Otherwise, a request on the FileSource is made, and when all glyphs
- // are parsed and added to the atlas, the observer will be notified.
- // Workers are given a copied 'GlyphPositions' map to use for placing their glyphs.
- // The positions specified in this object are guaranteed to be
- // valid for the lifetime of the tile.
- void getGlyphs(GlyphRequestor&, GlyphDependencies);
- void removeGlyphs(GlyphRequestor&);
-
- void setURL(const std::string& url) {
- glyphURL = url;
- }
-
- void setObserver(GlyphAtlasObserver*);
-
- // Binds the atlas texture to the GPU, and uploads data if it is out of date.
- void bind(gl::Context&, gl::TextureUnit unit);
-
- // Uploads the texture to the GPU to be available when we need it. This is a lazy operation;
- // the texture is only bound when the data is out of date (=dirty).
- void upload(gl::Context&, gl::TextureUnit unit);
-
- Size getSize() const;
-private:
- FileSource& fileSource;
- std::string glyphURL;
+using GlyphPositions = std::map<GlyphID, GlyphPosition>;
- struct GlyphValue {
- AlphaImage bitmap;
- GlyphMetrics metrics;
- optional<Rect<uint16_t>> rect;
- std::unordered_set<GlyphRequestor*> ids;
- };
-
- struct GlyphRequest {
- bool parsed = false;
- std::unique_ptr<AsyncRequest> req;
- std::unordered_map<GlyphRequestor*, std::shared_ptr<GlyphDependencies>> requestors;
- };
-
- struct Entry {
- std::map<GlyphRange, GlyphRequest> ranges;
- std::map<GlyphID, GlyphValue> glyphs;
- };
-
- std::unordered_map<FontStack, Entry, FontStackHash> entries;
-
- GlyphRequest& requestRange(Entry&, const FontStack&, const GlyphRange&);
- void processResponse(const Response&, const FontStack&, const GlyphRange&);
-
- void addGlyphs(GlyphRequestor&, const GlyphDependencies&);
- Rect<uint16_t> addGlyph(GlyphValue&);
-
- void removeGlyphValues(GlyphRequestor&, std::map<GlyphID, GlyphValue>&);
- void removePendingRanges(GlyphRequestor&, std::map<GlyphRange, GlyphRequest>&);
-
- GlyphAtlasObserver* observer = nullptr;
-
- BinPack<uint16_t> bin;
+class GlyphAtlas {
+public:
AlphaImage image;
- bool dirty;
- mbgl::optional<gl::Texture> texture;
+ GlyphPositions positions;
};
+GlyphAtlas makeGlyphAtlas(const Glyphs&);
+
} // namespace mbgl
diff --git a/src/mbgl/text/glyph_manager.cpp b/src/mbgl/text/glyph_manager.cpp
new file mode 100644
index 0000000000..916d39ae62
--- /dev/null
+++ b/src/mbgl/text/glyph_manager.cpp
@@ -0,0 +1,145 @@
+#include <mbgl/text/glyph_manager.hpp>
+#include <mbgl/text/glyph_manager_observer.hpp>
+#include <mbgl/text/glyph_pbf.hpp>
+#include <mbgl/storage/file_source.hpp>
+#include <mbgl/storage/resource.hpp>
+#include <mbgl/storage/response.hpp>
+
+namespace mbgl {
+
+static GlyphManagerObserver nullObserver;
+
+GlyphManager::GlyphManager(FileSource& fileSource_)
+ : fileSource(fileSource_),
+ observer(&nullObserver) {
+}
+
+GlyphManager::~GlyphManager() = default;
+
+void GlyphManager::getGlyphs(GlyphRequestor& requestor, GlyphDependencies glyphDependencies) {
+ auto dependencies = std::make_shared<GlyphDependencies>(std::move(glyphDependencies));
+
+ // Figure out which glyph ranges need to be fetched. For each range that does need to
+ // be fetched, record an entry mapping the requestor to a shared pointer containing the
+ // dependencies. When the shared pointer becomes unique, we know that all the dependencies
+ // for that requestor have been fetched, and can notify it of completion.
+ for (const auto& dependency : *dependencies) {
+ const FontStack& fontStack = dependency.first;
+ Entry& entry = entries[fontStack];
+
+ const GlyphIDs& glyphIDs = dependency.second;
+ GlyphRangeSet ranges;
+ for (const auto& glyphID : glyphIDs) {
+ ranges.insert(getGlyphRange(glyphID));
+ }
+
+ for (const auto& range : ranges) {
+ auto it = entry.ranges.find(range);
+ if (it == entry.ranges.end() || !it->second.parsed) {
+ GlyphRequest& request = requestRange(entry, fontStack, range);
+ request.requestors[&requestor] = dependencies;
+ }
+ }
+ }
+
+ // If the shared dependencies pointer is already unique, then all dependent glyph ranges
+ // have already been loaded. Send a notification immediately.
+ if (dependencies.unique()) {
+ notify(requestor, *dependencies);
+ }
+}
+
+GlyphManager::GlyphRequest& GlyphManager::requestRange(Entry& entry, const FontStack& fontStack, const GlyphRange& range) {
+ GlyphRequest& request = entry.ranges[range];
+
+ if (request.req) {
+ return request;
+ }
+
+ request.req = fileSource.request(Resource::glyphs(glyphURL, fontStack, range), [this, fontStack, range](Response res) {
+ processResponse(res, fontStack, range);
+ });
+
+ return request;
+}
+
+void GlyphManager::processResponse(const Response& res, const FontStack& fontStack, const GlyphRange& range) {
+ if (res.error) {
+ observer->onGlyphsError(fontStack, range, std::make_exception_ptr(std::runtime_error(res.error->message)));
+ return;
+ }
+
+ if (res.notModified) {
+ return;
+ }
+
+ Entry& entry = entries[fontStack];
+ GlyphRequest& request = entry.ranges[range];
+
+ if (!res.noContent) {
+ std::vector<Glyph> glyphs;
+
+ try {
+ glyphs = parseGlyphPBF(range, *res.data);
+ } catch (...) {
+ observer->onGlyphsError(fontStack, range, std::current_exception());
+ return;
+ }
+
+ for (auto& glyph : glyphs) {
+ entry.glyphs.erase(glyph.id);
+ entry.glyphs.emplace(glyph.id, makeMutable<Glyph>(std::move(glyph)));
+ }
+ }
+
+ request.parsed = true;
+
+ for (auto& pair : request.requestors) {
+ GlyphRequestor& requestor = *pair.first;
+ const std::shared_ptr<GlyphDependencies>& dependencies = pair.second;
+ if (dependencies.unique()) {
+ notify(requestor, *dependencies);
+ }
+ }
+
+ request.requestors.clear();
+
+ observer->onGlyphsLoaded(fontStack, range);
+}
+
+void GlyphManager::setObserver(GlyphManagerObserver* observer_) {
+ observer = observer_ ? observer_ : &nullObserver;
+}
+
+void GlyphManager::notify(GlyphRequestor& requestor, const GlyphDependencies& glyphDependencies) {
+ GlyphMap response;
+
+ for (const auto& dependency : glyphDependencies) {
+ const FontStack& fontStack = dependency.first;
+ const GlyphIDs& glyphIDs = dependency.second;
+
+ Glyphs& glyphs = response[fontStack];
+ Entry& entry = entries[fontStack];
+
+ for (const auto& glyphID : glyphIDs) {
+ auto it = entry.glyphs.find(glyphID);
+ if (it != entry.glyphs.end()) {
+ glyphs.emplace(*it);
+ } else {
+ glyphs.emplace(glyphID, std::experimental::nullopt);
+ }
+ }
+ }
+
+ requestor.onGlyphsAvailable(response);
+}
+
+void GlyphManager::removeRequestor(GlyphRequestor& requestor) {
+ for (auto& entry : entries) {
+ for (auto& range : entry.second.ranges) {
+ range.second.requestors.erase(&requestor);
+ }
+ }
+}
+
+} // namespace mbgl
diff --git a/src/mbgl/text/glyph_manager.hpp b/src/mbgl/text/glyph_manager.hpp
new file mode 100644
index 0000000000..00df079462
--- /dev/null
+++ b/src/mbgl/text/glyph_manager.hpp
@@ -0,0 +1,68 @@
+#pragma once
+
+#include <mbgl/text/glyph.hpp>
+#include <mbgl/text/glyph_manager_observer.hpp>
+#include <mbgl/text/glyph_range.hpp>
+#include <mbgl/util/noncopyable.hpp>
+#include <mbgl/util/font_stack.hpp>
+#include <mbgl/util/immutable.hpp>
+
+#include <string>
+#include <unordered_map>
+
+namespace mbgl {
+
+class FileSource;
+class AsyncRequest;
+class Response;
+
+class GlyphRequestor {
+public:
+ virtual ~GlyphRequestor() = default;
+ virtual void onGlyphsAvailable(GlyphMap) = 0;
+};
+
+class GlyphManager : public util::noncopyable {
+public:
+ GlyphManager(FileSource&);
+ ~GlyphManager();
+
+ // Workers send a `getGlyphs` message to the main thread once they have determined
+ // their `GlyphDependencies`. If all glyphs are already locally available, GlyphManager
+ // will provide them to the requestor immediately. Otherwise, it makes a request on the
+ // FileSource is made for each range neeed, and notifies the observer when all are
+ // complete.
+ void getGlyphs(GlyphRequestor&, GlyphDependencies);
+ void removeRequestor(GlyphRequestor&);
+
+ void setURL(const std::string& url) {
+ glyphURL = url;
+ }
+
+ void setObserver(GlyphManagerObserver*);
+
+private:
+ FileSource& fileSource;
+ std::string glyphURL;
+
+ struct GlyphRequest {
+ bool parsed = false;
+ std::unique_ptr<AsyncRequest> req;
+ std::unordered_map<GlyphRequestor*, std::shared_ptr<GlyphDependencies>> requestors;
+ };
+
+ struct Entry {
+ std::map<GlyphRange, GlyphRequest> ranges;
+ std::map<GlyphID, Immutable<Glyph>> glyphs;
+ };
+
+ std::unordered_map<FontStack, Entry, FontStackHash> entries;
+
+ GlyphRequest& requestRange(Entry&, const FontStack&, const GlyphRange&);
+ void processResponse(const Response&, const FontStack&, const GlyphRange&);
+ void notify(GlyphRequestor&, const GlyphDependencies&);
+
+ GlyphManagerObserver* observer = nullptr;
+};
+
+} // namespace mbgl
diff --git a/src/mbgl/text/glyph_atlas_observer.hpp b/src/mbgl/text/glyph_manager_observer.hpp
index 9841017117..b8678e060a 100644
--- a/src/mbgl/text/glyph_atlas_observer.hpp
+++ b/src/mbgl/text/glyph_manager_observer.hpp
@@ -8,9 +8,9 @@
namespace mbgl {
-class GlyphAtlasObserver {
+class GlyphManagerObserver {
public:
- virtual ~GlyphAtlasObserver() = default;
+ virtual ~GlyphManagerObserver() = default;
virtual void onGlyphsLoaded(const FontStack&, const GlyphRange&) {}
virtual void onGlyphsError(const FontStack&, const GlyphRange&, std::exception_ptr) {}
diff --git a/src/mbgl/text/glyph_pbf.cpp b/src/mbgl/text/glyph_pbf.cpp
index 033f50fe9c..cfaf803f75 100644
--- a/src/mbgl/text/glyph_pbf.cpp
+++ b/src/mbgl/text/glyph_pbf.cpp
@@ -4,8 +4,8 @@
namespace mbgl {
-std::vector<SDFGlyph> parseGlyphPBF(const GlyphRange& glyphRange, const std::string& data) {
- std::vector<SDFGlyph> result;
+std::vector<Glyph> parseGlyphPBF(const GlyphRange& glyphRange, const std::string& data) {
+ std::vector<Glyph> result;
result.reserve(256);
protozero::pbf_reader glyphs_pbf(data);
@@ -15,7 +15,7 @@ std::vector<SDFGlyph> parseGlyphPBF(const GlyphRange& glyphRange, const std::str
while (fontstack_pbf.next(3)) {
auto glyph_pbf = fontstack_pbf.get_message();
- SDFGlyph glyph;
+ Glyph glyph;
protozero::data_view glyphData;
bool hasID = false, hasWidth = false, hasHeight = false, hasLeft = false,
@@ -73,8 +73,8 @@ std::vector<SDFGlyph> parseGlyphPBF(const GlyphRange& glyphRange, const std::str
// with the implicit border size, otherwise we expect there to be no bitmap at all.
if (glyph.metrics.width && glyph.metrics.height) {
const Size size {
- glyph.metrics.width + 2 * SDFGlyph::borderSize,
- glyph.metrics.height + 2 * SDFGlyph::borderSize
+ glyph.metrics.width + 2 * Glyph::borderSize,
+ glyph.metrics.height + 2 * Glyph::borderSize
};
if (size.area() != glyphData.size()) {
diff --git a/src/mbgl/text/glyph_pbf.hpp b/src/mbgl/text/glyph_pbf.hpp
index 162aeed93a..28a28b4114 100644
--- a/src/mbgl/text/glyph_pbf.hpp
+++ b/src/mbgl/text/glyph_pbf.hpp
@@ -2,28 +2,12 @@
#include <mbgl/text/glyph.hpp>
#include <mbgl/text/glyph_range.hpp>
-#include <mbgl/util/image.hpp>
#include <string>
#include <vector>
namespace mbgl {
-class SDFGlyph {
-public:
- // We're using this value throughout the Mapbox GL ecosystem. If this is different, the glyphs
- // also need to be reencoded.
- static constexpr const uint8_t borderSize = 3;
-
- GlyphID id = 0;
-
- // A signed distance field of the glyph with a border (see above).
- AlphaImage bitmap;
-
- // Glyph metrics
- GlyphMetrics metrics;
-};
-
-std::vector<SDFGlyph> parseGlyphPBF(const GlyphRange&, const std::string& data);
+std::vector<Glyph> parseGlyphPBF(const GlyphRange&, const std::string& data);
} // namespace mbgl
diff --git a/src/mbgl/text/quads.cpp b/src/mbgl/text/quads.cpp
index 33b94d7114..ad57342b82 100644
--- a/src/mbgl/text/quads.cpp
+++ b/src/mbgl/text/quads.cpp
@@ -315,10 +315,10 @@ SymbolQuads getGlyphQuads(Anchor& anchor,
for (const PositionedGlyph &positionedGlyph: shapedText.positionedGlyphs) {
auto face_it = face.find(positionedGlyph.glyph);
- if (face_it == face.end() || !face_it->second || !(*face_it->second).rect.hasArea())
+ if (face_it == face.end())
continue;
- const Glyph& glyph = *face_it->second;
+ const GlyphPosition& glyph = face_it->second;
const Rect<uint16_t>& rect = glyph.rect;
const float centerX = (positionedGlyph.x + glyph.metrics.advance / 2.0f) * boxScale;
diff --git a/src/mbgl/text/quads.hpp b/src/mbgl/text/quads.hpp
index 38dad95243..89df423529 100644
--- a/src/mbgl/text/quads.hpp
+++ b/src/mbgl/text/quads.hpp
@@ -1,6 +1,6 @@
#pragma once
-#include <mbgl/text/glyph.hpp>
+#include <mbgl/text/glyph_atlas.hpp>
#include <mbgl/style/types.hpp>
#include <mbgl/style/layers/symbol_layer_properties.hpp>
#include <mbgl/tile/geometry_tile_data.hpp>
diff --git a/src/mbgl/text/shaping.cpp b/src/mbgl/text/shaping.cpp
index f9f90627d4..0ef347ba0b 100644
--- a/src/mbgl/text/shaping.cpp
+++ b/src/mbgl/text/shaping.cpp
@@ -42,7 +42,7 @@ void align(Shaping& shaping,
// justify left = 0, right = 1, center = .5
void justifyLine(std::vector<PositionedGlyph>& positionedGlyphs,
- const GlyphPositions& glyphs,
+ const Glyphs& glyphs,
std::size_t start,
std::size_t end,
float justify) {
@@ -53,7 +53,7 @@ void justifyLine(std::vector<PositionedGlyph>& positionedGlyphs,
PositionedGlyph& glyph = positionedGlyphs[end];
auto it = glyphs.find(glyph.glyph);
if (it != glyphs.end() && it->second) {
- const uint32_t lastAdvance = it->second->metrics.advance;
+ const uint32_t lastAdvance = (*it->second)->metrics.advance;
const float lineIndent = float(glyph.x + lastAdvance) * justify;
for (std::size_t j = start; j <= end; j++) {
@@ -65,13 +65,13 @@ void justifyLine(std::vector<PositionedGlyph>& positionedGlyphs,
float determineAverageLineWidth(const std::u16string& logicalInput,
const float spacing,
float maxWidth,
- const GlyphPositions& glyphs) {
+ const Glyphs& glyphs) {
float totalWidth = 0;
for (char16_t chr : logicalInput) {
auto it = glyphs.find(chr);
if (it != glyphs.end() && it->second) {
- totalWidth += it->second->metrics.advance + spacing;
+ totalWidth += (*it->second)->metrics.advance + spacing;
}
}
@@ -164,7 +164,7 @@ std::set<std::size_t> determineLineBreaks(const std::u16string& logicalInput,
const float spacing,
float maxWidth,
const WritingModeType writingMode,
- const GlyphPositions& glyphs) {
+ const Glyphs& glyphs) {
if (!maxWidth || writingMode != WritingModeType::Horizontal) {
return {};
}
@@ -182,7 +182,7 @@ std::set<std::size_t> determineLineBreaks(const std::u16string& logicalInput,
const char16_t codePoint = logicalInput[i];
auto it = glyphs.find(codePoint);
if (it != glyphs.end() && it->second && !boost::algorithm::is_any_of(u" \t\n\v\f\r")(codePoint)) {
- currentX += it->second->metrics.advance + spacing;
+ currentX += (*it->second)->metrics.advance + spacing;
}
// Ideographic characters, spaces, and word-breaking punctuation that often appear without
@@ -208,7 +208,7 @@ void shapeLines(Shaping& shaping,
const Point<float>& translate,
const float verticalHeight,
const WritingModeType writingMode,
- const GlyphPositions& glyphs) {
+ const Glyphs& glyphs) {
// the y offset *should* be part of the font metadata
const int32_t yOffset = -17;
@@ -234,7 +234,7 @@ void shapeLines(Shaping& shaping,
continue;
}
- const Glyph& glyph = *it->second;
+ const Glyph& glyph = **it->second;
if (writingMode == WritingModeType::Horizontal || !util::i18n::hasUprightVerticalOrientation(chr)) {
shaping.positionedGlyphs.emplace_back(chr, x, y, 0);
@@ -280,7 +280,7 @@ const Shaping getShaping(const std::u16string& logicalInput,
const float verticalHeight,
const WritingModeType writingMode,
BiDi& bidi,
- const GlyphPositions& glyphs) {
+ const Glyphs& glyphs) {
Shaping shaping(translate.x, translate.y, writingMode);
std::vector<std::u16string> reorderedLines =
diff --git a/src/mbgl/text/shaping.hpp b/src/mbgl/text/shaping.hpp
index bf81b9213c..1984b14e1d 100644
--- a/src/mbgl/text/shaping.hpp
+++ b/src/mbgl/text/shaping.hpp
@@ -2,7 +2,6 @@
#include <mbgl/text/glyph.hpp>
#include <mbgl/sprite/sprite_atlas.hpp>
-#include <mbgl/style/image.hpp>
namespace mbgl {
@@ -54,6 +53,6 @@ const Shaping getShaping(const std::u16string& string,
float verticalHeight,
const WritingModeType,
BiDi& bidi,
- const GlyphPositions& glyphs);
+ const Glyphs& glyphs);
} // namespace mbgl