diff options
author | John Firebaugh <john.firebaugh@gmail.com> | 2016-09-15 15:40:35 -0700 |
---|---|---|
committer | John Firebaugh <john.firebaugh@gmail.com> | 2016-09-19 09:40:43 -0700 |
commit | 940124ff713a960d7f70071779dd37d07010fa80 (patch) | |
tree | a89c129b9e972946ab29f05587381c45649a5d99 | |
parent | 1014a503a22dc47e4e6ec3c37d034fd729873345 (diff) | |
download | qtlocation-mapboxgl-940124ff713a960d7f70071779dd37d07010fa80.tar.gz |
[core] Merge SpriteStore and SpriteAtlas
-rw-r--r-- | cmake/core-files.cmake | 4 | ||||
-rw-r--r-- | cmake/test-files.cmake | 2 | ||||
-rw-r--r-- | src/mbgl/annotation/annotation_manager.cpp | 23 | ||||
-rw-r--r-- | src/mbgl/annotation/annotation_manager.hpp | 3 | ||||
-rw-r--r-- | src/mbgl/layout/symbol_layout.cpp | 5 | ||||
-rw-r--r-- | src/mbgl/layout/symbol_layout.hpp | 3 | ||||
-rw-r--r-- | src/mbgl/sprite/sprite_atlas.cpp | 164 | ||||
-rw-r--r-- | src/mbgl/sprite/sprite_atlas.hpp | 55 | ||||
-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.cpp | 158 | ||||
-rw-r--r-- | src/mbgl/sprite/sprite_store.hpp | 70 | ||||
-rw-r--r-- | src/mbgl/style/bucket_parameters.hpp | 4 | ||||
-rw-r--r-- | src/mbgl/style/observer.hpp | 4 | ||||
-rw-r--r-- | src/mbgl/style/style.cpp | 14 | ||||
-rw-r--r-- | src/mbgl/style/style.hpp | 6 | ||||
-rw-r--r-- | src/mbgl/tile/geometry_tile.cpp | 1 | ||||
-rw-r--r-- | src/mbgl/tile/geometry_tile_worker.cpp | 6 | ||||
-rw-r--r-- | src/mbgl/tile/geometry_tile_worker.hpp | 3 | ||||
-rw-r--r-- | test/sprite/sprite_atlas.cpp | 292 | ||||
-rw-r--r-- | test/sprite/sprite_store.cpp | 305 | ||||
-rw-r--r-- | test/style/variant.cpp | 0 |
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 |