summaryrefslogtreecommitdiff
path: root/src/mbgl/text
diff options
context:
space:
mode:
Diffstat (limited to 'src/mbgl/text')
-rw-r--r--src/mbgl/text/collision_feature.cpp2
-rw-r--r--src/mbgl/text/collision_feature.hpp2
-rw-r--r--src/mbgl/text/collision_tile.hpp9
-rw-r--r--src/mbgl/text/get_anchors.cpp2
-rw-r--r--src/mbgl/text/glyph.hpp39
-rw-r--r--src/mbgl/text/glyph_atlas.cpp285
-rw-r--r--src/mbgl/text/glyph_atlas.hpp107
-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/glyph_range.hpp4
-rw-r--r--src/mbgl/text/quads.cpp36
-rw-r--r--src/mbgl/text/quads.hpp6
-rw-r--r--src/mbgl/text/shaping.cpp34
-rw-r--r--src/mbgl/text/shaping.hpp17
17 files changed, 360 insertions, 428 deletions
diff --git a/src/mbgl/text/collision_feature.cpp b/src/mbgl/text/collision_feature.cpp
index 885ba5c426..71d7cc74e0 100644
--- a/src/mbgl/text/collision_feature.cpp
+++ b/src/mbgl/text/collision_feature.cpp
@@ -71,7 +71,7 @@ void CollisionFeature::bboxifyLabel(const GeometryCoordinates& line, GeometryCoo
p = line[index];
} while (anchorDistance > -labelLength / 2);
- float segmentLength = util::dist<float>(line[index], line[index + 1]);
+ auto segmentLength = util::dist<float>(line[index], line[index + 1]);
for (unsigned int i = 0; i < nBoxes; i++) {
// the distance the box will be from the anchor
diff --git a/src/mbgl/text/collision_feature.hpp b/src/mbgl/text/collision_feature.hpp
index 006a47eb74..c94ec23513 100644
--- a/src/mbgl/text/collision_feature.hpp
+++ b/src/mbgl/text/collision_feature.hpp
@@ -34,7 +34,7 @@ public:
class CollisionFeature {
public:
enum class AlignmentType : bool {
- Straight = 0,
+ Straight = false,
Curved
};
diff --git a/src/mbgl/text/collision_tile.hpp b/src/mbgl/text/collision_tile.hpp
index ea4324edaf..bdacbb7437 100644
--- a/src/mbgl/text/collision_tile.hpp
+++ b/src/mbgl/text/collision_tile.hpp
@@ -17,6 +17,7 @@
#pragma GCC diagnostic ignored "-Wshorten-64-to-32"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
+#pragma GCC diagnostic ignored "-Wmisleading-indentation"
#include <boost/geometry.hpp>
#include <boost/geometry/geometries/point.hpp>
#include <boost/geometry/geometries/box.hpp>
@@ -28,10 +29,10 @@ namespace mbgl {
namespace bg = boost::geometry;
namespace bgm = bg::model;
namespace bgi = bg::index;
-typedef bgm::point<float, 2, bg::cs::cartesian> CollisionPoint;
-typedef bgm::box<CollisionPoint> Box;
-typedef std::tuple<Box, CollisionBox, IndexedSubfeature> CollisionTreeBox;
-typedef bgi::rtree<CollisionTreeBox, bgi::linear<16, 4>> Tree;
+using CollisionPoint = bgm::point<float, 2, bg::cs::cartesian>;
+using Box = bgm::box<CollisionPoint>;
+using CollisionTreeBox = std::tuple<Box, CollisionBox, IndexedSubfeature>;
+using Tree = bgi::rtree<CollisionTreeBox, bgi::linear<16, 4>>;
class IndexedSubfeature;
diff --git a/src/mbgl/text/get_anchors.cpp b/src/mbgl/text/get_anchors.cpp
index 82702b20f0..d41faf2a71 100644
--- a/src/mbgl/text/get_anchors.cpp
+++ b/src/mbgl/text/get_anchors.cpp
@@ -34,7 +34,7 @@ static Anchors resample(const GeometryCoordinates& line,
const GeometryCoordinate& a = *(it);
const GeometryCoordinate& b = *(it + 1);
- const float segmentDist = util::dist<float>(a, b);
+ const auto segmentDist = util::dist<float>(a, b);
const float angle = util::angle_to(b, a);
while (markedDistance + spacing < distance + segmentDist) {
diff --git a/src/mbgl/text/glyph.hpp b/src/mbgl/text/glyph.hpp
index 9cf39de840..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>
@@ -13,8 +15,8 @@
namespace mbgl {
-typedef char16_t GlyphID;
-typedef std::set<GlyphID> GlyphIDs;
+using GlyphID = char16_t;
+using GlyphIDs = std::set<GlyphID>;
// Note: this only works for the BMP
GlyphRange getGlyphRange(GlyphID glyph);
@@ -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;
};
-typedef std::map<GlyphID, optional<Glyph>> GlyphPositions;
-typedef std::map<FontStack, GlyphPositions> GlyphPositionMap;
+using Glyphs = std::map<GlyphID, optional<Immutable<Glyph>>>;
+using GlyphMap = std::map<FontStack, Glyphs>;
class PositionedGlyph {
public:
@@ -58,14 +70,14 @@ enum class WritingModeType : uint8_t;
class Shaping {
public:
- explicit Shaping() : top(0), bottom(0), left(0), right(0) {}
+ explicit Shaping() = default;
explicit Shaping(float x, float y, WritingModeType writingMode_)
: top(y), bottom(y), left(x), right(x), writingMode(writingMode_) {}
std::vector<PositionedGlyph> positionedGlyphs;
- int32_t top;
- int32_t bottom;
- int32_t left;
- int32_t right;
+ int32_t top = 0;
+ int32_t bottom = 0;
+ int32_t left = 0;
+ int32_t right = 0;
WritingModeType writingMode;
explicit operator bool() const { return !positionedGlyphs.empty(); }
@@ -97,8 +109,7 @@ constexpr WritingModeType operator~(WritingModeType value) {
return WritingModeType(~mbgl::underlying_type(value));
}
-typedef std::map<FontStack,GlyphIDs> GlyphDependencies;
-typedef std::map<FontStack,GlyphRangeSet> GlyphRangeDependencies;
-
+using GlyphDependencies = std::map<FontStack,GlyphIDs>;
+using GlyphRangeDependencies = std::map<FontStack,GlyphRangeSet>;
} // end namespace mbgl
diff --git a/src/mbgl/text/glyph_atlas.cpp b/src/mbgl/text/glyph_atlas.cpp
index 4feaab01f9..1b98ea36bf 100644
--- a/src/mbgl/text/glyph_atlas.cpp
+++ b/src/mbgl/text/glyph_atlas.cpp
@@ -1,262 +1,65 @@
#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;
-
-void GlyphAtlas::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()) {
- 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;
- }
-
- request.req = fileSource.request(Resource::glyphs(glyphURL, fontStack, range), [this, fontStack, range](Response res) {
- processResponse(res, fontStack, range);
- });
+GlyphAtlas makeGlyphAtlas(const GlyphMap& glyphs) {
+ GlyphAtlas result;
- return request;
-}
+ mapbox::ShelfPack::ShelfPackOptions options;
+ options.autoResize = true;
+ mapbox::ShelfPack pack(0, 0, options);
-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;
- }
+ for (const auto& glyphMapEntry : glyphs) {
+ const FontStack& fontStack = glyphMapEntry.first;
+ GlyphPositionMap& positions = result.positions[fontStack];
- if (res.notModified) {
- return;
- }
+ for (const auto& entry : glyphMapEntry.second) {
+ if (entry.second && (*entry.second)->bitmap.valid()) {
+ const Glyph& glyph = **entry.second;
- Entry& entry = entries[fontStack];
- GlyphRequest& request = entry.ranges[range];
+ const mapbox::Bin& bin = *pack.packOne(-1,
+ glyph.bitmap.size.width + 2 * padding,
+ glyph.bitmap.size.height + 2 * padding);
- 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),
- {}, {}
+ result.image.resize({
+ static_cast<uint32_t>(pack.width()),
+ static_cast<uint32_t>(pack.height())
});
- } 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;
- }
- }
- }
-
- 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;
- uint16_t width = value.bitmap.size.width + 2 * padding;
- uint16_t height = value.bitmap.size.height + 2 * padding;
-
- // Increase to next number divisible by 4, but at least 1.
- // This is so we can scale down the texture coordinates and pack them
- // into 2 bytes rather than 4 bytes.
- width += (4 - width % 4);
- height += (4 - height % 4);
-
- 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.begin(); it != values.end(); it++) {
- 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;
- }
+ AlphaImage::copy(glyph.bitmap,
+ result.image,
+ { 0, 0 },
+ {
+ bin.x + padding,
+ bin.y + padding
+ },
+ glyph.bitmap.size);
+
+ 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
+ });
}
-
- bin.release(rect);
- value.rect = {};
}
}
-}
-
-void GlyphAtlas::removePendingRanges(mbgl::GlyphRequestor& requestor, std::map<GlyphRange, GlyphRequest>& ranges) {
- for (auto it = ranges.begin(); it != ranges.end(); it++) {
- it->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;
-}
+ pack.shrink();
+ result.image.resize({
+ static_cast<uint32_t>(pack.width()),
+ static_cast<uint32_t>(pack.height())
+ });
-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 ad9cf35adc..bb9115e4b4 100644
--- a/src/mbgl/text/glyph_atlas.hpp
+++ b/src/mbgl/text/glyph_atlas.hpp
@@ -1,110 +1,25 @@
#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/work_queue.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 GlyphPositionMap = std::map<GlyphID, GlyphPosition>;
+using GlyphPositions = std::map<FontStack, GlyphPositionMap>;
- 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 GlyphMap&);
+
} // 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/glyph_range.hpp b/src/mbgl/text/glyph_range.hpp
index dd39e092b7..74afb73dfc 100644
--- a/src/mbgl/text/glyph_range.hpp
+++ b/src/mbgl/text/glyph_range.hpp
@@ -7,7 +7,7 @@
namespace mbgl {
-typedef std::pair<uint16_t, uint16_t> GlyphRange;
+using GlyphRange = std::pair<uint16_t, uint16_t>;
struct GlyphRangeHash {
std::size_t operator()(const GlyphRange& glyphRange) const {
@@ -15,7 +15,7 @@ struct GlyphRangeHash {
}
};
-typedef std::unordered_set<GlyphRange, GlyphRangeHash> GlyphRangeSet;
+using GlyphRangeSet = std::unordered_set<GlyphRange, GlyphRangeHash>;
constexpr uint32_t GLYPHS_PER_GLYPH_RANGE = 256;
constexpr uint32_t GLYPH_RANGES_PER_FONT_STACK = 256;
diff --git a/src/mbgl/text/quads.cpp b/src/mbgl/text/quads.cpp
index e1a9699835..ab10c5a6b7 100644
--- a/src/mbgl/text/quads.cpp
+++ b/src/mbgl/text/quads.cpp
@@ -22,13 +22,17 @@ SymbolQuad getIconQuad(const Anchor& anchor,
const float layoutTextSize,
const style::SymbolPlacementType placement,
const Shaping& shapedText) {
- auto image = *shapedIcon.image();
+ const ImagePosition& image = shapedIcon.image();
+ // If you have a 10px icon that isn't perfectly aligned to the pixel grid it will cover 11 actual
+ // pixels. The quad needs to be padded to account for this, otherwise they'll look slightly clipped
+ // on one edge in some cases.
const float border = 1.0;
- auto left = shapedIcon.left() - border;
- auto right = left + image.pos.w / image.relativePixelRatio;
- auto top = shapedIcon.top() - border;
- auto bottom = top + image.pos.h / image.relativePixelRatio;
+
+ float top = shapedIcon.top() - border / image.pixelRatio;
+ float left = shapedIcon.left() - border / image.pixelRatio;
+ float bottom = shapedIcon.bottom() + border / image.pixelRatio;
+ float right = shapedIcon.right() + border / image.pixelRatio;
Point<float> tl;
Point<float> tr;
Point<float> br;
@@ -92,7 +96,15 @@ SymbolQuad getIconQuad(const Anchor& anchor,
br = util::matrixMultiply(matrix, br);
}
- return SymbolQuad { tl, tr, bl, br, image.pos, 0, 0, anchor.point, globalMinScale, std::numeric_limits<float>::infinity(), shapedText.writingMode };
+ // Icon quad is padded, so texture coordinates also need to be padded.
+ Rect<uint16_t> textureRect {
+ static_cast<uint16_t>(image.textureRect.x - border),
+ static_cast<uint16_t>(image.textureRect.y - border),
+ static_cast<uint16_t>(image.textureRect.w + border * 2),
+ static_cast<uint16_t>(image.textureRect.h + border * 2)
+ };
+
+ return SymbolQuad { tl, tr, bl, br, textureRect, 0, 0, anchor.point, globalMinScale, std::numeric_limits<float>::infinity(), shapedText.writingMode };
}
struct GlyphInstance {
@@ -108,7 +120,7 @@ struct GlyphInstance {
const float angle = 0.0f;
};
-typedef std::vector<GlyphInstance> GlyphInstances;
+using GlyphInstances = std::vector<GlyphInstance>;
struct VirtualSegment {
Point<float> anchor;
@@ -177,7 +189,7 @@ inline Point<float> getVirtualSegmentAnchor(const Point<float>& segmentBegin, co
inline float getMinScaleForSegment(const float glyphDistanceFromAnchor,
const Point<float>& segmentAnchor,
const Point<float>& segmentEnd) {
- const float distanceFromAnchorToEnd = util::dist<float>(segmentAnchor, segmentEnd);
+ const auto distanceFromAnchorToEnd = util::dist<float>(segmentAnchor, segmentEnd);
return glyphDistanceFromAnchor / distanceFromAnchorToEnd;
}
@@ -295,18 +307,18 @@ SymbolQuads getGlyphQuads(Anchor& anchor,
const GeometryCoordinates& line,
const SymbolLayoutProperties::Evaluated& layout,
const style::SymbolPlacementType placement,
- const GlyphPositions& face) {
+ const GlyphPositionMap& positions) {
const float textRotate = layout.get<TextRotate>() * util::DEG2RAD;
const bool keepUpright = layout.get<TextKeepUpright>();
SymbolQuads quads;
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())
+ auto positionsIt = positions.find(positionedGlyph.glyph);
+ if (positionsIt == positions.end())
continue;
- const Glyph& glyph = *face_it->second;
+ const GlyphPosition& glyph = positionsIt->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 333000627b..b29f6b0ad3 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>
@@ -49,7 +49,7 @@ public:
WritingModeType writingMode;
};
-typedef std::vector<SymbolQuad> SymbolQuads;
+using SymbolQuads = std::vector<SymbolQuad>;
SymbolQuad getIconQuad(const Anchor& anchor,
const PositionedIcon& shapedIcon,
@@ -65,6 +65,6 @@ SymbolQuads getGlyphQuads(Anchor& anchor,
const GeometryCoordinates& line,
const style::SymbolLayoutProperties::Evaluated&,
style::SymbolPlacementType placement,
- const GlyphPositions& face);
+ const GlyphPositionMap& positions);
} // namespace mbgl
diff --git a/src/mbgl/text/shaping.cpp b/src/mbgl/text/shaping.cpp
index 78aa142c61..338abe2e43 100644
--- a/src/mbgl/text/shaping.cpp
+++ b/src/mbgl/text/shaping.cpp
@@ -10,19 +10,15 @@
namespace mbgl {
-optional<PositionedIcon> PositionedIcon::shapeIcon(const SpriteAtlasElement& image, const std::array<float, 2>& iconOffset, const float iconRotation) {
- if (!image.pos.hasArea()) {
- return {};
- }
-
+PositionedIcon PositionedIcon::shapeIcon(const ImagePosition& image, const std::array<float, 2>& iconOffset, const float iconRotation) {
float dx = iconOffset[0];
float dy = iconOffset[1];
- float x1 = dx - image.width/ 2.0f;
- float x2 = x1 + image.width;
- float y1 = dy - image.height / 2.0f;
- float y2 = y1 + image.height;
+ float x1 = dx - image.displaySize()[0] / 2.0f;
+ float x2 = x1 + image.displaySize()[0];
+ float y1 = dy - image.displaySize()[1] / 2.0f;
+ float y2 = y1 + image.displaySize()[1];
- return { PositionedIcon { image, y1, y2, x1, x2, iconRotation } };
+ return PositionedIcon { image, y1, y2, x1, x2, iconRotation };
}
void align(Shaping& shaping,
@@ -46,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) {
@@ -57,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++) {
@@ -69,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;
}
}
@@ -168,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 {};
}
@@ -186,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
@@ -212,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;
@@ -238,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);
@@ -284,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 b7eee5a5db..ca475e2a6c 100644
--- a/src/mbgl/text/shaping.hpp
+++ b/src/mbgl/text/shaping.hpp
@@ -1,32 +1,29 @@
#pragma once
#include <mbgl/text/glyph.hpp>
-#include <mbgl/sprite/sprite_atlas.hpp>
-#include <mbgl/style/image.hpp>
-#include <mbgl/util/optional.hpp>
+#include <mbgl/renderer/image_atlas.hpp>
namespace mbgl {
-class SpriteAtlasElement;
class SymbolFeature;
class BiDi;
class PositionedIcon {
private:
- PositionedIcon(const SpriteAtlasElement& image_,
+ PositionedIcon(ImagePosition image_,
float top_,
float bottom_,
float left_,
float right_,
float angle_)
- : _image(image_),
+ : _image(std::move(image_)),
_top(top_),
_bottom(bottom_),
_left(left_),
_right(right_),
_angle(angle_) {}
- optional<SpriteAtlasElement> _image;
+ ImagePosition _image;
float _top;
float _bottom;
float _left;
@@ -34,9 +31,9 @@ private:
float _angle;
public:
- static optional<PositionedIcon> shapeIcon(const class SpriteAtlasElement&, const std::array<float, 2>& iconOffset, const float iconRotation);
+ static PositionedIcon shapeIcon(const ImagePosition&, const std::array<float, 2>& iconOffset, const float iconRotation);
- optional<class SpriteAtlasElement> image() const { return _image; }
+ const ImagePosition& image() const { return _image; }
float top() const { return _top; }
float bottom() const { return _bottom; }
float left() const { return _left; }
@@ -55,6 +52,6 @@ const Shaping getShaping(const std::u16string& string,
float verticalHeight,
const WritingModeType,
BiDi& bidi,
- const GlyphPositions& glyphs);
+ const Glyphs& glyphs);
} // namespace mbgl