[core] Merge SpriteStore and SpriteAtlas

This commit is contained in:
John Firebaugh
2016-09-15 15:40:35 -07:00
parent 1014a503a2
commit 940124ff71
21 changed files with 507 additions and 619 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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;
};

View File

@@ -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;
}

View File

@@ -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&,

View File

@@ -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_)) {
}

View File

@@ -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;

View File

@@ -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) {}

View File

@@ -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

View File

@@ -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

View File

@@ -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;

View File

@@ -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) {}

View File

@@ -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

View File

@@ -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;

View File

@@ -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,

View File

@@ -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,

View File

@@ -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;

View File

@@ -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();
}

View File

@@ -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();
}

View File