summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIvo van Dongen <ivovandongen@users.noreply.github.com>2017-05-12 23:19:00 +0300
committerJohn Firebaugh <john.firebaugh@gmail.com>2017-05-12 13:19:00 -0700
commitcc9f040a2d35293c51dcc5be9c7affea7f1263bd (patch)
treede62a5610e719f5bfe07226c3382d4d2a5e17530
parentc80f3e9d29d1c26ccc88ef30f8f17329c9bfb1b7 (diff)
downloadqtlocation-mapboxgl-cc9f040a2d35293c51dcc5be9c7affea7f1263bd.tar.gz
[core] Split style image collection from SpriteAtlas
-rw-r--r--cmake/core-files.cmake13
-rw-r--r--cmake/test-files.cmake2
-rw-r--r--include/mbgl/style/image.hpp17
-rw-r--r--platform/ios/src/UIImage+MGLAdditions.mm6
-rw-r--r--platform/macos/src/NSImage+MGLAdditions.mm4
-rw-r--r--src/mbgl/annotation/annotation_manager.cpp13
-rw-r--r--src/mbgl/annotation/annotation_manager.hpp3
-rw-r--r--src/mbgl/map/map.cpp8
-rw-r--r--src/mbgl/sprite/sprite_atlas.cpp115
-rw-r--r--src/mbgl/sprite/sprite_atlas.hpp28
-rw-r--r--src/mbgl/sprite/sprite_atlas_observer.hpp15
-rw-r--r--src/mbgl/sprite/sprite_image_collection.cpp40
-rw-r--r--src/mbgl/sprite/sprite_image_collection.hpp25
-rw-r--r--src/mbgl/sprite/sprite_loader.cpp107
-rw-r--r--src/mbgl/sprite/sprite_loader.hpp46
-rw-r--r--src/mbgl/sprite/sprite_loader_observer.hpp24
-rw-r--r--src/mbgl/sprite/sprite_loader_worker.cpp (renamed from src/mbgl/sprite/sprite_atlas_worker.cpp)12
-rw-r--r--src/mbgl/sprite/sprite_loader_worker.hpp (renamed from src/mbgl/sprite/sprite_atlas_worker.hpp)8
-rw-r--r--src/mbgl/sprite/sprite_parser.hpp4
-rw-r--r--src/mbgl/style/image.cpp38
-rw-r--r--src/mbgl/style/image_impl.cpp22
-rw-r--r--src/mbgl/style/image_impl.hpp25
-rw-r--r--src/mbgl/style/layers/symbol_layer_impl.hpp1
-rw-r--r--src/mbgl/style/observer.hpp4
-rw-r--r--src/mbgl/style/style.cpp41
-rw-r--r--src/mbgl/style/style.hpp15
-rw-r--r--test/map/map.test.cpp2
-rw-r--r--test/sprite/sprite_atlas.test.cpp187
-rw-r--r--test/sprite/sprite_image_collection.test.cpp45
-rw-r--r--test/sprite/sprite_loader.test.cpp164
-rw-r--r--test/sprite/sprite_parser.test.cpp40
-rw-r--r--test/src/mbgl/test/stub_style_observer.hpp6
-rw-r--r--test/style/style_image.test.cpp12
33 files changed, 678 insertions, 414 deletions
diff --git a/cmake/core-files.cmake b/cmake/core-files.cmake
index f83a0413b9..fbe43bef3b 100644
--- a/cmake/core-files.cmake
+++ b/cmake/core-files.cmake
@@ -283,9 +283,13 @@ set(MBGL_CORE_FILES
# sprite
src/mbgl/sprite/sprite_atlas.cpp
src/mbgl/sprite/sprite_atlas.hpp
- src/mbgl/sprite/sprite_atlas_observer.hpp
- src/mbgl/sprite/sprite_atlas_worker.cpp
- src/mbgl/sprite/sprite_atlas_worker.hpp
+ src/mbgl/sprite/sprite_image_collection.cpp
+ src/mbgl/sprite/sprite_image_collection.hpp
+ src/mbgl/sprite/sprite_loader.cpp
+ src/mbgl/sprite/sprite_loader.hpp
+ src/mbgl/sprite/sprite_loader_observer.hpp
+ src/mbgl/sprite/sprite_loader_worker.cpp
+ src/mbgl/sprite/sprite_loader_worker.hpp
src/mbgl/sprite/sprite_parser.cpp
src/mbgl/sprite/sprite_parser.hpp
@@ -322,6 +326,8 @@ set(MBGL_CORE_FILES
src/mbgl/style/class_dictionary.cpp
src/mbgl/style/class_dictionary.hpp
src/mbgl/style/image.cpp
+ src/mbgl/style/image_impl.cpp
+ src/mbgl/style/image_impl.hpp
src/mbgl/style/layer.cpp
src/mbgl/style/layer_impl.cpp
src/mbgl/style/layer_impl.hpp
@@ -363,6 +369,7 @@ set(MBGL_CORE_FILES
include/mbgl/style/conversion/tileset.hpp
include/mbgl/style/conversion/transition_options.hpp
src/mbgl/style/conversion/geojson.cpp
+ src/mbgl/style/conversion/json.hpp
src/mbgl/style/conversion/stringify.hpp
# style/function
diff --git a/cmake/test-files.cmake b/cmake/test-files.cmake
index 6026374de6..5eb9d8d9f8 100644
--- a/cmake/test-files.cmake
+++ b/cmake/test-files.cmake
@@ -46,6 +46,8 @@ set(MBGL_TEST_FILES
# sprite
test/sprite/sprite_atlas.test.cpp
+ test/sprite/sprite_image_collection.test.cpp
+ test/sprite/sprite_loader.test.cpp
test/sprite/sprite_parser.test.cpp
# src/mbgl/test
diff --git a/include/mbgl/style/image.hpp b/include/mbgl/style/image.hpp
index 499377467e..4abd86f1be 100644
--- a/include/mbgl/style/image.hpp
+++ b/include/mbgl/style/image.hpp
@@ -2,6 +2,8 @@
#include <mbgl/util/image.hpp>
+#include <memory>
+
namespace mbgl {
namespace style {
@@ -9,16 +11,21 @@ class Image {
public:
Image(PremultipliedImage&&, float pixelRatio, bool sdf = false);
- PremultipliedImage image;
+ PremultipliedImage& getImage() const;
// Pixel ratio of the sprite image.
- const float pixelRatio;
+ float getPixelRatio() const;
// Whether this image should be interpreted as a signed distance field icon.
- const bool sdf;
+ bool isSdf() const;
+
+ float getWidth() const;
+ float getHeight() const;
+
+ class Impl;
- float getWidth() const { return image.size.width / pixelRatio; }
- float getHeight() const { return image.size.height / pixelRatio; }
+private:
+ const std::shared_ptr<Impl> impl;
};
} // namespace style
diff --git a/platform/ios/src/UIImage+MGLAdditions.mm b/platform/ios/src/UIImage+MGLAdditions.mm
index db64d78232..4ced1e30d5 100644
--- a/platform/ios/src/UIImage+MGLAdditions.mm
+++ b/platform/ios/src/UIImage+MGLAdditions.mm
@@ -6,14 +6,14 @@
- (nullable instancetype)initWithMGLStyleImage:(const mbgl::style::Image *)styleImage
{
- CGImageRef image = CGImageFromMGLPremultipliedImage(styleImage->image.clone());
+ CGImageRef image = CGImageFromMGLPremultipliedImage(styleImage->getImage().clone());
if (!image) {
return nil;
}
- if (self = [self initWithCGImage:image scale:styleImage->pixelRatio orientation:UIImageOrientationUp])
+ if (self = [self initWithCGImage:image scale:styleImage->getPixelRatio() orientation:UIImageOrientationUp])
{
- if (styleImage->sdf)
+ if (styleImage->isSdf())
{
self = [self imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
}
diff --git a/platform/macos/src/NSImage+MGLAdditions.mm b/platform/macos/src/NSImage+MGLAdditions.mm
index 91c4f7bf66..fee3fe2751 100644
--- a/platform/macos/src/NSImage+MGLAdditions.mm
+++ b/platform/macos/src/NSImage+MGLAdditions.mm
@@ -16,7 +16,7 @@
}
- (nullable instancetype)initWithMGLStyleImage:(const mbgl::style::Image *)styleImage {
- CGImageRef image = CGImageFromMGLPremultipliedImage(styleImage->image.clone());
+ CGImageRef image = CGImageFromMGLPremultipliedImage(styleImage->getImage().clone());
if (!image) {
return nil;
}
@@ -25,7 +25,7 @@
CGImageRelease(image);
if (self = [self initWithSize:NSMakeSize(styleImage->getWidth(), styleImage->getHeight())]) {
[self addRepresentation:rep];
- [self setTemplate:styleImage->sdf];
+ [self setTemplate:styleImage->isSdf()];
}
return self;
}
diff --git a/src/mbgl/annotation/annotation_manager.cpp b/src/mbgl/annotation/annotation_manager.cpp
index e11ecff30c..96cdd96750 100644
--- a/src/mbgl/annotation/annotation_manager.cpp
+++ b/src/mbgl/annotation/annotation_manager.cpp
@@ -4,6 +4,7 @@
#include <mbgl/annotation/symbol_annotation_impl.hpp>
#include <mbgl/annotation/line_annotation_impl.hpp>
#include <mbgl/annotation/fill_annotation_impl.hpp>
+#include <mbgl/sprite/sprite_image_collection.hpp>
#include <mbgl/style/style.hpp>
#include <mbgl/style/layers/symbol_layer.hpp>
#include <mbgl/style/layers/symbol_layer_impl.hpp>
@@ -19,7 +20,7 @@ const std::string AnnotationManager::SourceID = "com.mapbox.annotations";
const std::string AnnotationManager::PointLayerID = "com.mapbox.annotations.points";
AnnotationManager::AnnotationManager(float pixelRatio)
- : spriteAtlas({ 1024, 1024 }, pixelRatio) {
+ : spriteAtlas({ 1024, 1024 }, pixelRatio){
// This is a special atlas, holding only images added via addIcon, so we always treat it as
// loaded.
spriteAtlas.markAsLoaded();
@@ -190,16 +191,20 @@ void AnnotationManager::removeTile(AnnotationTile& tile) {
}
void AnnotationManager::addImage(const std::string& id, std::unique_ptr<style::Image> image) {
- spriteAtlas.addImage(id, std::move(image));
+ addSpriteImage(spriteImages, id, std::move(image), [&](style::Image& added) {
+ spriteAtlas.addImage(id, std::make_unique<style::Image>(added));
+ });
}
void AnnotationManager::removeImage(const std::string& id) {
- spriteAtlas.removeImage(id);
+ removeSpriteImage(spriteImages, id, [&] () {
+ spriteAtlas.removeImage(id);
+ });
}
double AnnotationManager::getTopOffsetPixelsForImage(const std::string& id) {
const style::Image* image = spriteAtlas.getImage(id);
- return image ? -(image->image.size.height / image->pixelRatio) / 2 : 0;
+ return image ? -(image->getImage().size.height / image->getPixelRatio()) / 2 : 0;
}
} // namespace mbgl
diff --git a/src/mbgl/annotation/annotation_manager.hpp b/src/mbgl/annotation/annotation_manager.hpp
index de213c830d..69232677f9 100644
--- a/src/mbgl/annotation/annotation_manager.hpp
+++ b/src/mbgl/annotation/annotation_manager.hpp
@@ -9,6 +9,7 @@
#include <string>
#include <vector>
#include <unordered_set>
+#include <unordered_map>
namespace mbgl {
@@ -72,6 +73,8 @@ private:
ShapeAnnotationMap shapeAnnotations;
std::unordered_set<std::string> obsoleteShapeAnnotationLayers;
std::unordered_set<AnnotationTile*> tiles;
+
+ std::unordered_map<std::string, std::unique_ptr<style::Image>> spriteImages;
SpriteAtlas spriteAtlas;
friend class AnnotationTile;
diff --git a/src/mbgl/map/map.cpp b/src/mbgl/map/map.cpp
index 4ef422a2de..16254f6e9b 100644
--- a/src/mbgl/map/map.cpp
+++ b/src/mbgl/map/map.cpp
@@ -942,8 +942,7 @@ void Map::addImage(const std::string& id, std::unique_ptr<style::Image> image) {
}
impl->styleMutated = true;
- impl->style->spriteAtlas->addImage(id, std::move(image));
- impl->onUpdate(Update::Repaint);
+ impl->style->addImage(id, std::move(image));
}
void Map::removeImage(const std::string& id) {
@@ -952,13 +951,12 @@ void Map::removeImage(const std::string& id) {
}
impl->styleMutated = true;
- impl->style->spriteAtlas->removeImage(id);
- impl->onUpdate(Update::Repaint);
+ impl->style->removeImage(id);
}
const style::Image* Map::getImage(const std::string& id) {
if (impl->style) {
- return impl->style->spriteAtlas->getImage(id);
+ return impl->style->getImage(id);
}
return nullptr;
}
diff --git a/src/mbgl/sprite/sprite_atlas.cpp b/src/mbgl/sprite/sprite_atlas.cpp
index fed8451aed..bef74b7ce5 100644
--- a/src/mbgl/sprite/sprite_atlas.cpp
+++ b/src/mbgl/sprite/sprite_atlas.cpp
@@ -1,7 +1,4 @@
#include <mbgl/sprite/sprite_atlas.hpp>
-#include <mbgl/sprite/sprite_atlas_worker.hpp>
-#include <mbgl/sprite/sprite_atlas_observer.hpp>
-#include <mbgl/sprite/sprite_parser.hpp>
#include <mbgl/gl/context.hpp>
#include <mbgl/util/logging.hpp>
#include <mbgl/util/platform.hpp>
@@ -9,11 +6,6 @@
#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 <mbgl/util/run_loop.hpp>
-#include <mbgl/actor/actor.hpp>
#include <cassert>
#include <cmath>
@@ -21,28 +13,12 @@
namespace mbgl {
-static SpriteAtlasObserver nullObserver;
-
-struct SpriteAtlas::Loader {
- Loader(Scheduler& scheduler, SpriteAtlas& spriteAtlas)
- : mailbox(std::make_shared<Mailbox>(*util::RunLoop::Get())),
- worker(scheduler, ActorRef<SpriteAtlas>(spriteAtlas, mailbox)) {
- }
-
- std::shared_ptr<const std::string> image;
- std::shared_ptr<const std::string> json;
- std::unique_ptr<AsyncRequest> jsonRequest;
- std::unique_ptr<AsyncRequest> spriteRequest;
- std::shared_ptr<Mailbox> mailbox;
- Actor<SpriteAtlasWorker> worker;
-};
-
SpriteAtlasElement::SpriteAtlasElement(Rect<uint16_t> rect_,
const style::Image& image,
Size size_, float pixelRatio)
: pos(std::move(rect_)),
- sdf(image.sdf),
- relativePixelRatio(image.pixelRatio / pixelRatio),
+ sdf(image.isSdf()),
+ relativePixelRatio(image.getPixelRatio() / pixelRatio),
width(image.getWidth()),
height(image.getHeight()) {
@@ -59,87 +35,25 @@ SpriteAtlasElement::SpriteAtlasElement(Rect<uint16_t> rect_,
SpriteAtlas::SpriteAtlas(Size size_, float pixelRatio_)
: size(std::move(size_)),
pixelRatio(pixelRatio_),
- observer(&nullObserver),
bin(size.width, size.height),
dirty(true) {
}
SpriteAtlas::~SpriteAtlas() = default;
-void SpriteAtlas::load(const std::string& url, Scheduler& scheduler, FileSource& fileSource) {
- if (url.empty()) {
- // Treat a non-existent sprite as a successfully loaded empty sprite.
- markAsLoaded();
- return;
- }
-
- loader = std::make_unique<Loader>(scheduler, *this);
-
- 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;
- }
-
- loader->worker.invoke(&SpriteAtlasWorker::parse, loader->image, loader->json);
- // TODO: delete the loader?
-}
-
-void SpriteAtlas::onParsed(Images&& result) {
+void SpriteAtlas::onSpriteLoaded(Images&& result) {
markAsLoaded();
+
for (auto& pair : result) {
addImage(pair.first, std::move(pair.second));
}
- observer->onSpriteLoaded();
+
for (auto requestor : requestors) {
requestor->onIconsAvailable(buildIconMap());
}
requestors.clear();
}
-void SpriteAtlas::onError(std::exception_ptr err) {
- observer->onSpriteError(err);
-}
-
-void SpriteAtlas::setObserver(SpriteAtlasObserver* observer_) {
- observer = observer_;
-}
-
-void SpriteAtlas::dumpDebugLogs() const {
- Log::Info(Event::General, "SpriteAtlas::loaded: %d", loaded);
-}
-
void SpriteAtlas::addImage(const std::string& id, std::unique_ptr<style::Image> image_) {
icons.clear();
@@ -152,10 +66,7 @@ void SpriteAtlas::addImage(const std::string& id, std::unique_ptr<style::Image>
Entry& entry = it->second;
// There is already a sprite with that name in our store.
- if (entry.image->image.size != image_->image.size) {
- Log::Warning(Event::Sprite, "Can't change sprite dimensions for '%s'", id.c_str());
- return;
- }
+ assert(entry.image->getImage().size == image_->getImage().size);
entry.image = std::move(image_);
@@ -172,9 +83,7 @@ void SpriteAtlas::removeImage(const std::string& id) {
icons.clear();
auto it = entries.find(id);
- if (it == entries.end()) {
- return;
- }
+ assert(it != entries.end());
Entry& entry = it->second;
@@ -243,8 +152,8 @@ optional<SpriteAtlasElement> SpriteAtlas::getImage(const std::string& id,
};
}
- const uint16_t pixelWidth = std::ceil(entry.image->image.size.width / pixelRatio);
- const uint16_t pixelHeight = std::ceil(entry.image->image.size.height / pixelRatio);
+ const uint16_t pixelWidth = std::ceil(entry.image->getImage().size.width / pixelRatio);
+ const uint16_t pixelHeight = std::ceil(entry.image->getImage().size.height / pixelRatio);
// Increase to next number divisible by 4, but at least 1.
// This is so we can scale down the texture coordinates and pack them
@@ -279,7 +188,7 @@ void SpriteAtlas::copy(const Entry& entry, optional<Rect<uint16_t>> Entry::*entr
image.fill(0);
}
- const PremultipliedImage& src = entry.image->image;
+ const PremultipliedImage& src = entry.image->getImage();
const Rect<uint16_t>& rect = *(entry.*entryRect);
const uint32_t padding = 1;
@@ -337,4 +246,8 @@ void SpriteAtlas::bind(bool linear, gl::Context& context, gl::TextureUnit unit)
linear ? gl::TextureFilter::Linear : gl::TextureFilter::Nearest);
}
+void SpriteAtlas::dumpDebugLogs() const {
+ Log::Info(Event::General, "SpriteAtlas::loaded: %d", loaded);
+}
+
} // namespace mbgl
diff --git a/src/mbgl/sprite/sprite_atlas.hpp b/src/mbgl/sprite/sprite_atlas.hpp
index 5d99677726..7ae583dcba 100644
--- a/src/mbgl/sprite/sprite_atlas.hpp
+++ b/src/mbgl/sprite/sprite_atlas.hpp
@@ -7,7 +7,6 @@
#include <mbgl/style/image.hpp>
#include <string>
-#include <map>
#include <set>
#include <unordered_map>
#include <array>
@@ -15,10 +14,6 @@
namespace mbgl {
-class Scheduler;
-class FileSource;
-class SpriteAtlasObserver;
-
namespace gl {
class Context;
} // namespace gl
@@ -38,7 +33,7 @@ public:
float height;
};
-using IconMap = std::map<std::string, SpriteAtlasElement>;
+using IconMap = std::unordered_map<std::string, SpriteAtlasElement>;
using IconDependencies = std::set<std::string>;
class IconRequestor {
@@ -48,12 +43,12 @@ public:
class SpriteAtlas : public util::noncopyable {
public:
- using Images = std::map<std::string, std::unique_ptr<style::Image>>;
+ using Images = std::unordered_map<std::string, std::unique_ptr<style::Image>>;
SpriteAtlas(Size, float pixelRatio);
~SpriteAtlas();
- void load(const std::string& url, Scheduler&, FileSource&);
+ void onSpriteLoaded(Images&&);
void markAsLoaded() {
loaded = true;
@@ -65,8 +60,6 @@ public:
void dumpDebugLogs() const;
- void setObserver(SpriteAtlasObserver*);
-
const style::Image* getImage(const std::string&) const;
void addImage(const std::string&, std::unique_ptr<style::Image>);
void removeImage(const std::string&);
@@ -93,25 +86,12 @@ public:
}
private:
- void emitSpriteLoadedIfComplete();
-
- // Invoked by SpriteAtlasWorker
- friend class SpriteAtlasWorker;
- void onParsed(Images&& result);
- void onError(std::exception_ptr);
-
const Size size;
const float pixelRatio;
-
- struct Loader;
- std::unique_ptr<Loader> loader;
-
bool loaded = false;
- SpriteAtlasObserver* observer = nullptr;
-
struct Entry {
- std::unique_ptr<style::Image> image;
+ std::unique_ptr<const style::Image> image;
// One sprite image might be used as both an icon image and a pattern image. If so,
// it must have two distinct entries in the texture. The one for the icon image has
diff --git a/src/mbgl/sprite/sprite_atlas_observer.hpp b/src/mbgl/sprite/sprite_atlas_observer.hpp
deleted file mode 100644
index 263c95b0e8..0000000000
--- a/src/mbgl/sprite/sprite_atlas_observer.hpp
+++ /dev/null
@@ -1,15 +0,0 @@
-#pragma once
-
-#include <exception>
-
-namespace mbgl {
-
-class SpriteAtlasObserver {
-public:
- virtual ~SpriteAtlasObserver() = default;
-
- virtual void onSpriteLoaded() {}
- virtual void onSpriteError(std::exception_ptr) {}
-};
-
-} // namespace mbgl
diff --git a/src/mbgl/sprite/sprite_image_collection.cpp b/src/mbgl/sprite/sprite_image_collection.cpp
new file mode 100644
index 0000000000..787ba83db2
--- /dev/null
+++ b/src/mbgl/sprite/sprite_image_collection.cpp
@@ -0,0 +1,40 @@
+#include <mbgl/sprite/sprite_image_collection.hpp>
+#include <mbgl/util/logging.hpp>
+
+namespace mbgl {
+
+void addSpriteImage(Images& images, const std::string& id,
+ std::unique_ptr<style::Image> image_,
+ std::function<void (style::Image&)> onAdded) {
+
+ auto it = images.find(id);
+ if (it == images.end()) {
+ // Add new
+ it = images.emplace(id, std::move(image_)).first;
+ onAdded(*it->second.get());
+ return;
+ }
+
+ std::unique_ptr<style::Image>& image = it->second;
+
+ // There is already a sprite with that name in our store.
+ if (image->getImage().size != image_->getImage().size) {
+ Log::Warning(Event::Sprite, "Can't change sprite dimensions for '%s'", id.c_str());
+ }
+
+ // Update existing
+ image = std::move(image_);
+ onAdded(*it->second.get());
+}
+
+void removeSpriteImage(Images& images,
+ const std::string& id,
+ std::function<void ()> onRemoved) {
+ if (images.erase(id) > 0) {
+ onRemoved();
+ }
+}
+
+
+
+} // namespace mbgl
diff --git a/src/mbgl/sprite/sprite_image_collection.hpp b/src/mbgl/sprite/sprite_image_collection.hpp
new file mode 100644
index 0000000000..00e252f74f
--- /dev/null
+++ b/src/mbgl/sprite/sprite_image_collection.hpp
@@ -0,0 +1,25 @@
+#pragma once
+
+#include <mbgl/style/image.hpp>
+
+#include <functional>
+#include <memory>
+#include <string>
+#include <unordered_map>
+
+namespace mbgl {
+
+using Images = std::unordered_map<std::string, std::unique_ptr<style::Image>>;
+
+void addSpriteImage(Images&,
+ const std::string&,
+ std::unique_ptr<style::Image>,
+ std::function<void (style::Image&)> onAdded = [] (style::Image&){});
+
+void removeSpriteImage(Images&,
+ const std::string&,
+ std::function<void ()> onRemoved = [] (){});
+
+
+
+} // namespace mbgl
diff --git a/src/mbgl/sprite/sprite_loader.cpp b/src/mbgl/sprite/sprite_loader.cpp
new file mode 100644
index 0000000000..86325bba35
--- /dev/null
+++ b/src/mbgl/sprite/sprite_loader.cpp
@@ -0,0 +1,107 @@
+#include <mbgl/sprite/sprite_loader.hpp>
+#include <mbgl/sprite/sprite_loader_worker.hpp>
+#include <mbgl/sprite/sprite_loader_observer.hpp>
+#include <mbgl/sprite/sprite_parser.hpp>
+#include <mbgl/util/logging.hpp>
+#include <mbgl/util/platform.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 <mbgl/util/run_loop.hpp>
+#include <mbgl/actor/actor.hpp>
+
+#include <cassert>
+
+namespace mbgl {
+
+static SpriteLoaderObserver nullObserver;
+
+struct SpriteLoader::Loader {
+ Loader(Scheduler& scheduler, SpriteLoader& spriteAtlas)
+ : mailbox(std::make_shared<Mailbox>(*util::RunLoop::Get())),
+ worker(scheduler, ActorRef<SpriteLoader>(spriteAtlas, mailbox)) {
+ }
+
+ std::shared_ptr<const std::string> image;
+ std::shared_ptr<const std::string> json;
+ std::unique_ptr<AsyncRequest> jsonRequest;
+ std::unique_ptr<AsyncRequest> spriteRequest;
+ std::shared_ptr<Mailbox> mailbox;
+ Actor<SpriteLoaderWorker> worker;
+};
+
+SpriteLoader::SpriteLoader(float pixelRatio_)
+ : pixelRatio(pixelRatio_)
+ , observer(&nullObserver) {
+}
+
+SpriteLoader::~SpriteLoader() = default;
+
+void SpriteLoader::load(const std::string& url, Scheduler& scheduler, FileSource& fileSource) {
+ if (url.empty()) {
+ // Treat a non-existent sprite as a successfully loaded empty sprite.
+ observer->onSpriteLoaded({});
+ return;
+ }
+
+ loader = std::make_unique<Loader>(scheduler, *this);
+
+ 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 SpriteLoader::emitSpriteLoadedIfComplete() {
+ assert(loader);
+
+ if (!loader->image || !loader->json) {
+ return;
+ }
+
+ loader->worker.invoke(&SpriteLoaderWorker::parse, loader->image, loader->json);
+}
+
+void SpriteLoader::onParsed(Images&& result) {
+
+
+
+ observer->onSpriteLoaded(std::move(result));
+}
+
+void SpriteLoader::onError(std::exception_ptr err) {
+ observer->onSpriteError(err);
+}
+
+void SpriteLoader::setObserver(SpriteLoaderObserver* observer_) {
+ observer = observer_;
+}
+
+} // namespace mbgl
diff --git a/src/mbgl/sprite/sprite_loader.hpp b/src/mbgl/sprite/sprite_loader.hpp
new file mode 100644
index 0000000000..dbebede03b
--- /dev/null
+++ b/src/mbgl/sprite/sprite_loader.hpp
@@ -0,0 +1,46 @@
+#pragma once
+
+#include <mbgl/util/noncopyable.hpp>
+#include <mbgl/style/image.hpp>
+
+#include <string>
+#include <map>
+#include <set>
+#include <unordered_map>
+#include <array>
+#include <memory>
+
+namespace mbgl {
+
+class Scheduler;
+class FileSource;
+class SpriteLoaderObserver;
+
+class SpriteLoader : public util::noncopyable {
+public:
+ using Images = std::unordered_map<std::string, std::unique_ptr<style::Image>>;
+
+ SpriteLoader(float pixelRatio);
+ ~SpriteLoader();
+
+ void load(const std::string& url, Scheduler&, FileSource&);
+
+ void setObserver(SpriteLoaderObserver*);
+
+private:
+ void emitSpriteLoadedIfComplete();
+
+ // Invoked by SpriteAtlasWorker
+ friend class SpriteLoaderWorker;
+ void onParsed(Images&& result);
+ void onError(std::exception_ptr);
+
+ const float pixelRatio;
+
+ struct Loader;
+ std::unique_ptr<Loader> loader;
+
+ SpriteLoaderObserver* observer = nullptr;
+};
+
+} // namespace mbgl
diff --git a/src/mbgl/sprite/sprite_loader_observer.hpp b/src/mbgl/sprite/sprite_loader_observer.hpp
new file mode 100644
index 0000000000..632c48ea56
--- /dev/null
+++ b/src/mbgl/sprite/sprite_loader_observer.hpp
@@ -0,0 +1,24 @@
+#pragma once
+
+#include <exception>
+#include <memory>
+#include <unordered_map>
+#include <string>
+
+namespace mbgl {
+
+namespace style {
+class Image;
+} // namespace style
+
+class SpriteLoaderObserver {
+public:
+ using Images = std::unordered_map<std::string, std::unique_ptr<style::Image>>;
+
+ virtual ~SpriteLoaderObserver() = default;
+
+ virtual void onSpriteLoaded(Images&&) {}
+ virtual void onSpriteError(std::exception_ptr) {}
+};
+
+} // namespace mbgl
diff --git a/src/mbgl/sprite/sprite_atlas_worker.cpp b/src/mbgl/sprite/sprite_loader_worker.cpp
index da507aabab..4bded33d53 100644
--- a/src/mbgl/sprite/sprite_atlas_worker.cpp
+++ b/src/mbgl/sprite/sprite_loader_worker.cpp
@@ -1,14 +1,14 @@
-#include <mbgl/sprite/sprite_atlas_worker.hpp>
-#include <mbgl/sprite/sprite_atlas.hpp>
+#include <mbgl/sprite/sprite_loader_worker.hpp>
+#include <mbgl/sprite/sprite_loader.hpp>
#include <mbgl/sprite/sprite_parser.hpp>
namespace mbgl {
-SpriteAtlasWorker::SpriteAtlasWorker(ActorRef<SpriteAtlasWorker>, ActorRef<SpriteAtlas> parent_)
+SpriteLoaderWorker::SpriteLoaderWorker(ActorRef<SpriteLoaderWorker>, ActorRef<SpriteLoader> parent_)
: parent(std::move(parent_)) {
}
-void SpriteAtlasWorker::parse(std::shared_ptr<const std::string> image,
+void SpriteLoaderWorker::parse(std::shared_ptr<const std::string> image,
std::shared_ptr<const std::string> json) {
try {
if (!image) {
@@ -20,9 +20,9 @@ void SpriteAtlasWorker::parse(std::shared_ptr<const std::string> image,
throw std::runtime_error("missing sprite metadata");
}
- parent.invoke(&SpriteAtlas::onParsed, parseSprite(*image, *json));
+ parent.invoke(&SpriteLoader::onParsed, parseSprite(*image, *json));
} catch (...) {
- parent.invoke(&SpriteAtlas::onError, std::current_exception());
+ parent.invoke(&SpriteLoader::onError, std::current_exception());
}
}
diff --git a/src/mbgl/sprite/sprite_atlas_worker.hpp b/src/mbgl/sprite/sprite_loader_worker.hpp
index 70ca0d52e5..d61e07d14f 100644
--- a/src/mbgl/sprite/sprite_atlas_worker.hpp
+++ b/src/mbgl/sprite/sprite_loader_worker.hpp
@@ -8,16 +8,16 @@
namespace mbgl {
-class SpriteAtlas;
+class SpriteLoader;
-class SpriteAtlasWorker {
+class SpriteLoaderWorker {
public:
- SpriteAtlasWorker(ActorRef<SpriteAtlasWorker>, ActorRef<SpriteAtlas>);
+ SpriteLoaderWorker(ActorRef<SpriteLoaderWorker>, ActorRef<SpriteLoader>);
void parse(std::shared_ptr<const std::string> image, std::shared_ptr<const std::string> json);
private:
- ActorRef<SpriteAtlas> parent;
+ ActorRef<SpriteLoader> parent;
};
} // namespace mbgl
diff --git a/src/mbgl/sprite/sprite_parser.hpp b/src/mbgl/sprite/sprite_parser.hpp
index 5be8435ebb..175ec8a883 100644
--- a/src/mbgl/sprite/sprite_parser.hpp
+++ b/src/mbgl/sprite/sprite_parser.hpp
@@ -7,7 +7,7 @@
#include <string>
#include <memory>
-#include <map>
+#include <unordered_map>
namespace mbgl {
@@ -24,7 +24,7 @@ std::unique_ptr<style::Image> createStyleImage(const PremultipliedImage&,
double ratio,
bool sdf);
-using Images = std::map<std::string, std::unique_ptr<style::Image>>;
+using Images = std::unordered_map<std::string, std::unique_ptr<style::Image>>;
// Parses an image and an associated JSON file and returns the sprite objects.
Images parseSprite(const std::string& image, const std::string& json);
diff --git a/src/mbgl/style/image.cpp b/src/mbgl/style/image.cpp
index 4c0c6a859b..c5fecec2b2 100644
--- a/src/mbgl/style/image.cpp
+++ b/src/mbgl/style/image.cpp
@@ -1,21 +1,35 @@
#include <mbgl/style/image.hpp>
+#include <mbgl/style/image_impl.hpp>
#include <mbgl/util/exception.hpp>
namespace mbgl {
namespace style {
-Image::Image(PremultipliedImage&& image_,
- const float pixelRatio_,
- bool sdf_)
- : image(std::move(image_)),
- pixelRatio(pixelRatio_),
- sdf(sdf_) {
-
- if (!image.valid()) {
- throw util::SpriteImageException("Sprite image dimensions may not be zero");
- } else if (pixelRatio <= 0) {
- throw util::SpriteImageException("Sprite pixelRatio may not be <= 0");
- }
+Image::Image(PremultipliedImage &&image,
+ const float pixelRatio,
+ bool sdf)
+ : impl(std::make_shared<Impl>(std::move(image), pixelRatio, sdf)) {
+}
+
+PremultipliedImage& Image::getImage() const {
+ assert(impl);
+ return impl->image;
+}
+
+bool Image::isSdf() const {
+ return impl->sdf;
+}
+
+float Image::getPixelRatio() const {
+ return impl->pixelRatio;
+}
+
+float Image::getWidth() const {
+ return impl->getWidth();
+}
+
+float Image::getHeight() const {
+ return impl->getHeight();
}
} // namespace style
diff --git a/src/mbgl/style/image_impl.cpp b/src/mbgl/style/image_impl.cpp
new file mode 100644
index 0000000000..910bffa905
--- /dev/null
+++ b/src/mbgl/style/image_impl.cpp
@@ -0,0 +1,22 @@
+#include <mbgl/style/image_impl.hpp>
+#include <mbgl/util/exception.hpp>
+
+namespace mbgl {
+namespace style {
+
+Image::Impl::Impl(PremultipliedImage&& image_,
+ const float pixelRatio_,
+ bool sdf_)
+ : image(std::move(image_)),
+ pixelRatio(pixelRatio_),
+ sdf(sdf_) {
+
+ if (!image.valid()) {
+ throw util::SpriteImageException("Sprite image dimensions may not be zero");
+ } else if (pixelRatio <= 0) {
+ throw util::SpriteImageException("Sprite pixelRatio may not be <= 0");
+ }
+}
+
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/image_impl.hpp b/src/mbgl/style/image_impl.hpp
new file mode 100644
index 0000000000..dce4a6e4c0
--- /dev/null
+++ b/src/mbgl/style/image_impl.hpp
@@ -0,0 +1,25 @@
+#pragma once
+
+#include <mbgl/style/image.hpp>
+
+namespace mbgl {
+namespace style {
+
+class Image::Impl {
+public:
+ Impl(PremultipliedImage&&, float pixelRatio, bool sdf = false);
+
+ PremultipliedImage image;
+
+ // Pixel ratio of the sprite image.
+ const float pixelRatio;
+
+ // Whether this image should be interpreted as a signed distance field icon.
+ const bool sdf;
+
+ float getWidth() const { return image.size.width / pixelRatio; }
+ float getHeight() const { return image.size.height / pixelRatio; }
+};
+
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/layers/symbol_layer_impl.hpp b/src/mbgl/style/layers/symbol_layer_impl.hpp
index 5e04834583..a20d001e0e 100644
--- a/src/mbgl/style/layers/symbol_layer_impl.hpp
+++ b/src/mbgl/style/layers/symbol_layer_impl.hpp
@@ -1,6 +1,5 @@
#pragma once
-#include <mbgl/sprite/sprite_atlas.hpp>
#include <mbgl/style/layer_impl.hpp>
#include <mbgl/style/layers/symbol_layer.hpp>
#include <mbgl/style/layers/symbol_layer_properties.hpp>
diff --git a/src/mbgl/style/observer.hpp b/src/mbgl/style/observer.hpp
index 60432334e2..77d97685be 100644
--- a/src/mbgl/style/observer.hpp
+++ b/src/mbgl/style/observer.hpp
@@ -1,7 +1,7 @@
#pragma once
#include <mbgl/text/glyph_atlas_observer.hpp>
-#include <mbgl/sprite/sprite_atlas_observer.hpp>
+#include <mbgl/sprite/sprite_loader_observer.hpp>
#include <mbgl/style/source_observer.hpp>
#include <mbgl/map/update.hpp>
@@ -11,7 +11,7 @@ namespace mbgl {
namespace style {
class Observer : public GlyphAtlasObserver,
- public SpriteAtlasObserver,
+ public SpriteLoaderObserver,
public SourceObserver {
public:
virtual void onUpdate(Update) {}
diff --git a/src/mbgl/style/style.cpp b/src/mbgl/style/style.cpp
index 6af876d740..0dcb110443 100644
--- a/src/mbgl/style/style.cpp
+++ b/src/mbgl/style/style.cpp
@@ -17,6 +17,8 @@
#include <mbgl/style/transition_options.hpp>
#include <mbgl/style/class_dictionary.hpp>
#include <mbgl/sprite/sprite_atlas.hpp>
+#include <mbgl/sprite/sprite_image_collection.hpp>
+#include <mbgl/sprite/sprite_loader.hpp>
#include <mbgl/text/glyph_atlas.hpp>
#include <mbgl/geometry/line_atlas.hpp>
#include <mbgl/renderer/update_parameters.hpp>
@@ -72,13 +74,14 @@ Style::Style(Scheduler& scheduler_, FileSource& fileSource_, float pixelRatio)
: scheduler(scheduler_),
fileSource(fileSource_),
glyphAtlas(std::make_unique<GlyphAtlas>(Size{ 2048, 2048 }, fileSource)),
+ spriteLoader(std::make_unique<SpriteLoader>(pixelRatio)),
spriteAtlas(std::make_unique<SpriteAtlas>(Size{ 1024, 1024 }, pixelRatio)),
lineAtlas(std::make_unique<LineAtlas>(Size{ 256, 512 })),
light(std::make_unique<Light>()),
renderLight(light->impl),
observer(&nullObserver) {
glyphAtlas->setObserver(this);
- spriteAtlas->setObserver(this);
+ spriteLoader->setObserver(this);
light->setObserver(this);
}
@@ -160,7 +163,7 @@ void Style::setJSON(const std::string& json) {
setLight(std::make_unique<Light>(parser.light));
glyphAtlas->setURL(parser.glyphURL);
- spriteAtlas->load(parser.spriteURL, scheduler, fileSource);
+ spriteLoader->load(parser.spriteURL, scheduler, fileSource);
loaded = true;
@@ -539,6 +542,24 @@ bool Style::isLoaded() const {
return true;
}
+void Style::addImage(const std::string& id, std::unique_ptr<style::Image> image) {
+ addSpriteImage(spriteImages, id, std::move(image), [&](style::Image& added) {
+ spriteAtlas->addImage(id, std::make_unique<style::Image>(added));
+ observer->onUpdate(Update::Repaint);
+ });
+}
+
+void Style::removeImage(const std::string& id) {
+ removeSpriteImage(spriteImages, id, [&] () {
+ spriteAtlas->removeImage(id);
+ observer->onUpdate(Update::Repaint);
+ });
+}
+
+const style::Image* Style::getImage(const std::string& id) const {
+ return spriteAtlas->getImage(id);
+}
+
RenderData Style::getRenderData(MapDebugOptions debugOptions, float angle) const {
RenderData result;
@@ -755,8 +776,20 @@ void Style::onTileError(RenderSource& source, const OverscaledTileID& tileID, st
observer->onResourceError(error);
}
-void Style::onSpriteLoaded() {
- observer->onSpriteLoaded();
+void Style::onSpriteLoaded(SpriteLoader::Images&& images) {
+ // Add images to collection
+ Images addedImages;
+ for (auto& entry : images) {
+ addSpriteImage(spriteImages, entry.first, std::move(entry.second), [&] (style::Image& added) {
+ addedImages.emplace(entry.first, std::make_unique<Image>(added));
+ });
+ }
+
+ // Update render sprite atlas
+ spriteAtlas->onSpriteLoaded(std::move(addedImages));
+
+ // Update observer
+ observer->onSpriteLoaded(std::move(images));
observer->onUpdate(Update::Repaint); // For *-pattern properties.
}
diff --git a/src/mbgl/style/style.hpp b/src/mbgl/style/style.hpp
index 04d3a11d55..5953d823d9 100644
--- a/src/mbgl/style/style.hpp
+++ b/src/mbgl/style/style.hpp
@@ -11,7 +11,7 @@
#include <mbgl/renderer/render_layer.hpp>
#include <mbgl/renderer/render_light.hpp>
#include <mbgl/text/glyph_atlas_observer.hpp>
-#include <mbgl/sprite/sprite_atlas_observer.hpp>
+#include <mbgl/sprite/sprite_loader_observer.hpp>
#include <mbgl/map/mode.hpp>
#include <mbgl/map/zoom_history.hpp>
@@ -31,6 +31,7 @@ namespace mbgl {
class FileSource;
class GlyphAtlas;
class SpriteAtlas;
+class SpriteLoader;
class LineAtlas;
class RenderData;
class TransformState;
@@ -44,7 +45,7 @@ class Layer;
class QueryParameters;
class Style : public GlyphAtlasObserver,
- public SpriteAtlasObserver,
+ public SpriteLoaderObserver,
public SourceObserver,
public RenderSourceObserver,
public LayerObserver,
@@ -106,6 +107,10 @@ public:
Light* getLight() const;
const RenderLight& getRenderLight() const;
+ const style::Image* getImage(const std::string&) const;
+ void addImage(const std::string&, std::unique_ptr<style::Image>);
+ void removeImage(const std::string&);
+
RenderData getRenderData(MapDebugOptions, float angle) const;
std::vector<Feature> queryRenderedFeatures(const ScreenLineString& geometry,
@@ -120,6 +125,7 @@ public:
Scheduler& scheduler;
FileSource& fileSource;
std::unique_ptr<GlyphAtlas> glyphAtlas;
+ std::unique_ptr<SpriteLoader> spriteLoader;
std::unique_ptr<SpriteAtlas> spriteAtlas;
std::unique_ptr<LineAtlas> lineAtlas;
@@ -152,8 +158,9 @@ private:
void onGlyphsLoaded(const FontStack&, const GlyphRange&) override;
void onGlyphsError(const FontStack&, const GlyphRange&, std::exception_ptr) override;
- // SpriteStoreObserver implementation.
- void onSpriteLoaded() override;
+ // SpriteLoaderObserver implementation.
+ std::unordered_map<std::string, std::unique_ptr<style::Image>> spriteImages;
+ void onSpriteLoaded(SpriteLoaderObserver::Images&&) override;
void onSpriteError(std::exception_ptr) override;
// SourceObserver implementation.
diff --git a/test/map/map.test.cpp b/test/map/map.test.cpp
index c24f736fcd..6809fefb65 100644
--- a/test/map/map.test.cpp
+++ b/test/map/map.test.cpp
@@ -512,7 +512,7 @@ TEST(Map, GetImage) {
map.setStyleJSON(util::read_file("test/fixtures/api/icon_style.json"));
map.addImage("test-icon", std::move(image));
- test::checkImage("test/fixtures/map/get_icon", map.getImage("test-icon")->image);
+ test::checkImage("test/fixtures/map/get_icon", map.getImage("test-icon")->getImage());
}
TEST(Map, DontLoadUnneededTiles) {
diff --git a/test/sprite/sprite_atlas.test.cpp b/test/sprite/sprite_atlas.test.cpp
index 08388f0a93..0cbd211de8 100644
--- a/test/sprite/sprite_atlas.test.cpp
+++ b/test/sprite/sprite_atlas.test.cpp
@@ -4,7 +4,6 @@
#include <mbgl/test/stub_style_observer.hpp>
#include <mbgl/sprite/sprite_atlas.hpp>
-#include <mbgl/sprite/sprite_atlas.hpp>
#include <mbgl/sprite/sprite_parser.hpp>
#include <mbgl/util/io.hpp>
#include <mbgl/util/image.hpp>
@@ -185,189 +184,3 @@ TEST(SpriteAtlas, RemoveReleasesBinPackRect) {
EXPECT_TRUE(atlas.getIcon("big"));
EXPECT_TRUE(log.empty());
}
-
-TEST(SpriteAtlas, OtherPixelRatio) {
- FixtureLog log;
- SpriteAtlas atlas({ 32, 32 }, 1);
-
- // Adding mismatched sprite image
- atlas.addImage("one", std::make_unique<style::Image>(PremultipliedImage({ 8, 8 }), 2));
-}
-
-TEST(SpriteAtlas, Replace) {
- FixtureLog log;
- SpriteAtlas atlas({ 32, 32 }, 1);
-
- atlas.addImage("sprite", std::make_unique<style::Image>(PremultipliedImage({ 16, 16 }), 2));
- auto image = atlas.getImage("sprite");
- atlas.addImage("sprite", std::make_unique<style::Image>(PremultipliedImage({ 16, 16 }), 2));
- EXPECT_NE(image, atlas.getImage("sprite"));
-}
-
-TEST(SpriteAtlas, ReplaceWithDifferentDimensions) {
- FixtureLog log;
- SpriteAtlas atlas({ 32, 32 }, 1);
-
- atlas.addImage("sprite", std::make_unique<style::Image>(PremultipliedImage({ 16, 16 }), 2));
- atlas.addImage("sprite", std::make_unique<style::Image>(PremultipliedImage({ 18, 18 }), 2));
-
- EXPECT_EQ(1u, log.count({
- EventSeverity::Warning,
- Event::Sprite,
- int64_t(-1),
- "Can't change sprite dimensions for 'sprite'",
- }));
-}
-
-class SpriteAtlasTest {
-public:
- SpriteAtlasTest() = default;
-
- util::RunLoop loop;
- StubFileSource fileSource;
- StubStyleObserver observer;
- ThreadPool threadPool { 1 };
- 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", threadPool, 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_unique<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_unique<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_unique<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_image_collection.test.cpp b/test/sprite/sprite_image_collection.test.cpp
new file mode 100644
index 0000000000..51ea20fdf3
--- /dev/null
+++ b/test/sprite/sprite_image_collection.test.cpp
@@ -0,0 +1,45 @@
+#include <mbgl/test/util.hpp>
+#include <mbgl/test/fixture_log_observer.hpp>
+
+#include <mbgl/sprite/sprite_image_collection.hpp>
+#include <mbgl/util/image.hpp>
+#include <mbgl/util/string.hpp>
+
+#include <utility>
+
+using namespace mbgl;
+
+TEST(SpriteImageCollection, OtherPixelRatio) {
+ FixtureLog log;
+ Images images;
+
+ // Adding mismatched sprite image
+ addSpriteImage(images, "one", std::make_unique<style::Image>(PremultipliedImage({ 8, 8 }), 2));
+}
+
+TEST(SpriteImageCollection, Replace) {
+ FixtureLog log;
+ Images images;
+
+ addSpriteImage(images, "sprite", std::make_unique<style::Image>(PremultipliedImage({ 16, 16 }), 2));
+ auto image = images.find("sprite")->second.get();
+ addSpriteImage(images, "sprite", std::make_unique<style::Image>(PremultipliedImage({ 16, 16 }), 2));
+ EXPECT_NE(image, images.find("sprite")->second.get());
+}
+
+TEST(SpriteImageCollection, ReplaceWithDifferentDimensions) {
+ FixtureLog log;
+ Images images;
+
+ addSpriteImage(images, "sprite", std::make_unique<style::Image>(PremultipliedImage({ 16, 16 }), 2));
+ addSpriteImage(images, "sprite", std::make_unique<style::Image>(PremultipliedImage({ 18, 18 }), 2));
+
+ EXPECT_EQ(1u, log.count({
+ EventSeverity::Warning,
+ Event::Sprite,
+ int64_t(-1),
+ "Can't change sprite dimensions for 'sprite'",
+ }));
+}
+
+
diff --git a/test/sprite/sprite_loader.test.cpp b/test/sprite/sprite_loader.test.cpp
new file mode 100644
index 0000000000..06018cc390
--- /dev/null
+++ b/test/sprite/sprite_loader.test.cpp
@@ -0,0 +1,164 @@
+#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_loader.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/default_thread_pool.hpp>
+#include <mbgl/util/string.hpp>
+
+#include <utility>
+
+using namespace mbgl;
+
+class SpriteLoaderTest {
+public:
+ SpriteLoaderTest() = default;
+
+ util::RunLoop loop;
+ StubFileSource fileSource;
+ StubStyleObserver observer;
+ ThreadPool threadPool { 1 };
+ SpriteLoader spriteLoader{ 1 };
+
+ void run() {
+ // Squelch logging.
+ Log::setObserver(std::make_unique<Log::NullObserver>());
+
+ spriteLoader.setObserver(&observer);
+ spriteLoader.load("test/fixtures/resources/sprite", threadPool, 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_unique<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_unique<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_unique<std::string>("CORRUPT");
+ return response;
+}
+
+TEST(SpriteLoader, LoadingSuccess) {
+ SpriteLoaderTest 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 = [&] (SpriteLoaderObserver::Images&& images) {
+ EXPECT_EQ(images.size(), Images::size_type(367));
+ test.end();
+ };
+
+ test.run();
+}
+
+TEST(SpriteLoader, JSONLoadingFail) {
+ SpriteLoaderTest 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));
+ test.end();
+ };
+
+ test.run();
+}
+
+TEST(SpriteLoader, ImageLoadingFail) {
+ SpriteLoaderTest 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));
+ test.end();
+ };
+
+ test.run();
+}
+
+TEST(SpriteLoader, JSONLoadingCorrupted) {
+ SpriteLoaderTest 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));
+ test.end();
+ };
+
+ test.run();
+}
+
+TEST(SpriteLoader, ImageLoadingCorrupted) {
+ SpriteLoaderTest 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.
+ test.end();
+ };
+
+ test.run();
+}
+
+TEST(SpriteLoader, LoadingCancel) {
+ SpriteLoaderTest test;
+
+ test.fileSource.spriteImageResponse =
+ test.fileSource.spriteJSONResponse = [&] (const Resource&) {
+ test.end();
+ return optional<Response>();
+ };
+
+ test.observer.spriteLoaded = [&] (const SpriteLoaderObserver::Images&) {
+ FAIL() << "Should never be called";
+ };
+
+ test.run();
+}
diff --git a/test/sprite/sprite_parser.test.cpp b/test/sprite/sprite_parser.test.cpp
index bb8e71db95..19c6b914a8 100644
--- a/test/sprite/sprite_parser.test.cpp
+++ b/test/sprite/sprite_parser.test.cpp
@@ -145,11 +145,11 @@ TEST(Sprite, SpriteImageCreation1x) {
ASSERT_TRUE(sprite.get());
EXPECT_EQ(18, sprite->getWidth());
EXPECT_EQ(18, sprite->getHeight());
- EXPECT_EQ(18u, sprite->image.size.width);
- EXPECT_EQ(18u, sprite->image.size.height);
- EXPECT_EQ(1, sprite->pixelRatio);
+ EXPECT_EQ(18u, sprite->getImage().size.width);
+ EXPECT_EQ(18u, sprite->getImage().size.height);
+ EXPECT_EQ(1, sprite->getPixelRatio());
EXPECT_EQ(readImage("test/fixtures/annotations/result-spriteimagecreation1x-museum.png"),
- sprite->image);
+ sprite->getImage());
}
}
@@ -161,11 +161,11 @@ TEST(Sprite, SpriteImageCreation2x) {
ASSERT_TRUE(sprite.get());
EXPECT_EQ(18, sprite->getWidth());
EXPECT_EQ(18, sprite->getHeight());
- EXPECT_EQ(36u, sprite->image.size.width);
- EXPECT_EQ(36u, sprite->image.size.height);
- EXPECT_EQ(2, sprite->pixelRatio);
+ EXPECT_EQ(36u, sprite->getImage().size.width);
+ EXPECT_EQ(36u, sprite->getImage().size.height);
+ EXPECT_EQ(2, sprite->getPixelRatio());
EXPECT_EQ(readImage("test/fixtures/annotations/result-spriteimagecreation2x.png"),
- sprite->image);
+ sprite->getImage());
}
TEST(Sprite, SpriteImageCreation1_5x) {
@@ -176,22 +176,22 @@ TEST(Sprite, SpriteImageCreation1_5x) {
ASSERT_TRUE(sprite.get());
EXPECT_EQ(24, sprite->getWidth());
EXPECT_EQ(24, sprite->getHeight());
- EXPECT_EQ(36u, sprite->image.size.width);
- EXPECT_EQ(36u, sprite->image.size.height);
- EXPECT_EQ(1.5, sprite->pixelRatio);
+ EXPECT_EQ(36u, sprite->getImage().size.width);
+ EXPECT_EQ(36u, sprite->getImage().size.height);
+ EXPECT_EQ(1.5, sprite->getPixelRatio());
EXPECT_EQ(readImage("test/fixtures/annotations/result-spriteimagecreation1_5x-museum.png"),
- sprite->image);
+ sprite->getImage());
// "hospital_icon":{"x":314,"y":518,"width":36,"height":36,"pixelRatio":2,"sdf":false}
const auto sprite2 = createStyleImage(image_2x, 314, 518, 35, 35, 1.5, false);
ASSERT_TRUE(sprite2.get());
EXPECT_EQ(float(35 / 1.5), sprite2->getWidth());
EXPECT_EQ(float(35 / 1.5), sprite2->getHeight());
- EXPECT_EQ(35u, sprite2->image.size.width);
- EXPECT_EQ(35u, sprite2->image.size.height);
- EXPECT_EQ(1.5, sprite2->pixelRatio);
+ EXPECT_EQ(35u, sprite2->getImage().size.width);
+ EXPECT_EQ(35u, sprite2->getImage().size.height);
+ EXPECT_EQ(1.5, sprite2->getPixelRatio());
EXPECT_EQ(readImage("test/fixtures/annotations/result-spriteimagecreation1_5x-hospital.png"),
- sprite2->image);
+ sprite2->getImage());
}
TEST(Sprite, SpriteParsing) {
@@ -283,10 +283,10 @@ TEST(Sprite, SpriteParsing) {
auto& sprite = images.find("generic-metro")->second;
EXPECT_EQ(18, sprite->getWidth());
EXPECT_EQ(18, sprite->getHeight());
- EXPECT_EQ(18u, sprite->image.size.width);
- EXPECT_EQ(18u, sprite->image.size.height);
- EXPECT_EQ(1, sprite->pixelRatio);
- EXPECT_EQ(readImage("test/fixtures/annotations/result-spriteparsing.png"), sprite->image);
+ EXPECT_EQ(18u, sprite->getImage().size.width);
+ EXPECT_EQ(18u, sprite->getImage().size.height);
+ EXPECT_EQ(1, sprite->getPixelRatio());
+ EXPECT_EQ(readImage("test/fixtures/annotations/result-spriteparsing.png"), sprite->getImage());
}
}
diff --git a/test/src/mbgl/test/stub_style_observer.hpp b/test/src/mbgl/test/stub_style_observer.hpp
index 7e22c68823..a3a48b04c0 100644
--- a/test/src/mbgl/test/stub_style_observer.hpp
+++ b/test/src/mbgl/test/stub_style_observer.hpp
@@ -18,8 +18,8 @@ public:
if (glyphsError) glyphsError(fontStack, glyphRange, error);
}
- void onSpriteLoaded() override {
- if (spriteLoaded) spriteLoaded();
+ void onSpriteLoaded(Images&& images) override {
+ if (spriteLoaded) spriteLoaded(std::move(images));
}
void onSpriteError(std::exception_ptr error) override {
@@ -48,7 +48,7 @@ public:
std::function<void (const FontStack&, const GlyphRange&)> glyphsLoaded;
std::function<void (const FontStack&, const GlyphRange&, std::exception_ptr)> glyphsError;
- std::function<void ()> spriteLoaded;
+ std::function<void (Images&&)> spriteLoaded;
std::function<void (std::exception_ptr)> spriteError;
std::function<void (Source&)> sourceLoaded;
std::function<void (Source&)> sourceChanged;
diff --git a/test/style/style_image.test.cpp b/test/style/style_image.test.cpp
index 319120df83..34814bee88 100644
--- a/test/style/style_image.test.cpp
+++ b/test/style/style_image.test.cpp
@@ -36,17 +36,17 @@ TEST(StyleImage, ZeroRatio) {
TEST(StyleImage, Retina) {
style::Image image(PremultipliedImage({ 32, 24 }), 2.0);
EXPECT_EQ(16, image.getWidth());
- EXPECT_EQ(32u, image.image.size.width);
+ EXPECT_EQ(32u, image.getImage().size.width);
EXPECT_EQ(12, image.getHeight());
- EXPECT_EQ(24u, image.image.size.height);
- EXPECT_EQ(2, image.pixelRatio);
+ EXPECT_EQ(24u, image.getImage().size.height);
+ EXPECT_EQ(2, image.getPixelRatio());
}
TEST(StyleImage, FractionalRatio) {
style::Image image(PremultipliedImage({ 20, 12 }), 1.5);
EXPECT_EQ(float(20.0 / 1.5), image.getWidth());
- EXPECT_EQ(20u, image.image.size.width);
+ EXPECT_EQ(20u, image.getImage().size.width);
EXPECT_EQ(float(12.0 / 1.5), image.getHeight());
- EXPECT_EQ(12u, image.image.size.height);
- EXPECT_EQ(1.5, image.pixelRatio);
+ EXPECT_EQ(12u, image.getImage().size.height);
+ EXPECT_EQ(1.5, image.getPixelRatio());
}