summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmake/core-files.cmake4
-rw-r--r--cmake/test-files.cmake2
-rw-r--r--src/mbgl/annotation/annotation_manager.cpp23
-rw-r--r--src/mbgl/annotation/annotation_manager.hpp3
-rw-r--r--src/mbgl/layout/symbol_layout.cpp5
-rw-r--r--src/mbgl/layout/symbol_layout.hpp3
-rw-r--r--src/mbgl/sprite/sprite_atlas.cpp164
-rw-r--r--src/mbgl/sprite/sprite_atlas.hpp55
-rw-r--r--src/mbgl/sprite/sprite_atlas_observer.hpp (renamed from src/mbgl/sprite/sprite_store_observer.hpp)4
-rw-r--r--src/mbgl/sprite/sprite_store.cpp158
-rw-r--r--src/mbgl/sprite/sprite_store.hpp70
-rw-r--r--src/mbgl/style/bucket_parameters.hpp4
-rw-r--r--src/mbgl/style/observer.hpp4
-rw-r--r--src/mbgl/style/style.cpp14
-rw-r--r--src/mbgl/style/style.hpp6
-rw-r--r--src/mbgl/tile/geometry_tile.cpp1
-rw-r--r--src/mbgl/tile/geometry_tile_worker.cpp6
-rw-r--r--src/mbgl/tile/geometry_tile_worker.hpp3
-rw-r--r--test/sprite/sprite_atlas.cpp292
-rw-r--r--test/sprite/sprite_store.cpp305
-rw-r--r--test/style/variant.cpp0
21 files changed, 507 insertions, 619 deletions
diff --git a/cmake/core-files.cmake b/cmake/core-files.cmake
index 11300e444d..4e1773eaac 100644
--- a/cmake/core-files.cmake
+++ b/cmake/core-files.cmake
@@ -210,12 +210,10 @@ set(MBGL_CORE_FILES
include/mbgl/sprite/sprite_image.hpp
src/mbgl/sprite/sprite_atlas.cpp
src/mbgl/sprite/sprite_atlas.hpp
+ src/mbgl/sprite/sprite_atlas_observer.hpp
src/mbgl/sprite/sprite_image.cpp
src/mbgl/sprite/sprite_parser.cpp
src/mbgl/sprite/sprite_parser.hpp
- src/mbgl/sprite/sprite_store.cpp
- src/mbgl/sprite/sprite_store.hpp
- src/mbgl/sprite/sprite_store_observer.hpp
# storage
include/mbgl/storage/default_file_source.hpp
diff --git a/cmake/test-files.cmake b/cmake/test-files.cmake
index 84f9bcca63..23dc0b82ce 100644
--- a/cmake/test-files.cmake
+++ b/cmake/test-files.cmake
@@ -40,7 +40,6 @@ set(MBGL_TEST_FILES
test/sprite/sprite_atlas.cpp
test/sprite/sprite_image.cpp
test/sprite/sprite_parser.cpp
- test/sprite/sprite_store.cpp
# src
test/src/main.cpp
@@ -81,7 +80,6 @@ set(MBGL_TEST_FILES
test/style/style_layer.cpp
test/style/style_parser.cpp
test/style/tile_source.cpp
- test/style/variant.cpp
# text
test/text/quads.cpp
diff --git a/src/mbgl/annotation/annotation_manager.cpp b/src/mbgl/annotation/annotation_manager.cpp
index fac494f223..28a97d292e 100644
--- a/src/mbgl/annotation/annotation_manager.cpp
+++ b/src/mbgl/annotation/annotation_manager.cpp
@@ -8,6 +8,7 @@
#include <mbgl/style/style.hpp>
#include <mbgl/style/layers/symbol_layer.hpp>
#include <mbgl/style/layers/symbol_layer_impl.hpp>
+#include <mbgl/storage/file_source.hpp>
#include <boost/function_output_iterator.hpp>
@@ -19,8 +20,20 @@ const std::string AnnotationManager::SourceID = "com.mapbox.annotations";
const std::string AnnotationManager::PointLayerID = "com.mapbox.annotations.points";
AnnotationManager::AnnotationManager(float pixelRatio)
- : spriteStore(pixelRatio),
- spriteAtlas(1024, 1024, pixelRatio, spriteStore) {
+ : spriteAtlas(1024, 1024, pixelRatio) {
+
+ struct NullFileSource : public FileSource {
+ std::unique_ptr<AsyncRequest> request(const Resource&, Callback) override {
+ assert(false);
+ return nullptr;
+ }
+ };
+
+ NullFileSource nullFileSource;
+
+ // This is a special atlas, holding only images added via addIcon. But we need its isLoaded()
+ // method to return true.
+ spriteAtlas.load("", nullFileSource);
}
AnnotationManager::~AnnotationManager() = default;
@@ -187,17 +200,17 @@ void AnnotationManager::removeTile(AnnotationTile& tile) {
}
void AnnotationManager::addIcon(const std::string& name, std::shared_ptr<const SpriteImage> sprite) {
- spriteStore.setSprite(name, sprite);
+ spriteAtlas.setSprite(name, sprite);
spriteAtlas.updateDirty();
}
void AnnotationManager::removeIcon(const std::string& name) {
- spriteStore.removeSprite(name);
+ spriteAtlas.removeSprite(name);
spriteAtlas.updateDirty();
}
double AnnotationManager::getTopOffsetPixelsForIcon(const std::string& name) {
- auto sprite = spriteStore.getSprite(name);
+ auto sprite = spriteAtlas.getSprite(name);
return sprite ? -(sprite->image.height / sprite->pixelRatio) / 2 : 0;
}
diff --git a/src/mbgl/annotation/annotation_manager.hpp b/src/mbgl/annotation/annotation_manager.hpp
index a9afa0d52d..b4959964f6 100644
--- a/src/mbgl/annotation/annotation_manager.hpp
+++ b/src/mbgl/annotation/annotation_manager.hpp
@@ -2,7 +2,6 @@
#include <mbgl/annotation/annotation.hpp>
#include <mbgl/annotation/symbol_annotation_impl.hpp>
-#include <mbgl/sprite/sprite_store.hpp>
#include <mbgl/sprite/sprite_atlas.hpp>
#include <mbgl/map/update.hpp>
#include <mbgl/util/noncopyable.hpp>
@@ -75,8 +74,6 @@ private:
ShapeAnnotationMap shapeAnnotations;
std::set<std::string> obsoleteShapeAnnotationLayers;
std::set<AnnotationTile*> tiles;
-
- SpriteStore spriteStore;
SpriteAtlas spriteAtlas;
};
diff --git a/src/mbgl/layout/symbol_layout.cpp b/src/mbgl/layout/symbol_layout.cpp
index 4c448d1804..277376a4e3 100644
--- a/src/mbgl/layout/symbol_layout.cpp
+++ b/src/mbgl/layout/symbol_layout.cpp
@@ -3,7 +3,6 @@
#include <mbgl/layout/clip_lines.hpp>
#include <mbgl/renderer/symbol_bucket.hpp>
#include <mbgl/style/filter_evaluator.hpp>
-#include <mbgl/sprite/sprite_store.hpp>
#include <mbgl/sprite/sprite_atlas.hpp>
#include <mbgl/geometry/glyph_atlas.hpp>
#include <mbgl/text/get_anchors.hpp>
@@ -125,12 +124,12 @@ bool SymbolLayout::hasSymbolInstances() const {
return !symbolInstances.empty();
}
-bool SymbolLayout::canPrepare(GlyphStore& glyphStore, SpriteStore& spriteStore) {
+bool SymbolLayout::canPrepare(GlyphStore& glyphStore) {
if (!layout.textField.value.empty() && !layout.textFont.value.empty() && !glyphStore.hasGlyphRanges(layout.textFont, ranges)) {
return false;
}
- if (!layout.iconImage.value.empty() && !spriteStore.isLoaded()) {
+ if (!layout.iconImage.value.empty() && !spriteAtlas.isLoaded()) {
return false;
}
diff --git a/src/mbgl/layout/symbol_layout.hpp b/src/mbgl/layout/symbol_layout.hpp
index e365e7b443..000dacaf3a 100644
--- a/src/mbgl/layout/symbol_layout.hpp
+++ b/src/mbgl/layout/symbol_layout.hpp
@@ -15,7 +15,6 @@ namespace mbgl {
class GeometryTileLayer;
class CollisionTile;
class SpriteAtlas;
-class SpriteStore;
class GlyphAtlas;
class GlyphStore;
class SymbolBucket;
@@ -39,7 +38,7 @@ public:
float textMaxSize,
SpriteAtlas&);
- bool canPrepare(GlyphStore&, SpriteStore&);
+ bool canPrepare(GlyphStore&);
void prepare(uintptr_t tileUID,
GlyphAtlas&,
diff --git a/src/mbgl/sprite/sprite_atlas.cpp b/src/mbgl/sprite/sprite_atlas.cpp
index d346464b51..78d5fe638a 100644
--- a/src/mbgl/sprite/sprite_atlas.cpp
+++ b/src/mbgl/sprite/sprite_atlas.cpp
@@ -1,5 +1,6 @@
#include <mbgl/sprite/sprite_atlas.hpp>
-#include <mbgl/sprite/sprite_store.hpp>
+#include <mbgl/sprite/sprite_atlas_observer.hpp>
+#include <mbgl/sprite/sprite_parser.hpp>
#include <mbgl/gl/gl.hpp>
#include <mbgl/gl/gl_config.hpp>
#include <mbgl/platform/log.hpp>
@@ -7,6 +8,10 @@
#include <mbgl/util/math.hpp>
#include <mbgl/util/std.hpp>
#include <mbgl/util/constants.hpp>
+#include <mbgl/util/exception.hpp>
+#include <mbgl/storage/file_source.hpp>
+#include <mbgl/storage/resource.hpp>
+#include <mbgl/storage/response.hpp>
#include <cassert>
#include <cmath>
@@ -14,15 +19,147 @@
namespace mbgl {
-SpriteAtlas::SpriteAtlas(dimension width_, dimension height_, float pixelRatio_, SpriteStore& store_)
+static SpriteAtlasObserver nullObserver;
+
+struct SpriteAtlas::Loader {
+ std::shared_ptr<const std::string> image;
+ std::shared_ptr<const std::string> json;
+ std::unique_ptr<AsyncRequest> jsonRequest;
+ std::unique_ptr<AsyncRequest> spriteRequest;
+};
+
+SpriteAtlas::SpriteAtlas(dimension width_, dimension height_, float pixelRatio_)
: width(width_),
height(height_),
pixelWidth(std::ceil(width * pixelRatio_)),
pixelHeight(std::ceil(height * pixelRatio_)),
pixelRatio(pixelRatio_),
- store(store_),
+ observer(&nullObserver),
bin(width_, height_),
- dirty(true) {
+ dirtyFlag(true) {
+}
+
+SpriteAtlas::~SpriteAtlas() = default;
+
+void SpriteAtlas::load(const std::string& url, FileSource& fileSource) {
+ if (url.empty()) {
+ // Treat a non-existent sprite as a successfully loaded empty sprite.
+ loaded = true;
+ return;
+ }
+
+ loader = std::make_unique<Loader>();
+
+ loader->jsonRequest = fileSource.request(Resource::spriteJSON(url, pixelRatio), [this](Response res) {
+ if (res.error) {
+ observer->onSpriteError(std::make_exception_ptr(std::runtime_error(res.error->message)));
+ } else if (res.notModified) {
+ return;
+ } else if (res.noContent) {
+ loader->json = std::make_shared<const std::string>();
+ emitSpriteLoadedIfComplete();
+ } else {
+ // Only trigger a sprite loaded event we got new data.
+ loader->json = res.data;
+ emitSpriteLoadedIfComplete();
+ }
+ });
+
+ loader->spriteRequest = fileSource.request(Resource::spriteImage(url, pixelRatio), [this](Response res) {
+ if (res.error) {
+ observer->onSpriteError(std::make_exception_ptr(std::runtime_error(res.error->message)));
+ } else if (res.notModified) {
+ return;
+ } else if (res.noContent) {
+ loader->image = std::make_shared<const std::string>();
+ emitSpriteLoadedIfComplete();
+ } else {
+ loader->image = res.data;
+ emitSpriteLoadedIfComplete();
+ }
+ });
+}
+
+void SpriteAtlas::emitSpriteLoadedIfComplete() {
+ assert(loader);
+
+ if (!loader->image || !loader->json) {
+ return;
+ }
+
+ auto result = parseSprite(*loader->image, *loader->json);
+ if (result.is<Sprites>()) {
+ loaded = true;
+ setSprites(result.get<Sprites>());
+ observer->onSpriteLoaded();
+ } else {
+ observer->onSpriteError(result.get<std::exception_ptr>());
+ }
+}
+
+void SpriteAtlas::setObserver(SpriteAtlasObserver* observer_) {
+ observer = observer_;
+}
+
+void SpriteAtlas::dumpDebugLogs() const {
+ Log::Info(Event::General, "SpriteAtlas::loaded: %d", loaded);
+}
+
+void SpriteAtlas::setSprites(const Sprites& newSprites) {
+ std::lock_guard<std::mutex> lock(mutex);
+ for (const auto& pair : newSprites) {
+ _setSprite(pair.first, pair.second);
+ }
+}
+
+void SpriteAtlas::setSprite(const std::string& name, std::shared_ptr<const SpriteImage> sprite) {
+ std::lock_guard<std::mutex> lock(mutex);
+ _setSprite(name, sprite);
+}
+
+void SpriteAtlas::removeSprite(const std::string& name) {
+ std::lock_guard<std::mutex> lock(mutex);
+ _setSprite(name);
+}
+
+void SpriteAtlas::_setSprite(const std::string& name,
+ const std::shared_ptr<const SpriteImage>& sprite) {
+ if (sprite) {
+ auto it = sprites.find(name);
+ if (it != sprites.end()) {
+ // There is already a sprite with that name in our store.
+ if ((it->second->image.width != sprite->image.width || it->second->image.height != sprite->image.height)) {
+ Log::Warning(Event::Sprite, "Can't change sprite dimensions for '%s'", name.c_str());
+ return;
+ }
+ it->second = sprite;
+ } else {
+ sprites.emplace(name, sprite);
+ }
+
+ // Always add/replace the value in the dirty list.
+ auto dirty_it = dirtySprites.find(name);
+ if (dirty_it != dirtySprites.end()) {
+ dirty_it->second = sprite;
+ } else {
+ dirtySprites.emplace(name, sprite);
+ }
+ } else if (sprites.erase(name) > 0) {
+ dirtySprites.emplace(name, nullptr);
+ }
+}
+
+std::shared_ptr<const SpriteImage> SpriteAtlas::getSprite(const std::string& name) {
+ std::lock_guard<std::mutex> lock(mutex);
+ const auto it = sprites.find(name);
+ if (it != sprites.end()) {
+ return it->second;
+ } else {
+ if (!sprites.empty()) {
+ Log::Info(Event::Sprite, "Can't find sprite named '%s'", name.c_str());
+ }
+ return nullptr;
+ }
}
Rect<SpriteAtlas::dimension> SpriteAtlas::allocateImage(const SpriteImage& spriteImage) {
@@ -55,7 +192,7 @@ optional<SpriteAtlasElement> SpriteAtlas::getImage(const std::string& name,
return SpriteAtlasElement { rect_it->second.pos, rect_it->second.spriteImage, rect_it->second.spriteImage->pixelRatio / pixelRatio };
}
- auto sprite = store.getSprite(name);
+ auto sprite = getSprite(name);
if (!sprite) {
return {};
}
@@ -142,21 +279,16 @@ void SpriteAtlas::copy(const Holder& holder, const SpritePatternMode mode) {
dstData, pixelWidth, (holder.pos.x + padding) * pixelRatio, (holder.pos.y + padding) * pixelRatio, pixelWidth * pixelHeight,
uint32_t(holder.spriteImage->image.width), uint32_t(holder.spriteImage->image.height), mode);
- dirty = true;
+ dirtyFlag = true;
}
void SpriteAtlas::upload(gl::ObjectStore& objectStore, gl::Config& config, uint32_t unit) {
- if (dirty) {
+ if (dirtyFlag) {
bind(false, objectStore, config, unit);
}
}
void SpriteAtlas::updateDirty() {
- auto dirtySprites = store.getDirty();
- if (dirtySprites.empty()) {
- return;
- }
-
std::lock_guard<std::recursive_mutex> lock(mtx);
auto imageIterator = images.begin();
@@ -180,6 +312,8 @@ void SpriteAtlas::updateDirty() {
// name, but a different wrap value.
}
}
+
+ dirtySprites.clear();
}
void SpriteAtlas::bind(bool linear, gl::ObjectStore& objectStore, gl::Config& config, uint32_t unit) {
@@ -212,7 +346,7 @@ void SpriteAtlas::bind(bool linear, gl::ObjectStore& objectStore, gl::Config& co
filter = filter_val;
}
- if (dirty) {
+ if (dirtyFlag) {
std::lock_guard<std::recursive_mutex> lock(mtx);
config.activeTexture = unit;
@@ -243,7 +377,7 @@ void SpriteAtlas::bind(bool linear, gl::ObjectStore& objectStore, gl::Config& co
));
}
- dirty = false;
+ dirtyFlag = false;
#ifndef GL_ES_VERSION_2_0
// platform::showColorDebugImage("Sprite Atlas", reinterpret_cast<const char*>(data.get()),
@@ -252,8 +386,6 @@ void SpriteAtlas::bind(bool linear, gl::ObjectStore& objectStore, gl::Config& co
}
}
-SpriteAtlas::~SpriteAtlas() = default;
-
SpriteAtlas::Holder::Holder(std::shared_ptr<const SpriteImage> spriteImage_, Rect<dimension> pos_)
: spriteImage(std::move(spriteImage_)), pos(std::move(pos_)) {
}
diff --git a/src/mbgl/sprite/sprite_atlas.hpp b/src/mbgl/sprite/sprite_atlas.hpp
index d7901244cb..d23d3a0fd1 100644
--- a/src/mbgl/sprite/sprite_atlas.hpp
+++ b/src/mbgl/sprite/sprite_atlas.hpp
@@ -5,6 +5,7 @@
#include <mbgl/gl/object_store.hpp>
#include <mbgl/util/noncopyable.hpp>
#include <mbgl/util/optional.hpp>
+#include <mbgl/sprite/sprite_image.hpp>
#include <atomic>
#include <string>
@@ -12,14 +13,17 @@
#include <mutex>
#include <set>
#include <array>
+#include <memory>
namespace mbgl {
+class FileSource;
+class SpriteAtlasObserver;
+
namespace gl {
class Config;
} // namespace gl
-class SpriteStore;
class SpriteImage;
class SpritePosition;
@@ -43,10 +47,33 @@ enum class SpritePatternMode : bool {
class SpriteAtlas : public util::noncopyable {
public:
typedef uint16_t dimension;
+ using Sprites = std::map<std::string, std::shared_ptr<const SpriteImage>>;
- SpriteAtlas(dimension width, dimension height, float pixelRatio, SpriteStore& store);
+ SpriteAtlas(dimension width, dimension height, float pixelRatio);
~SpriteAtlas();
+ void load(const std::string& url, FileSource&);
+
+ bool isLoaded() const {
+ return loaded;
+ }
+
+ void dumpDebugLogs() const;
+
+ void setObserver(SpriteAtlasObserver*);
+
+ // Adds/replaces a Sprite image.
+ void setSprite(const std::string&, std::shared_ptr<const SpriteImage>);
+
+ // Adds/replaces mutliple Sprite images.
+ void setSprites(const Sprites& sprites);
+
+ // Removes a Sprite.
+ void removeSprite(const std::string&);
+
+ // Obtains a Sprite image.
+ std::shared_ptr<const SpriteImage> getSprite(const std::string&);
+
// If the sprite is loaded, copies the requsted image from it into the atlas and returns
// the resulting icon measurements. If not, returns an empty optional.
optional<SpriteAtlasElement> getImage(const std::string& name, SpritePatternMode mode);
@@ -58,7 +85,7 @@ public:
// Binds the atlas texture to the GPU, and uploads data if it is out of date.
void bind(bool linear, gl::ObjectStore&, gl::Config&, uint32_t unit);
- // Updates sprites in the atlas texture that may have changed in the source SpriteStore object.
+ // Updates sprites in the atlas texture that may have changed.
void updateDirty();
// Uploads the texture to the GPU to be available when we need it. This is a lazy operation;
@@ -75,10 +102,29 @@ public:
const uint32_t* getData() const { return data.get(); }
private:
+ void _setSprite(const std::string&, const std::shared_ptr<const SpriteImage>& = nullptr);
+ void emitSpriteLoadedIfComplete();
+
const GLsizei width, height;
const dimension pixelWidth, pixelHeight;
const float pixelRatio;
+ struct Loader;
+ std::unique_ptr<Loader> loader;
+
+ bool loaded = false;
+
+ SpriteAtlasObserver* observer = nullptr;
+
+ // Lock for sprites and dirty maps.
+ std::mutex mutex;
+
+ // Stores all current sprites.
+ Sprites sprites;
+
+ // Stores all Sprite IDs that changed since the last invocation.
+ Sprites dirtySprites;
+
struct Holder : private util::noncopyable {
Holder(std::shared_ptr<const SpriteImage>, Rect<dimension>);
Holder(Holder&&);
@@ -92,12 +138,11 @@ private:
void copy(const Holder& holder, SpritePatternMode mode);
std::recursive_mutex mtx;
- SpriteStore& store;
BinPack<dimension> bin;
std::map<Key, Holder> images;
std::set<std::string> uninitialized;
std::unique_ptr<uint32_t[]> data;
- std::atomic<bool> dirty;
+ std::atomic<bool> dirtyFlag;
bool fullUploadRequired = true;
mbgl::optional<gl::UniqueTexture> texture;
uint32_t filter = 0;
diff --git a/src/mbgl/sprite/sprite_store_observer.hpp b/src/mbgl/sprite/sprite_atlas_observer.hpp
index 32ce040aa4..263c95b0e8 100644
--- a/src/mbgl/sprite/sprite_store_observer.hpp
+++ b/src/mbgl/sprite/sprite_atlas_observer.hpp
@@ -4,9 +4,9 @@
namespace mbgl {
-class SpriteStoreObserver {
+class SpriteAtlasObserver {
public:
- virtual ~SpriteStoreObserver() = default;
+ virtual ~SpriteAtlasObserver() = default;
virtual void onSpriteLoaded() {}
virtual void onSpriteError(std::exception_ptr) {}
diff --git a/src/mbgl/sprite/sprite_store.cpp b/src/mbgl/sprite/sprite_store.cpp
deleted file mode 100644
index b9249a3ffc..0000000000
--- a/src/mbgl/sprite/sprite_store.cpp
+++ /dev/null
@@ -1,158 +0,0 @@
-#include <mbgl/sprite/sprite_store.hpp>
-#include <mbgl/sprite/sprite_store_observer.hpp>
-#include <mbgl/sprite/sprite_parser.hpp>
-#include <mbgl/platform/log.hpp>
-#include <mbgl/storage/file_source.hpp>
-#include <mbgl/storage/resource.hpp>
-#include <mbgl/storage/response.hpp>
-#include <mbgl/util/exception.hpp>
-
-#include <cassert>
-#include <string>
-
-namespace mbgl {
-
-static SpriteStoreObserver nullObserver;
-
-struct SpriteStore::Loader {
- std::shared_ptr<const std::string> image;
- std::shared_ptr<const std::string> json;
- std::unique_ptr<AsyncRequest> jsonRequest;
- std::unique_ptr<AsyncRequest> spriteRequest;
-};
-
-SpriteStore::SpriteStore(float pixelRatio_)
- : pixelRatio(pixelRatio_ > 1 ? 2 : 1), observer(&nullObserver) {
-}
-
-SpriteStore::~SpriteStore() = default;
-
-void SpriteStore::load(const std::string& url, FileSource& fileSource) {
- if (url.empty()) {
- // Treat a non-existent sprite as a successfully loaded empty sprite.
- loaded = true;
- return;
- }
-
- loader = std::make_unique<Loader>();
-
- loader->jsonRequest = fileSource.request(Resource::spriteJSON(url, pixelRatio), [this](Response res) {
- if (res.error) {
- observer->onSpriteError(std::make_exception_ptr(std::runtime_error(res.error->message)));
- } else if (res.notModified) {
- return;
- } else if (res.noContent) {
- loader->json = std::make_shared<const std::string>();
- emitSpriteLoadedIfComplete();
- } else {
- // Only trigger a sprite loaded event we got new data.
- loader->json = res.data;
- emitSpriteLoadedIfComplete();
- }
- });
-
- loader->spriteRequest = fileSource.request(Resource::spriteImage(url, pixelRatio), [this](Response res) {
- if (res.error) {
- observer->onSpriteError(std::make_exception_ptr(std::runtime_error(res.error->message)));
- } else if (res.notModified) {
- return;
- } else if (res.noContent) {
- loader->image = std::make_shared<const std::string>();
- emitSpriteLoadedIfComplete();
- } else {
- loader->image = res.data;
- emitSpriteLoadedIfComplete();
- }
- });
-}
-
-void SpriteStore::emitSpriteLoadedIfComplete() {
- assert(loader);
-
- if (!loader->image || !loader->json) {
- return;
- }
-
- auto result = parseSprite(*loader->image, *loader->json);
- if (result.is<Sprites>()) {
- loaded = true;
- setSprites(result.get<Sprites>());
- observer->onSpriteLoaded();
- } else {
- observer->onSpriteError(result.get<std::exception_ptr>());
- }
-}
-
-void SpriteStore::setObserver(SpriteStoreObserver* observer_) {
- observer = observer_;
-}
-
-void SpriteStore::dumpDebugLogs() const {
- Log::Info(Event::General, "SpriteStore::loaded: %d", loaded);
-}
-
-void SpriteStore::setSprite(const std::string& name, std::shared_ptr<const SpriteImage> sprite) {
- std::lock_guard<std::mutex> lock(mutex);
- _setSprite(name, sprite);
-}
-
-void SpriteStore::_setSprite(const std::string& name,
- const std::shared_ptr<const SpriteImage>& sprite) {
- if (sprite) {
- auto it = sprites.find(name);
- if (it != sprites.end()) {
- // There is already a sprite with that name in our store.
- if ((it->second->image.width != sprite->image.width || it->second->image.height != sprite->image.height)) {
- Log::Warning(Event::Sprite, "Can't change sprite dimensions for '%s'", name.c_str());
- return;
- }
- it->second = sprite;
- } else {
- sprites.emplace(name, sprite);
- }
-
- // Always add/replace the value in the dirty list.
- auto dirty_it = dirty.find(name);
- if (dirty_it != dirty.end()) {
- dirty_it->second = sprite;
- } else {
- dirty.emplace(name, sprite);
- }
- } else if (sprites.erase(name) > 0) {
- dirty.emplace(name, nullptr);
- }
-}
-
-void SpriteStore::setSprites(const Sprites& newSprites) {
- std::lock_guard<std::mutex> lock(mutex);
- for (const auto& pair : newSprites) {
- _setSprite(pair.first, pair.second);
- }
-}
-
-void SpriteStore::removeSprite(const std::string& name) {
- std::lock_guard<std::mutex> lock(mutex);
- _setSprite(name);
-}
-
-std::shared_ptr<const SpriteImage> SpriteStore::getSprite(const std::string& name) {
- std::lock_guard<std::mutex> lock(mutex);
- const auto it = sprites.find(name);
- if (it != sprites.end()) {
- return it->second;
- } else {
- if (!sprites.empty()) {
- Log::Info(Event::Sprite, "Can't find sprite named '%s'", name.c_str());
- }
- return nullptr;
- }
-}
-
-SpriteStore::Sprites SpriteStore::getDirty() {
- Sprites result;
- std::lock_guard<std::mutex> lock(mutex);
- dirty.swap(result);
- return result;
-}
-
-} // namespace mbgl
diff --git a/src/mbgl/sprite/sprite_store.hpp b/src/mbgl/sprite/sprite_store.hpp
deleted file mode 100644
index c6f34d6c5e..0000000000
--- a/src/mbgl/sprite/sprite_store.hpp
+++ /dev/null
@@ -1,70 +0,0 @@
-#pragma once
-
-#include <mbgl/sprite/sprite_image.hpp>
-#include <mbgl/util/noncopyable.hpp>
-
-#include <map>
-#include <memory>
-#include <mutex>
-
-namespace mbgl {
-
-class FileSource;
-class SpriteStoreObserver;
-
-class SpriteStore : private util::noncopyable {
-public:
- using Sprites = std::map<std::string, std::shared_ptr<const SpriteImage>>;
-
- SpriteStore(float pixelRatio);
- ~SpriteStore();
-
- void load(const std::string& url, FileSource&);
-
- bool isLoaded() const {
- return loaded;
- }
-
- void dumpDebugLogs() const;
-
- void setObserver(SpriteStoreObserver* observer);
-
- // Adds/replaces a Sprite image.
- void setSprite(const std::string&, std::shared_ptr<const SpriteImage> = nullptr);
-
- // Adds/replaces mutliple Sprite images.
- void setSprites(const Sprites& sprites);
-
- // Removes a Sprite.
- void removeSprite(const std::string&);
-
- // Obtains a Sprite image.
- std::shared_ptr<const SpriteImage> getSprite(const std::string&);
-
- // Returns Sprite images that changed since the last invocation of this function.
- Sprites getDirty();
-
- const float pixelRatio;
-
-private:
- void _setSprite(const std::string&, const std::shared_ptr<const SpriteImage>& = nullptr);
- void emitSpriteLoadedIfComplete();
-
- struct Loader;
- std::unique_ptr<Loader> loader;
-
- bool loaded = false;
-
- SpriteStoreObserver* observer = nullptr;
-
- // Lock for sprites and dirty maps.
- std::mutex mutex;
-
- // Stores all current sprites.
- Sprites sprites;
-
- // Stores all Sprite IDs that changed since the last invocation.
- Sprites dirty;
-};
-
-} // namespace mbgl
diff --git a/src/mbgl/style/bucket_parameters.hpp b/src/mbgl/style/bucket_parameters.hpp
index ccd5f9a0e4..14fda41b8d 100644
--- a/src/mbgl/style/bucket_parameters.hpp
+++ b/src/mbgl/style/bucket_parameters.hpp
@@ -12,7 +12,6 @@ namespace mbgl {
class TileID;
class GeometryTileLayer;
class GeometryTileFeature;
-class SpriteStore;
class GlyphAtlas;
class GlyphStore;
class CollisionTile;
@@ -26,7 +25,6 @@ public:
const GeometryTileLayer& layer_,
const std::atomic<bool>& obsolete_,
uintptr_t tileUID_,
- SpriteStore& spriteStore_,
GlyphAtlas& glyphAtlas_,
GlyphStore& glyphStore_,
FeatureIndex& featureIndex_,
@@ -35,7 +33,6 @@ public:
layer(layer_),
obsolete(obsolete_),
tileUID(tileUID_),
- spriteStore(spriteStore_),
glyphAtlas(glyphAtlas_),
glyphStore(glyphStore_),
featureIndex(featureIndex_),
@@ -51,7 +48,6 @@ public:
const GeometryTileLayer& layer;
const std::atomic<bool>& obsolete;
uintptr_t tileUID;
- SpriteStore& spriteStore;
GlyphAtlas& glyphAtlas;
GlyphStore& glyphStore;
FeatureIndex& featureIndex;
diff --git a/src/mbgl/style/observer.hpp b/src/mbgl/style/observer.hpp
index 41b1b46da7..799d15dff0 100644
--- a/src/mbgl/style/observer.hpp
+++ b/src/mbgl/style/observer.hpp
@@ -1,7 +1,7 @@
#pragma once
#include <mbgl/text/glyph_store_observer.hpp>
-#include <mbgl/sprite/sprite_store_observer.hpp>
+#include <mbgl/sprite/sprite_atlas_observer.hpp>
#include <mbgl/style/source_observer.hpp>
#include <mbgl/map/update.hpp>
@@ -9,7 +9,7 @@ namespace mbgl {
namespace style {
class Observer : public GlyphStoreObserver,
- public SpriteStoreObserver,
+ public SpriteAtlasObserver,
public SourceObserver {
public:
virtual void onUpdate(Update) {}
diff --git a/src/mbgl/style/style.cpp b/src/mbgl/style/style.cpp
index a00c5e4efc..3d045bf1b7 100644
--- a/src/mbgl/style/style.cpp
+++ b/src/mbgl/style/style.cpp
@@ -18,7 +18,6 @@
#include <mbgl/style/update_parameters.hpp>
#include <mbgl/style/cascade_parameters.hpp>
#include <mbgl/style/calculation_parameters.hpp>
-#include <mbgl/sprite/sprite_store.hpp>
#include <mbgl/sprite/sprite_atlas.hpp>
#include <mbgl/geometry/glyph_atlas.hpp>
#include <mbgl/geometry/line_atlas.hpp>
@@ -40,12 +39,11 @@ Style::Style(FileSource& fileSource_, float pixelRatio)
: fileSource(fileSource_),
glyphStore(std::make_unique<GlyphStore>(fileSource)),
glyphAtlas(std::make_unique<GlyphAtlas>(2048, 2048)),
- spriteStore(std::make_unique<SpriteStore>(pixelRatio)),
- spriteAtlas(std::make_unique<SpriteAtlas>(1024, 1024, pixelRatio, *spriteStore)),
+ spriteAtlas(std::make_unique<SpriteAtlas>(1024, 1024, pixelRatio)),
lineAtlas(std::make_unique<LineAtlas>(256, 512)),
observer(&nullObserver) {
glyphStore->setObserver(this);
- spriteStore->setObserver(this);
+ spriteAtlas->setObserver(this);
}
Style::~Style() {
@@ -54,7 +52,7 @@ Style::~Style() {
}
glyphStore->setObserver(nullptr);
- spriteStore->setObserver(nullptr);
+ spriteAtlas->setObserver(nullptr);
}
bool Style::addClass(const std::string& className) {
@@ -125,7 +123,7 @@ void Style::setJSON(const std::string& json) {
defaultPitch = parser.pitch;
glyphStore->setURL(parser.glyphURL);
- spriteStore->load(parser.spriteURL, fileSource);
+ spriteAtlas->load(parser.spriteURL, fileSource);
loaded = true;
}
@@ -302,7 +300,7 @@ bool Style::isLoaded() const {
}
}
- if (!spriteStore->isLoaded()) {
+ if (!spriteAtlas->isLoaded()) {
return false;
}
@@ -524,7 +522,7 @@ void Style::dumpDebugLogs() const {
source->baseImpl->dumpDebugLogs();
}
- spriteStore->dumpDebugLogs();
+ spriteAtlas->dumpDebugLogs();
}
} // namespace style
diff --git a/src/mbgl/style/style.hpp b/src/mbgl/style/style.hpp
index 3e678ec060..2621e53c45 100644
--- a/src/mbgl/style/style.hpp
+++ b/src/mbgl/style/style.hpp
@@ -6,7 +6,7 @@
#include <mbgl/style/layer_observer.hpp>
#include <mbgl/style/update_batch.hpp>
#include <mbgl/text/glyph_store_observer.hpp>
-#include <mbgl/sprite/sprite_store_observer.hpp>
+#include <mbgl/sprite/sprite_atlas_observer.hpp>
#include <mbgl/map/mode.hpp>
#include <mbgl/map/zoom_history.hpp>
@@ -26,7 +26,6 @@ namespace mbgl {
class FileSource;
class GlyphStore;
class GlyphAtlas;
-class SpriteStore;
class SpriteAtlas;
class LineAtlas;
class RenderData;
@@ -38,7 +37,7 @@ class UpdateParameters;
class QueryParameters;
class Style : public GlyphStoreObserver,
- public SpriteStoreObserver,
+ public SpriteAtlasObserver,
public SourceObserver,
public LayerObserver,
public util::noncopyable {
@@ -106,7 +105,6 @@ public:
FileSource& fileSource;
std::unique_ptr<GlyphStore> glyphStore;
std::unique_ptr<GlyphAtlas> glyphAtlas;
- std::unique_ptr<SpriteStore> spriteStore;
std::unique_ptr<SpriteAtlas> spriteAtlas;
std::unique_ptr<LineAtlas> lineAtlas;
diff --git a/src/mbgl/tile/geometry_tile.cpp b/src/mbgl/tile/geometry_tile.cpp
index ed9d4a551b..36919517a2 100644
--- a/src/mbgl/tile/geometry_tile.cpp
+++ b/src/mbgl/tile/geometry_tile.cpp
@@ -27,7 +27,6 @@ GeometryTile::GeometryTile(const OverscaledTileID& id_,
worker(parameters.workerScheduler,
ActorRef<GeometryTile>(*this, mailbox),
id_,
- *parameters.style.spriteStore,
*parameters.style.glyphAtlas,
*parameters.style.glyphStore,
obsolete,
diff --git a/src/mbgl/tile/geometry_tile_worker.cpp b/src/mbgl/tile/geometry_tile_worker.cpp
index 35d15ae1fa..927cd0c2a1 100644
--- a/src/mbgl/tile/geometry_tile_worker.cpp
+++ b/src/mbgl/tile/geometry_tile_worker.cpp
@@ -6,7 +6,6 @@
#include <mbgl/style/bucket_parameters.hpp>
#include <mbgl/style/layers/symbol_layer.hpp>
#include <mbgl/style/layers/symbol_layer_impl.hpp>
-#include <mbgl/sprite/sprite_atlas.hpp>
#include <mbgl/geometry/glyph_atlas.hpp>
#include <mbgl/renderer/symbol_bucket.hpp>
#include <mbgl/platform/log.hpp>
@@ -23,7 +22,6 @@ using namespace style;
GeometryTileWorker::GeometryTileWorker(ActorRef<GeometryTileWorker> self_,
ActorRef<GeometryTile> parent_,
OverscaledTileID id_,
- SpriteStore& spriteStore_,
GlyphAtlas& glyphAtlas_,
GlyphStore& glyphStore_,
const std::atomic<bool>& obsolete_,
@@ -31,7 +29,6 @@ GeometryTileWorker::GeometryTileWorker(ActorRef<GeometryTileWorker> self_,
: self(std::move(self_)),
parent(std::move(parent_)),
id(std::move(id_)),
- spriteStore(spriteStore_),
glyphAtlas(glyphAtlas_),
glyphStore(glyphStore_),
obsolete(obsolete_),
@@ -219,7 +216,6 @@ void GeometryTileWorker::redoLayout() {
*geometryLayer,
obsolete,
reinterpret_cast<uintptr_t>(this),
- spriteStore,
glyphAtlas,
glyphStore,
*featureIndex,
@@ -259,7 +255,7 @@ void GeometryTileWorker::attemptPlacement() {
}
if (symbolLayout->state == SymbolLayout::Pending) {
- if (symbolLayout->canPrepare(glyphStore, spriteStore)) {
+ if (symbolLayout->canPrepare(glyphStore)) {
symbolLayout->state = SymbolLayout::Prepared;
symbolLayout->prepare(reinterpret_cast<uintptr_t>(this),
glyphAtlas,
diff --git a/src/mbgl/tile/geometry_tile_worker.hpp b/src/mbgl/tile/geometry_tile_worker.hpp
index cc5e48f9b4..362021fa4f 100644
--- a/src/mbgl/tile/geometry_tile_worker.hpp
+++ b/src/mbgl/tile/geometry_tile_worker.hpp
@@ -14,7 +14,6 @@ namespace mbgl {
class GeometryTile;
class GeometryTileData;
-class SpriteStore;
class GlyphAtlas;
class GlyphStore;
class SymbolLayout;
@@ -28,7 +27,6 @@ public:
GeometryTileWorker(ActorRef<GeometryTileWorker> self,
ActorRef<GeometryTile> parent,
OverscaledTileID,
- SpriteStore&,
GlyphAtlas&,
GlyphStore&,
const std::atomic<bool>&,
@@ -49,7 +47,6 @@ private:
ActorRef<GeometryTile> parent;
const OverscaledTileID id;
- SpriteStore& spriteStore;
GlyphAtlas& glyphAtlas;
GlyphStore& glyphStore;
const std::atomic<bool>& obsolete;
diff --git a/test/sprite/sprite_atlas.cpp b/test/sprite/sprite_atlas.cpp
index 10bfbfa67f..f84e17cbcf 100644
--- a/test/sprite/sprite_atlas.cpp
+++ b/test/sprite/sprite_atlas.cpp
@@ -1,11 +1,17 @@
#include <mbgl/test/util.hpp>
#include <mbgl/test/fixture_log_observer.hpp>
+#include <mbgl/test/stub_file_source.hpp>
+#include <mbgl/test/stub_style_observer.hpp>
#include <mbgl/sprite/sprite_atlas.hpp>
-#include <mbgl/sprite/sprite_store.hpp>
+#include <mbgl/sprite/sprite_atlas.hpp>
#include <mbgl/sprite/sprite_parser.hpp>
#include <mbgl/util/io.hpp>
#include <mbgl/util/image.hpp>
+#include <mbgl/util/run_loop.hpp>
+#include <mbgl/util/string.hpp>
+
+#include <utility>
using namespace mbgl;
@@ -25,16 +31,14 @@ auto imageFromAtlas(const SpriteAtlas& atlas) {
} // namespace
-TEST(Sprite, SpriteAtlas) {
+TEST(SpriteAtlas, Basic) {
FixtureLog log;
auto spriteParseResult = parseSprite(util::read_file("test/fixtures/annotations/emerald.png"),
util::read_file("test/fixtures/annotations/emerald.json"));
- SpriteStore store(1);
- store.setSprites(spriteParseResult.get<Sprites>());
-
- SpriteAtlas atlas(63, 112, 1, store);
+ SpriteAtlas atlas(63, 112, 1);
+ atlas.setSprites(spriteParseResult.get<Sprites>());
EXPECT_EQ(1.0f, atlas.getPixelRatio());
EXPECT_EQ(63, atlas.getWidth());
@@ -86,14 +90,12 @@ TEST(Sprite, SpriteAtlas) {
EXPECT_EQ(readImage("test/fixtures/annotations/result-spriteatlas.png"), imageFromAtlas(atlas));
}
-TEST(Sprite, SpriteAtlasSize) {
+TEST(SpriteAtlas, Size) {
auto spriteParseResult = parseSprite(util::read_file("test/fixtures/annotations/emerald.png"),
util::read_file("test/fixtures/annotations/emerald.json"));
- SpriteStore store(1);
- store.setSprites(spriteParseResult.get<Sprites>());
-
- SpriteAtlas atlas(63, 112, 1.4, store);
+ SpriteAtlas atlas(63, 112, 1.4);
+ atlas.setSprites(spriteParseResult.get<Sprites>());
EXPECT_DOUBLE_EQ(1.4f, atlas.getPixelRatio());
EXPECT_EQ(63, atlas.getWidth());
@@ -116,10 +118,8 @@ TEST(Sprite, SpriteAtlasSize) {
imageFromAtlas(atlas));
}
-TEST(Sprite, SpriteAtlasUpdates) {
- SpriteStore store(1);
-
- SpriteAtlas atlas(32, 32, 1, store);
+TEST(SpriteAtlas, Updates) {
+ SpriteAtlas atlas(32, 32, 1);
EXPECT_EQ(1.0f, atlas.getPixelRatio());
EXPECT_EQ(32, atlas.getWidth());
@@ -127,7 +127,7 @@ TEST(Sprite, SpriteAtlasUpdates) {
EXPECT_EQ(32, atlas.getTextureWidth());
EXPECT_EQ(32, atlas.getTextureHeight());
- store.setSprite("one", std::make_shared<SpriteImage>(PremultipliedImage(16, 12), 1));
+ atlas.setSprite("one", std::make_shared<SpriteImage>(PremultipliedImage(16, 12), 1));
auto one = *atlas.getImage("one", SpritePatternMode::Single);
EXPECT_EQ(0, one.pos.x);
EXPECT_EQ(0, one.pos.y);
@@ -148,8 +148,8 @@ TEST(Sprite, SpriteAtlasUpdates) {
image2.data.get()[i] = 255;
}
auto newSprite = std::make_shared<SpriteImage>(std::move(image2), 1);
- store.setSprite("one", newSprite);
- ASSERT_EQ(newSprite, store.getSprite("one"));
+ atlas.setSprite("one", newSprite);
+ ASSERT_EQ(newSprite, atlas.getSprite("one"));
// Atlas texture hasn't changed yet.
EXPECT_EQ(readImage("test/fixtures/annotations/result-spriteatlas-empty.png"),
@@ -161,3 +161,259 @@ TEST(Sprite, SpriteAtlasUpdates) {
EXPECT_EQ(readImage("test/fixtures/annotations/result-spriteatlas-updated.png"),
imageFromAtlas(atlas));
}
+
+TEST(SpriteAtlas, AddRemove) {
+ FixtureLog log;
+
+ const auto sprite1 = std::make_shared<SpriteImage>(PremultipliedImage(16, 16), 2);
+ const auto sprite2 = std::make_shared<SpriteImage>(PremultipliedImage(16, 16), 2);
+ const auto sprite3 = std::make_shared<SpriteImage>(PremultipliedImage(16, 16), 2);
+
+ SpriteAtlas atlas(32, 32, 1);
+
+ // Adding single
+ atlas.setSprite("one", sprite1);
+
+ // Adding multiple
+ atlas.setSprite("two", sprite2);
+ atlas.setSprite("three", sprite3);
+
+ // Removing
+ atlas.removeSprite("one");
+ atlas.removeSprite("two");
+
+ // Accessing
+ EXPECT_EQ(sprite3, atlas.getSprite("three"));
+
+ EXPECT_TRUE(log.empty());
+
+ EXPECT_EQ(nullptr, atlas.getSprite("two"));
+ EXPECT_EQ(nullptr, atlas.getSprite("four"));
+
+ EXPECT_EQ(1u, log.count({
+ EventSeverity::Info,
+ Event::Sprite,
+ int64_t(-1),
+ "Can't find sprite named 'two'",
+ }));
+ EXPECT_EQ(1u, log.count({
+ EventSeverity::Info,
+ Event::Sprite,
+ int64_t(-1),
+ "Can't find sprite named 'four'",
+ }));
+
+ // Overwriting
+ atlas.setSprite("three", sprite1);
+}
+
+TEST(SpriteAtlas, OtherPixelRatio) {
+ FixtureLog log;
+
+ const auto sprite1 = std::make_shared<SpriteImage>(PremultipliedImage(8, 8), 1);
+
+ SpriteAtlas atlas(32, 32, 1);
+
+ // Adding mismatched sprite image
+ atlas.setSprite("one", sprite1);
+}
+
+TEST(SpriteAtlas, Multiple) {
+ const auto sprite1 = std::make_shared<SpriteImage>(PremultipliedImage(16, 16), 2);
+ const auto sprite2 = std::make_shared<SpriteImage>(PremultipliedImage(16, 16), 2);
+
+ SpriteAtlas atlas(32, 32, 1);
+
+ atlas.setSprites({
+ { "one", sprite1 }, { "two", sprite2 },
+ });
+}
+
+TEST(SpriteAtlas, Replace) {
+ FixtureLog log;
+
+ const auto sprite1 = std::make_shared<SpriteImage>(PremultipliedImage(16, 16), 2);
+ const auto sprite2 = std::make_shared<SpriteImage>(PremultipliedImage(16, 16), 2);
+
+ SpriteAtlas atlas(32, 32, 1);
+
+ atlas.setSprite("sprite", sprite1);
+ EXPECT_EQ(sprite1, atlas.getSprite("sprite"));
+ atlas.setSprite("sprite", sprite2);
+ EXPECT_EQ(sprite2, atlas.getSprite("sprite"));
+}
+
+TEST(SpriteAtlas, ReplaceWithDifferentDimensions) {
+ FixtureLog log;
+
+ PremultipliedImage image(16, 16);
+ PremultipliedImage image2(18, 18);
+ const auto sprite1 = std::make_shared<SpriteImage>(PremultipliedImage(16, 16), 2);
+ const auto sprite2 = std::make_shared<SpriteImage>(PremultipliedImage(18, 18), 2);
+
+ SpriteAtlas atlas(32, 32, 1);
+
+ atlas.setSprite("sprite", sprite1);
+ atlas.setSprite("sprite", sprite2);
+
+ EXPECT_EQ(1u, log.count({
+ EventSeverity::Warning,
+ Event::Sprite,
+ int64_t(-1),
+ "Can't change sprite dimensions for 'sprite'",
+ }));
+
+ EXPECT_EQ(sprite1, atlas.getSprite("sprite"));
+}
+
+class SpriteAtlasTest {
+public:
+ SpriteAtlasTest() = default;
+
+ util::RunLoop loop;
+ StubFileSource fileSource;
+ StubStyleObserver observer;
+ SpriteAtlas spriteAtlas { 32, 32, 1 };
+
+ void run() {
+ // Squelch logging.
+ Log::setObserver(std::make_unique<Log::NullObserver>());
+
+ spriteAtlas.setObserver(&observer);
+ spriteAtlas.load("test/fixtures/resources/sprite", fileSource);
+
+ loop.run();
+ }
+
+ void end() {
+ loop.stop();
+ }
+};
+
+Response successfulSpriteImageResponse(const Resource& resource) {
+ EXPECT_EQ("test/fixtures/resources/sprite.png", resource.url);
+ Response response;
+ response.data = std::make_shared<std::string>(util::read_file(resource.url));
+ return response;
+}
+
+Response successfulSpriteJSONResponse(const Resource& resource) {
+ EXPECT_EQ("test/fixtures/resources/sprite.json", resource.url);
+ Response response;
+ response.data = std::make_shared<std::string>(util::read_file(resource.url));
+ return response;
+}
+
+Response failedSpriteResponse(const Resource&) {
+ Response response;
+ response.error = std::make_unique<Response::Error>(
+ Response::Error::Reason::Other,
+ "Failed by the test case");
+ return response;
+}
+
+Response corruptSpriteResponse(const Resource&) {
+ Response response;
+ response.data = std::make_shared<std::string>("CORRUPT");
+ return response;
+}
+
+TEST(SpriteAtlas, LoadingSuccess) {
+ SpriteAtlasTest test;
+
+ test.fileSource.spriteImageResponse = successfulSpriteImageResponse;
+ test.fileSource.spriteJSONResponse = successfulSpriteJSONResponse;
+
+ test.observer.spriteError = [&] (std::exception_ptr error) {
+ FAIL() << util::toString(error);
+ test.end();
+ };
+
+ test.observer.spriteLoaded = [&] () {
+ EXPECT_EQ(1.0, test.spriteAtlas.getPixelRatio());
+ EXPECT_TRUE(test.spriteAtlas.isLoaded());
+ test.end();
+ };
+
+ test.run();
+}
+
+TEST(SpriteAtlas, JSONLoadingFail) {
+ SpriteAtlasTest test;
+
+ test.fileSource.spriteImageResponse = successfulSpriteImageResponse;
+ test.fileSource.spriteJSONResponse = failedSpriteResponse;
+
+ test.observer.spriteError = [&] (std::exception_ptr error) {
+ EXPECT_TRUE(error != nullptr);
+ EXPECT_EQ("Failed by the test case", util::toString(error));
+ EXPECT_FALSE(test.spriteAtlas.isLoaded());
+ test.end();
+ };
+
+ test.run();
+}
+
+TEST(SpriteAtlas, ImageLoadingFail) {
+ SpriteAtlasTest test;
+
+ test.fileSource.spriteImageResponse = failedSpriteResponse;
+ test.fileSource.spriteJSONResponse = successfulSpriteJSONResponse;
+
+ test.observer.spriteError = [&] (std::exception_ptr error) {
+ EXPECT_TRUE(error != nullptr);
+ EXPECT_EQ("Failed by the test case", util::toString(error));
+ EXPECT_FALSE(test.spriteAtlas.isLoaded());
+ test.end();
+ };
+
+ test.run();
+}
+
+TEST(SpriteAtlas, JSONLoadingCorrupted) {
+ SpriteAtlasTest test;
+
+ test.fileSource.spriteImageResponse = successfulSpriteImageResponse;
+ test.fileSource.spriteJSONResponse = corruptSpriteResponse;
+
+ test.observer.spriteError = [&] (std::exception_ptr error) {
+ EXPECT_TRUE(error != nullptr);
+ EXPECT_EQ("Failed to parse JSON: Invalid value. at offset 0", util::toString(error));
+ EXPECT_FALSE(test.spriteAtlas.isLoaded());
+ test.end();
+ };
+
+ test.run();
+}
+
+TEST(SpriteAtlas, ImageLoadingCorrupted) {
+ SpriteAtlasTest test;
+
+ test.fileSource.spriteImageResponse = corruptSpriteResponse;
+ test.fileSource.spriteJSONResponse = successfulSpriteJSONResponse;
+
+ test.observer.spriteError = [&] (std::exception_ptr error) {
+ EXPECT_TRUE(error != nullptr);
+ // Not asserting on platform-specific error text.
+ EXPECT_FALSE(test.spriteAtlas.isLoaded());
+ test.end();
+ };
+
+ test.run();
+}
+
+TEST(SpriteAtlas, LoadingCancel) {
+ SpriteAtlasTest test;
+
+ test.fileSource.spriteImageResponse =
+ test.fileSource.spriteJSONResponse = [&] (const Resource&) {
+ test.end();
+ return optional<Response>();
+ };
+
+ test.observer.spriteLoaded = [&] () {
+ FAIL() << "Should never be called";
+ };
+
+ test.run();
+}
diff --git a/test/sprite/sprite_store.cpp b/test/sprite/sprite_store.cpp
deleted file mode 100644
index 3c20d3c50e..0000000000
--- a/test/sprite/sprite_store.cpp
+++ /dev/null
@@ -1,305 +0,0 @@
-#include <mbgl/test/util.hpp>
-#include <mbgl/test/fixture_log_observer.hpp>
-#include <mbgl/test/stub_file_source.hpp>
-#include <mbgl/test/stub_style_observer.hpp>
-
-#include <mbgl/sprite/sprite_store.hpp>
-#include <mbgl/util/run_loop.hpp>
-#include <mbgl/util/string.hpp>
-#include <mbgl/util/io.hpp>
-
-#include <utility>
-
-using namespace mbgl;
-
-TEST(SpriteStore, SpriteStore) {
- FixtureLog log;
-
- const auto sprite1 = std::make_shared<SpriteImage>(PremultipliedImage(16, 16), 2);
- const auto sprite2 = std::make_shared<SpriteImage>(PremultipliedImage(16, 16), 2);
- const auto sprite3 = std::make_shared<SpriteImage>(PremultipliedImage(16, 16), 2);
-
- using Sprites = std::map<std::string, std::shared_ptr<const SpriteImage>>;
- SpriteStore store(1);
-
- // Adding single
- store.setSprite("one", sprite1);
- EXPECT_EQ(Sprites({
- { "one", sprite1 },
- }),
- store.getDirty());
- EXPECT_EQ(Sprites(), store.getDirty());
-
- // Adding multiple
- store.setSprite("two", sprite2);
- store.setSprite("three", sprite3);
- EXPECT_EQ(Sprites({
- { "two", sprite2 }, { "three", sprite3 },
- }),
- store.getDirty());
- EXPECT_EQ(Sprites(), store.getDirty());
-
- // Removing
- store.removeSprite("one");
- store.removeSprite("two");
- EXPECT_EQ(Sprites({
- { "one", nullptr }, { "two", nullptr },
- }),
- store.getDirty());
- EXPECT_EQ(Sprites(), store.getDirty());
-
- // Accessing
- EXPECT_EQ(sprite3, store.getSprite("three"));
-
- EXPECT_TRUE(log.empty());
-
- EXPECT_EQ(nullptr, store.getSprite("two"));
- EXPECT_EQ(nullptr, store.getSprite("four"));
-
- EXPECT_EQ(1u, log.count({
- EventSeverity::Info,
- Event::Sprite,
- int64_t(-1),
- "Can't find sprite named 'two'",
- }));
- EXPECT_EQ(1u, log.count({
- EventSeverity::Info,
- Event::Sprite,
- int64_t(-1),
- "Can't find sprite named 'four'",
- }));
-
- // Overwriting
- store.setSprite("three", sprite1);
- EXPECT_EQ(Sprites({
- { "three", sprite1 },
- }),
- store.getDirty());
- EXPECT_EQ(Sprites(), store.getDirty());
-}
-
-TEST(SpriteStore, OtherPixelRatio) {
- FixtureLog log;
-
- const auto sprite1 = std::make_shared<SpriteImage>(PremultipliedImage(8, 8), 1);
-
- using Sprites = std::map<std::string, std::shared_ptr<const SpriteImage>>;
- SpriteStore store(1);
-
- // Adding mismatched sprite image
- store.setSprite("one", sprite1);
- EXPECT_EQ(Sprites({ { "one", sprite1 } }), store.getDirty());
-}
-
-TEST(SpriteStore, Multiple) {
- const auto sprite1 = std::make_shared<SpriteImage>(PremultipliedImage(16, 16), 2);
- const auto sprite2 = std::make_shared<SpriteImage>(PremultipliedImage(16, 16), 2);
-
- using Sprites = std::map<std::string, std::shared_ptr<const SpriteImage>>;
- SpriteStore store(1);
-
- store.setSprites({
- { "one", sprite1 }, { "two", sprite2 },
- });
- EXPECT_EQ(Sprites({
- { "one", sprite1 }, { "two", sprite2 },
- }),
- store.getDirty());
- EXPECT_EQ(Sprites(), store.getDirty());
-}
-
-TEST(SpriteStore, Replace) {
- FixtureLog log;
-
- const auto sprite1 = std::make_shared<SpriteImage>(PremultipliedImage(16, 16), 2);
- const auto sprite2 = std::make_shared<SpriteImage>(PremultipliedImage(16, 16), 2);
-
- using Sprites = std::map<std::string, std::shared_ptr<const SpriteImage>>;
- SpriteStore store(1);
-
- store.setSprite("sprite", sprite1);
- EXPECT_EQ(sprite1, store.getSprite("sprite"));
- store.setSprite("sprite", sprite2);
- EXPECT_EQ(sprite2, store.getSprite("sprite"));
-
- EXPECT_EQ(Sprites({ { "sprite", sprite2 } }), store.getDirty());
-}
-
-TEST(SpriteStore, ReplaceWithDifferentDimensions) {
- FixtureLog log;
-
- PremultipliedImage image(16, 16);
- PremultipliedImage image2(18, 18);
- const auto sprite1 = std::make_shared<SpriteImage>(PremultipliedImage(16, 16), 2);
- const auto sprite2 = std::make_shared<SpriteImage>(PremultipliedImage(18, 18), 2);
-
- using Sprites = std::map<std::string, std::shared_ptr<const SpriteImage>>;
- SpriteStore store(1);
-
- store.setSprite("sprite", sprite1);
- store.setSprite("sprite", sprite2);
-
- EXPECT_EQ(1u, log.count({
- EventSeverity::Warning,
- Event::Sprite,
- int64_t(-1),
- "Can't change sprite dimensions for 'sprite'",
- }));
-
- EXPECT_EQ(sprite1, store.getSprite("sprite"));
-
- EXPECT_EQ(Sprites({ { "sprite", sprite1 } }), store.getDirty());
-}
-
-class SpriteStoreTest {
-public:
- SpriteStoreTest() = default;
-
- util::RunLoop loop;
- StubFileSource fileSource;
- StubStyleObserver observer;
- SpriteStore spriteStore = { 1.0 };
-
- void run() {
- // Squelch logging.
- Log::setObserver(std::make_unique<Log::NullObserver>());
-
- spriteStore.setObserver(&observer);
- spriteStore.load("test/fixtures/resources/sprite", fileSource);
-
- loop.run();
- }
-
- void end() {
- loop.stop();
- }
-};
-
-Response successfulSpriteImageResponse(const Resource& resource) {
- EXPECT_EQ("test/fixtures/resources/sprite.png", resource.url);
- Response response;
- response.data = std::make_shared<std::string>(util::read_file(resource.url));
- return response;
-}
-
-Response successfulSpriteJSONResponse(const Resource& resource) {
- EXPECT_EQ("test/fixtures/resources/sprite.json", resource.url);
- Response response;
- response.data = std::make_shared<std::string>(util::read_file(resource.url));
- return response;
-}
-
-Response failedSpriteResponse(const Resource&) {
- Response response;
- response.error = std::make_unique<Response::Error>(
- Response::Error::Reason::Other,
- "Failed by the test case");
- return response;
-}
-
-Response corruptSpriteResponse(const Resource&) {
- Response response;
- response.data = std::make_shared<std::string>("CORRUPT");
- return response;
-}
-
-TEST(SpriteStore, LoadingSuccess) {
- SpriteStoreTest test;
-
- test.fileSource.spriteImageResponse = successfulSpriteImageResponse;
- test.fileSource.spriteJSONResponse = successfulSpriteJSONResponse;
-
- test.observer.spriteError = [&] (std::exception_ptr error) {
- FAIL() << util::toString(error);
- test.end();
- };
-
- test.observer.spriteLoaded = [&] () {
- EXPECT_TRUE(!test.spriteStore.getDirty().empty());
- EXPECT_EQ(1.0, test.spriteStore.pixelRatio);
- EXPECT_TRUE(test.spriteStore.isLoaded());
- test.end();
- };
-
- test.run();
-}
-
-TEST(SpriteStore, JSONLoadingFail) {
- SpriteStoreTest test;
-
- test.fileSource.spriteImageResponse = successfulSpriteImageResponse;
- test.fileSource.spriteJSONResponse = failedSpriteResponse;
-
- test.observer.spriteError = [&] (std::exception_ptr error) {
- EXPECT_TRUE(error != nullptr);
- EXPECT_EQ("Failed by the test case", util::toString(error));
- EXPECT_FALSE(test.spriteStore.isLoaded());
- test.end();
- };
-
- test.run();
-}
-
-TEST(SpriteStore, ImageLoadingFail) {
- SpriteStoreTest test;
-
- test.fileSource.spriteImageResponse = failedSpriteResponse;
- test.fileSource.spriteJSONResponse = successfulSpriteJSONResponse;
-
- test.observer.spriteError = [&] (std::exception_ptr error) {
- EXPECT_TRUE(error != nullptr);
- EXPECT_EQ("Failed by the test case", util::toString(error));
- EXPECT_FALSE(test.spriteStore.isLoaded());
- test.end();
- };
-
- test.run();
-}
-
-TEST(SpriteStore, JSONLoadingCorrupted) {
- SpriteStoreTest test;
-
- test.fileSource.spriteImageResponse = successfulSpriteImageResponse;
- test.fileSource.spriteJSONResponse = corruptSpriteResponse;
-
- test.observer.spriteError = [&] (std::exception_ptr error) {
- EXPECT_TRUE(error != nullptr);
- EXPECT_EQ("Failed to parse JSON: Invalid value. at offset 0", util::toString(error));
- EXPECT_FALSE(test.spriteStore.isLoaded());
- test.end();
- };
-
- test.run();
-}
-
-TEST(SpriteStore, ImageLoadingCorrupted) {
- SpriteStoreTest test;
-
- test.fileSource.spriteImageResponse = corruptSpriteResponse;
- test.fileSource.spriteJSONResponse = successfulSpriteJSONResponse;
-
- test.observer.spriteError = [&] (std::exception_ptr error) {
- EXPECT_TRUE(error != nullptr);
- // Not asserting on platform-specific error text.
- EXPECT_FALSE(test.spriteStore.isLoaded());
- test.end();
- };
-
- test.run();
-}
-
-TEST(SpriteStore, LoadingCancel) {
- SpriteStoreTest test;
-
- test.fileSource.spriteImageResponse =
- test.fileSource.spriteJSONResponse = [&] (const Resource&) {
- test.end();
- return optional<Response>();
- };
-
- test.observer.spriteLoaded = [&] () {
- FAIL() << "Should never be called";
- };
-
- test.run();
-}
diff --git a/test/style/variant.cpp b/test/style/variant.cpp
deleted file mode 100644
index e69de29bb2..0000000000
--- a/test/style/variant.cpp
+++ /dev/null