[core] Merge Sprite into SpriteStore

This commit is contained in:
John Firebaugh
2015-11-05 14:54:50 -08:00
parent 1e350b7ea4
commit f9ebe54a63
19 changed files with 364 additions and 436 deletions

View File

@@ -168,7 +168,7 @@ std::unique_ptr<Bucket> SymbolLayer::createBucket(StyleBucketParameters& paramet
bucket->parseFeatures(parameters.layer, filter);
if (bucket->needsDependencies(parameters.glyphStore, parameters.sprite)) {
if (bucket->needsDependencies(parameters.glyphStore, parameters.spriteStore)) {
parameters.partialParse = true;
}

View File

@@ -169,7 +169,7 @@ void MapContext::update() {
data.setAnimationTime(Clock::now());
if (style->sprite && updateFlags & Update::Annotations) {
if (updateFlags & Update::Annotations) {
data.getAnnotationManager()->updateStyle(*style);
updateFlags |= Update::Classes;
}

View File

@@ -21,7 +21,6 @@ TileWorker::TileWorker(TileID id_,
sourceID(sourceID_),
style(style_),
state(state_) {
assert(style.sprite);
}
TileWorker::~TileWorker() {
@@ -64,7 +63,7 @@ TileParseResult TileWorker::parsePendingLayers() {
if (layer.type == StyleLayerType::Symbol) {
auto symbolBucket = dynamic_cast<SymbolBucket*>(bucket.get());
if (!symbolBucket->needsDependencies(*style.glyphStore, *style.sprite)) {
if (!symbolBucket->needsDependencies(*style.glyphStore, *style.spriteStore)) {
symbolBucket->addFeatures(reinterpret_cast<uintptr_t>(this), *style.spriteAtlas,
*style.glyphAtlas, *style.glyphStore, *collisionTile);
insertBucket(layer.bucketName(), std::move(bucket));
@@ -130,7 +129,7 @@ void TileWorker::parseLayer(const StyleLayer& layer, const GeometryTile& geometr
reinterpret_cast<uintptr_t>(this),
partialParse,
*style.spriteAtlas,
*style.sprite,
*style.spriteStore,
*style.glyphAtlas,
*style.glyphStore,
*collisionTile);

View File

@@ -1,8 +1,8 @@
#include <mbgl/renderer/symbol_bucket.hpp>
#include <mbgl/layer/symbol_layer.hpp>
#include <mbgl/map/geometry_tile.hpp>
#include <mbgl/sprite/sprite.hpp>
#include <mbgl/sprite/sprite_image.hpp>
#include <mbgl/sprite/sprite_store.hpp>
#include <mbgl/sprite/sprite_atlas.hpp>
#include <mbgl/geometry/text_buffer.hpp>
#include <mbgl/geometry/icon_buffer.hpp>
@@ -158,13 +158,12 @@ void SymbolBucket::parseFeatures(const GeometryTileLayer& layer,
}
}
bool SymbolBucket::needsDependencies(GlyphStore& glyphStore,
Sprite& sprite) {
bool SymbolBucket::needsDependencies(GlyphStore& glyphStore, SpriteStore& spriteStore) {
if (!layout.text.field.value.empty() && !layout.text.font.value.empty() && !glyphStore.hasGlyphRanges(layout.text.font, ranges)) {
return true;
}
if (!layout.icon.image.value.empty() && !sprite.isLoaded()) {
if (!layout.icon.image.value.empty() && !spriteStore.isLoaded()) {
return true;
}

View File

@@ -28,7 +28,7 @@ class CollisionBoxShader;
class DotShader;
class CollisionTile;
class SpriteAtlas;
class Sprite;
class SpriteStore;
class GlyphAtlas;
class GlyphStore;
@@ -88,8 +88,7 @@ public:
void parseFeatures(const GeometryTileLayer&,
const FilterExpression&);
bool needsDependencies(GlyphStore& glyphStore,
Sprite& sprite);
bool needsDependencies(GlyphStore&, SpriteStore&);
void placeFeatures(CollisionTile&) override;
private:

View File

@@ -1,119 +0,0 @@
#include <mbgl/sprite/sprite.hpp>
#include <mbgl/platform/log.hpp>
#include <mbgl/platform/platform.hpp>
#include <mbgl/storage/file_source.hpp>
#include <mbgl/storage/resource.hpp>
#include <mbgl/storage/response.hpp>
#include <mbgl/storage/request_holder.hpp>
#include <mbgl/util/exception.hpp>
#include <mbgl/util/raster.hpp>
#include <mbgl/util/thread.hpp>
#include <mbgl/util/uv_detail.hpp>
#include <mbgl/util/mapbox.hpp>
#include <rapidjson/document.h>
#include <string>
#include <sstream>
namespace mbgl {
struct Sprite::Loader {
std::shared_ptr<const std::string> image;
std::shared_ptr<const std::string> json;
RequestHolder jsonRequest;
RequestHolder spriteRequest;
};
Sprite::Sprite(const std::string& baseUrl, float pixelRatio_)
: pixelRatio(pixelRatio_ > 1 ? 2 : 1) {
if (baseUrl.empty()) {
// Treat a non-existent sprite as a successfully loaded empty sprite.
loaded = true;
return;
}
std::string spriteURL(baseUrl + (pixelRatio_ > 1 ? "@2x" : "") + ".png");
std::string jsonURL(baseUrl + (pixelRatio_ > 1 ? "@2x" : "") + ".json");
loader = std::make_unique<Loader>();
FileSource* fs = util::ThreadContext::getFileSource();
loader->jsonRequest = fs->request({ Resource::Kind::SpriteJSON, jsonURL }, util::RunLoop::getLoop(),
[this, jsonURL](const Response& res) {
if (res.stale) {
// Only handle fresh responses.
return;
}
loader->jsonRequest = nullptr;
if (res.error) {
std::stringstream message;
message << "Failed to load [" << jsonURL << "]: " << res.error->message;
emitSpriteLoadingFailed(message.str());
return;
} else {
loader->json = res.data;
}
emitSpriteLoadedIfComplete();
});
loader->spriteRequest =
fs->request({ Resource::Kind::SpriteImage, spriteURL }, util::RunLoop::getLoop(),
[this, spriteURL](const Response& res) {
if (res.stale) {
// Only handle fresh responses.
return;
}
loader->spriteRequest = nullptr;
if (res.error) {
std::stringstream message;
message << "Failed to load [" << spriteURL << "]: " << res.error->message;
emitSpriteLoadingFailed(message.str());
return;
} else {
loader->image = res.data;
}
emitSpriteLoadedIfComplete();
});
}
Sprite::~Sprite() {
}
void Sprite::emitSpriteLoadedIfComplete() {
assert(loader);
if (!loader->image || !loader->json || !observer) {
return;
}
auto local = std::move(loader);
auto result = parseSprite(*local->image, *local->json);
if (result.is<Sprites>()) {
loaded = true;
observer->onSpriteLoaded(result.get<Sprites>());
} else {
emitSpriteLoadingFailed(result.get<std::string>());
}
}
void Sprite::emitSpriteLoadingFailed(const std::string& message) {
if (!observer) {
return;
}
auto error = std::make_exception_ptr(util::SpriteLoadingException(message));
observer->onSpriteLoadingFailed(error);
}
void Sprite::setObserver(Observer* observer_) {
observer = observer_;
}
void Sprite::dumpDebugLogs() const {
Log::Info(Event::General, "Sprite::loaded: %d", loaded);
}
} // namespace mbgl

View File

@@ -1,57 +0,0 @@
#ifndef MBGL_SPRITE
#define MBGL_SPRITE
#include <mbgl/sprite/sprite_parser.hpp>
#include <mbgl/util/image.hpp>
#include <mbgl/util/noncopyable.hpp>
#include <mbgl/util/ptr.hpp>
#include <mbgl/storage/request.hpp>
#include <cstdint>
#include <atomic>
#include <iosfwd>
#include <string>
#include <unordered_map>
namespace mbgl {
class Request;
class Sprite : private util::noncopyable {
public:
class Observer {
public:
virtual ~Observer() = default;
virtual void onSpriteLoaded(const Sprites& sprites) = 0;
virtual void onSpriteLoadingFailed(std::exception_ptr error) = 0;
};
Sprite(const std::string& baseUrl, float pixelRatio);
~Sprite();
inline bool isLoaded() const {
return loaded;
}
void dumpDebugLogs() const;
const float pixelRatio;
void setObserver(Observer* observer);
private:
void emitSpriteLoadedIfComplete();
void emitSpriteLoadingFailed(const std::string& message);
struct Loader;
std::unique_ptr<Loader> loader;
bool loaded = false;
Observer* observer = nullptr;
};
} // namespace mbgl
#endif

View File

@@ -10,13 +10,10 @@
#include <mbgl/util/scaling.hpp>
#include <mbgl/util/thread_context.hpp>
#include <mbgl/sprite/sprite.hpp>
#include <cassert>
#include <cmath>
#include <algorithm>
using namespace mbgl;
SpriteAtlas::SpriteAtlas(dimension width_, dimension height_, float pixelRatio_, SpriteStore& store_)

View File

@@ -1,9 +1,122 @@
#include <mbgl/sprite/sprite_store.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/storage/request_holder.hpp>
#include <mbgl/util/exception.hpp>
#include <mbgl/util/thread_context.hpp>
#include <mbgl/util/run_loop.hpp>
#include <string>
#include <sstream>
namespace mbgl {
struct SpriteStore::Loader {
std::shared_ptr<const std::string> image;
std::shared_ptr<const std::string> json;
RequestHolder jsonRequest;
RequestHolder spriteRequest;
};
SpriteStore::SpriteStore(float pixelRatio_)
: pixelRatio(pixelRatio_ > 1 ? 2 : 1) {
}
SpriteStore::~SpriteStore() = default;
void SpriteStore::setURL(const std::string& url) {
if (url.empty()) {
// Treat a non-existent sprite as a successfully loaded empty sprite.
loaded = true;
return;
}
std::string spriteURL(url + (pixelRatio > 1 ? "@2x" : "") + ".png");
std::string jsonURL(url + (pixelRatio > 1 ? "@2x" : "") + ".json");
loader = std::make_unique<Loader>();
FileSource* fs = util::ThreadContext::getFileSource();
loader->jsonRequest = fs->request({ Resource::Kind::SpriteJSON, jsonURL }, util::RunLoop::getLoop(),
[this, jsonURL](const Response& res) {
if (res.stale) {
// Only handle fresh responses.
return;
}
loader->jsonRequest = nullptr;
if (res.error) {
std::stringstream message;
message << "Failed to load [" << jsonURL << "]: " << res.error->message;
emitSpriteLoadingFailed(message.str());
return;
} else {
loader->json = res.data;
}
emitSpriteLoadedIfComplete();
});
loader->spriteRequest =
fs->request({ Resource::Kind::SpriteImage, spriteURL }, util::RunLoop::getLoop(),
[this, spriteURL](const Response& res) {
if (res.stale) {
// Only handle fresh responses.
return;
}
loader->spriteRequest = nullptr;
if (res.error) {
std::stringstream message;
message << "Failed to load [" << spriteURL << "]: " << res.error->message;
emitSpriteLoadingFailed(message.str());
return;
} else {
loader->image = res.data;
}
emitSpriteLoadedIfComplete();
});
}
void SpriteStore::emitSpriteLoadedIfComplete() {
assert(loader);
if (!loader->image || !loader->json) {
return;
}
auto local = std::move(loader);
auto result = parseSprite(*local->image, *local->json);
if (result.is<Sprites>()) {
loaded = true;
setSprites(result.get<Sprites>());
if (observer) {
observer->onSpriteLoaded();
}
} else {
emitSpriteLoadingFailed(result.get<std::string>());
}
}
void SpriteStore::emitSpriteLoadingFailed(const std::string& message) {
if (!observer) {
return;
}
auto error = std::make_exception_ptr(util::SpriteLoadingException(message));
observer->onSpriteLoadingFailed(error);
}
void SpriteStore::setObserver(Observer* 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);

View File

@@ -2,24 +2,39 @@
#define MBGL_SPRITE_STORE
#include <mbgl/sprite/sprite_image.hpp>
#include <mbgl/util/noncopyable.hpp>
#include <mbgl/util/geo.hpp>
#include <map>
#include <set>
#include <vector>
#include <memory>
#include <mutex>
#include <cstdint>
namespace mbgl {
// The SpriteStore object holds Sprite images.
class SpriteStore : private util::noncopyable {
public:
using Sprites = std::map<std::string, std::shared_ptr<const SpriteImage>>;
public:
class Observer {
public:
virtual ~Observer() = default;
virtual void onSpriteLoaded() = 0;
virtual void onSpriteLoadingFailed(std::exception_ptr) = 0;
};
SpriteStore(float pixelRatio);
~SpriteStore();
void setURL(const std::string&);
bool isLoaded() const {
return loaded;
}
void dumpDebugLogs() const;
void setObserver(Observer* observer);
// Adds/replaces a Sprite image.
void setSprite(const std::string&, std::shared_ptr<const SpriteImage> = nullptr);
@@ -35,9 +50,21 @@ public:
// 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();
void emitSpriteLoadingFailed(const std::string& message);
struct Loader;
std::unique_ptr<Loader> loader;
bool loaded = false;
Observer* observer = nullptr;
// Lock for sprites and dirty maps.
std::mutex mutex;

View File

@@ -2,7 +2,6 @@
#include <mbgl/map/map_data.hpp>
#include <mbgl/map/source.hpp>
#include <mbgl/map/transform_state.hpp>
#include <mbgl/sprite/sprite.hpp>
#include <mbgl/sprite/sprite_store.hpp>
#include <mbgl/sprite/sprite_atlas.hpp>
#include <mbgl/style/style_layer.hpp>
@@ -28,12 +27,13 @@ Style::Style(MapData& data_)
: data(data_),
glyphStore(std::make_unique<GlyphStore>()),
glyphAtlas(std::make_unique<GlyphAtlas>(1024, 1024)),
spriteStore(std::make_unique<SpriteStore>()),
spriteStore(std::make_unique<SpriteStore>(data.pixelRatio)),
spriteAtlas(std::make_unique<SpriteAtlas>(512, 512, data.pixelRatio, *spriteStore)),
lineAtlas(std::make_unique<LineAtlas>(512, 512)),
mtx(std::make_unique<uv::rwlock>()),
workers(4) {
glyphStore->setObserver(this);
spriteStore->setObserver(this);
}
void Style::setJSON(const std::string& json, const std::string&) {
@@ -55,10 +55,8 @@ void Style::setJSON(const std::string& json, const std::string&) {
addLayer(std::move(layer));
}
sprite = std::make_unique<Sprite>(parser.getSprite(), data.pixelRatio);
sprite->setObserver(this);
glyphStore->setURL(parser.getGlyphURL());
spriteStore->setURL(parser.getSpriteURL());
}
Style::~Style() {
@@ -67,10 +65,7 @@ Style::~Style() {
}
glyphStore->setObserver(nullptr);
if (sprite) {
sprite->setObserver(nullptr);
}
spriteStore->setObserver(nullptr);
}
void Style::addSource(std::unique_ptr<Source> source) {
@@ -186,7 +181,7 @@ bool Style::isLoaded() const {
}
}
if (sprite && !sprite->isLoaded()) {
if (!spriteStore->isLoaded()) {
return false;
}
@@ -230,10 +225,7 @@ void Style::onTileLoadingFailed(std::exception_ptr error) {
emitResourceLoadingFailed(error);
}
void Style::onSpriteLoaded(const Sprites& sprites) {
// Add all sprite images to the SpriteStore object
spriteStore->setSprites(sprites);
void Style::onSpriteLoaded() {
shouldReparsePartialTiles = true;
emitTileDataChanged();
}
@@ -272,11 +264,7 @@ void Style::dumpDebugLogs() const {
source->dumpDebugLogs();
}
if (!sprite) {
Log::Info(Event::General, "no sprite loaded");
} else {
sprite->dumpDebugLogs();
}
spriteStore->dumpDebugLogs();
}
}

View File

@@ -5,10 +5,9 @@
#include <mbgl/style/zoom_history.hpp>
#include <mbgl/map/source.hpp>
#include <mbgl/sprite/sprite.hpp>
#include <mbgl/text/glyph_store.hpp>
#include <mbgl/sprite/sprite_store.hpp>
#include <mbgl/util/ptr.hpp>
#include <mbgl/util/noncopyable.hpp>
#include <mbgl/util/chrono.hpp>
#include <mbgl/util/worker.hpp>
@@ -27,8 +26,8 @@ class LineAtlas;
class StyleLayer;
class Style : public GlyphStore::Observer,
public SpriteStore::Observer,
public Source::Observer,
public Sprite::Observer,
public util::noncopyable {
public:
Style(MapData&);
@@ -74,7 +73,6 @@ public:
MapData& data;
std::unique_ptr<GlyphStore> glyphStore;
std::unique_ptr<GlyphAtlas> glyphAtlas;
util::ptr<Sprite> sprite;
std::unique_ptr<SpriteStore> spriteStore;
std::unique_ptr<SpriteAtlas> spriteAtlas;
std::unique_ptr<LineAtlas> lineAtlas;
@@ -89,16 +87,16 @@ private:
void onGlyphRangeLoaded() override;
void onGlyphRangeLoadingFailed(std::exception_ptr error) override;
// SpriteStore::Observer implementation.
void onSpriteLoaded() override;
void onSpriteLoadingFailed(std::exception_ptr error) override;
// Source::Observer implementation.
void onSourceLoaded() override;
void onSourceLoadingFailed(std::exception_ptr error) override;
void onTileLoaded(bool isNewTile) override;
void onTileLoadingFailed(std::exception_ptr error) override;
// Sprite::Observer implementation.
void onSpriteLoaded(const Sprites& sprites) override;
void onSpriteLoadingFailed(std::exception_ptr error) override;
void emitTileDataChanged();
void emitResourceLoadingFailed(std::exception_ptr error);

View File

@@ -12,7 +12,7 @@ class TileID;
class GeometryTileLayer;
class GeometryTileFeature;
class SpriteAtlas;
class Sprite;
class SpriteStore;
class GlyphAtlas;
class GlyphStore;
class CollisionTile;
@@ -25,7 +25,7 @@ public:
uintptr_t tileUID_,
bool& partialParse_,
SpriteAtlas& spriteAtlas_,
Sprite& sprite_,
SpriteStore& spriteStore_,
GlyphAtlas& glyphAtlas_,
GlyphStore& glyphStore_,
CollisionTile& collisionTile_)
@@ -35,7 +35,7 @@ public:
tileUID(tileUID_),
partialParse(partialParse_),
spriteAtlas(spriteAtlas_),
sprite(sprite_),
spriteStore(spriteStore_),
glyphAtlas(glyphAtlas_),
glyphStore(glyphStore_),
collisionTile(collisionTile_) {}
@@ -52,7 +52,7 @@ public:
uintptr_t tileUID;
bool& partialParse;
SpriteAtlas& spriteAtlas;
Sprite& sprite;
SpriteStore& spriteStore;
GlyphAtlas& glyphAtlas;
GlyphStore& glyphStore;
CollisionTile& collisionTile;

View File

@@ -24,11 +24,17 @@ void StyleParser::parse(const JSVal& document) {
}
if (document.HasMember("sprite")) {
parseSprite(document["sprite"]);
const JSVal& sprite = document["sprite"];
if (sprite.IsString()) {
spriteURL = { sprite.GetString(), sprite.GetStringLength() };
}
}
if (document.HasMember("glyphs")) {
parseGlyphURL(document["glyphs"]);
const JSVal& glyphs = document["glyphs"];
if (glyphs.IsString()) {
glyphURL = { glyphs.GetString(), glyphs.GetStringLength() };
}
}
}
@@ -266,18 +272,6 @@ void StyleParser::parseLayer(const std::string& id, const JSVal& value, util::pt
layer->parsePaints(value);
}
void StyleParser::parseSprite(const JSVal& value) {
if (value.IsString()) {
sprite = { value.GetString(), value.GetStringLength() };
}
}
void StyleParser::parseGlyphURL(const JSVal& value) {
if (value.IsString()) {
glyph_url = { value.GetString(), value.GetStringLength() };
}
}
void StyleParser::parseVisibility(StyleLayer& layer, const JSVal& value) {
if (!value.HasMember("visibility")) {
return;

View File

@@ -31,20 +31,18 @@ public:
return layers;
}
std::string getSprite() const {
return sprite;
std::string getSpriteURL() const {
return spriteURL;
}
std::string getGlyphURL() const {
return glyph_url;
return glyphURL;
}
private:
void parseSources(const JSVal&);
void parseLayers(const JSVal&);
void parseLayer(const std::string& id, const JSVal&, util::ptr<StyleLayer>&);
void parseSprite(const JSVal&);
void parseGlyphURL(const JSVal&);
void parseVisibility(StyleLayer&, const JSVal& value);
std::uint8_t version;
@@ -58,11 +56,8 @@ private:
// Store a stack of layer IDs we're parsing right now. This is to prevent reference cycles.
std::forward_list<std::string> stack;
// Base URL of the sprite image.
std::string sprite;
// URL template for glyph PBFs.
std::string glyph_url;
std::string spriteURL;
std::string glyphURL;
};
}

View File

@@ -1,173 +0,0 @@
#include "../fixtures/fixture_log_observer.hpp"
#include "../fixtures/mock_file_source.hpp"
#include "../fixtures/util.hpp"
#include <mbgl/sprite/sprite.hpp>
#include <mbgl/util/run_loop.hpp>
#include <mbgl/util/thread.hpp>
using namespace mbgl;
using SpriteTestCallback = std::function<void(Sprite*, const Sprites&, std::exception_ptr)>;
struct SpriteParams {
const std::string baseUrl;
const float pixelRatio;
};
class SpriteThread : public Sprite::Observer {
public:
SpriteThread(FileSource* fileSource, SpriteTestCallback callback) : callback_(callback) {
util::ThreadContext::setFileSource(fileSource);
}
void loadSprite(const SpriteParams& params) {
sprite_.reset(new Sprite(params.baseUrl, params.pixelRatio));
sprite_->setObserver(this);
}
void unloadSprite() {
sprite_->setObserver(nullptr);
sprite_.reset();
}
void onSpriteLoaded(const Sprites& sprites) override {
callback_(sprite_.get(), sprites, nullptr);
}
void onSpriteLoadingFailed(std::exception_ptr error) override {
callback_(sprite_.get(), Sprites(), error);
}
private:
std::unique_ptr<Sprite> sprite_;
SpriteTestCallback callback_;
};
class SpriteTest : public testing::Test {
protected:
void runTest(const SpriteParams& params, FileSource* fileSource, SpriteTestCallback callback) {
util::RunLoop loop(uv_default_loop());
async_ = std::make_unique<uv::async>(loop.get(), [&] { loop.stop(); });
async_->unref();
const util::ThreadContext context = {"Map", util::ThreadType::Map, util::ThreadPriority::Regular};
util::Thread<SpriteThread> tester(context, fileSource, callback);
tester.invoke(&SpriteThread::loadSprite, params);
uv_run(loop.get(), UV_RUN_DEFAULT);
tester.invoke(&SpriteThread::unloadSprite);
}
void stopTest() {
async_->send();
}
private:
std::unique_ptr<uv::async> async_;
};
TEST_F(SpriteTest, LoadingSuccess) {
SpriteParams params = {
"test/fixtures/resources/sprite",
1.0,
};
auto callback = [this, &params](Sprite* sprite, const Sprites& sprites, std::exception_ptr error) {
ASSERT_TRUE(util::ThreadContext::currentlyOn(util::ThreadType::Map));
ASSERT_TRUE(error == nullptr);
ASSERT_TRUE(!sprites.empty());
ASSERT_EQ(sprite->pixelRatio, params.pixelRatio);
ASSERT_NE(sprite->pixelRatio, 1.5);
ASSERT_NE(sprite->pixelRatio, 2.0);
ASSERT_TRUE(sprite->isLoaded());
stopTest();
};
MockFileSource fileSource(MockFileSource::Success, "");
runTest(params, &fileSource, callback);
}
TEST_F(SpriteTest, LoadingFail) {
SpriteParams params = {
"test/fixtures/resources/sprite",
1.0,
};
auto callback = [this, &params](Sprite* sprite, const Sprites&, std::exception_ptr error) {
ASSERT_TRUE(util::ThreadContext::currentlyOn(util::ThreadType::Map));
ASSERT_TRUE(error != nullptr);
ASSERT_EQ(sprite->pixelRatio, params.pixelRatio);
ASSERT_NE(sprite->pixelRatio, 1.5);
ASSERT_NE(sprite->pixelRatio, 2.0);
ASSERT_FALSE(sprite->isLoaded());
stopTest();
};
MockFileSource fileSourceFailSpriteJSON(MockFileSource::RequestFail, "sprite.json");
runTest(params, &fileSourceFailSpriteJSON, callback);
MockFileSource fileSourceFailSpriteImage(MockFileSource::RequestFail, "sprite.png");
runTest(params, &fileSourceFailSpriteImage, callback);
MockFileSource fileSourceCorruptedSpriteJSON(MockFileSource::RequestWithCorruptedData, "sprite.json");
runTest(params, &fileSourceCorruptedSpriteJSON, callback);
MockFileSource fileSourceCorruptedSpriteImage(MockFileSource::RequestWithCorruptedData, "sprite.png");
runTest(params, &fileSourceCorruptedSpriteImage, callback);
}
TEST_F(SpriteTest, LoadingCancel) {
SpriteParams params = {
"test/fixtures/resources/sprite",
1.0,
};
auto callback = [this](Sprite*, const Sprites&, std::exception_ptr) {
FAIL() << "Should never be called";
};
MockFileSource fileSourceDelaySpriteJSON(MockFileSource::SuccessWithDelay, "sprite.json");
fileSourceDelaySpriteJSON.setOnRequestDelayedCallback([this]{
stopTest();
});
runTest(params, &fileSourceDelaySpriteJSON, callback);
MockFileSource fileSourceDelaySpriteImage(MockFileSource::SuccessWithDelay, "sprite.png");
fileSourceDelaySpriteImage.setOnRequestDelayedCallback([this]{
stopTest();
});
runTest(params, &fileSourceDelaySpriteImage, callback);
}
TEST_F(SpriteTest, InvalidURL) {
SpriteParams params = {
"foo bar",
1.0,
};
auto callback = [this](Sprite* sprite, const Sprites&, std::exception_ptr error) {
ASSERT_TRUE(util::ThreadContext::currentlyOn(util::ThreadType::Map));
ASSERT_TRUE(error != nullptr);
ASSERT_EQ(sprite->isLoaded(), false);
stopTest();
};
MockFileSource fileSource(MockFileSource::Success, "");
runTest(params, &fileSource, callback);
}

View File

@@ -15,7 +15,7 @@ TEST(Sprite, SpriteAtlas) {
auto spriteParseResult = parseSprite(util::read_file("test/fixtures/annotations/emerald.png"),
util::read_file("test/fixtures/annotations/emerald.json"));
SpriteStore store;
SpriteStore store(1);
store.setSprites(spriteParseResult.get<Sprites>());
SpriteAtlas atlas(63, 112, 1, store);
@@ -90,7 +90,7 @@ TEST(Sprite, SpriteAtlasSize) {
auto spriteParseResult = parseSprite(util::read_file("test/fixtures/annotations/emerald.png"),
util::read_file("test/fixtures/annotations/emerald.json"));
SpriteStore store;
SpriteStore store(1);
store.setSprites(spriteParseResult.get<Sprites>());
SpriteAtlas atlas(63, 112, 1.4, store);
@@ -124,7 +124,7 @@ TEST(Sprite, SpriteAtlasSize) {
}
TEST(Sprite, SpriteAtlasUpdates) {
SpriteStore store;
SpriteStore store(1);
SpriteAtlas atlas(32, 32, 1, store);

View File

@@ -1,8 +1,12 @@
#include "../fixtures/util.hpp"
#include "../fixtures/fixture_log_observer.hpp"
#include "../fixtures/mock_file_source.hpp"
#include <mbgl/sprite/sprite_store.hpp>
#include <mbgl/util/run_loop.hpp>
#include <mbgl/util/thread.hpp>
using namespace mbgl;
TEST(Sprite, SpriteStore) {
@@ -13,7 +17,7 @@ TEST(Sprite, SpriteStore) {
const auto sprite3 = std::make_shared<SpriteImage>(8, 8, 2, std::string(16 * 16 * 4, '\0'));
using Sprites = std::map<std::string, std::shared_ptr<const SpriteImage>>;
SpriteStore store;
SpriteStore store(1);
// Adding single
store.setSprite("one", sprite1);
@@ -77,7 +81,7 @@ TEST(Sprite, SpriteStoreOtherPixelRatio) {
const auto sprite1 = std::make_shared<SpriteImage>(8, 8, 1, std::string(8 * 8 * 4, '\0'));
using Sprites = std::map<std::string, std::shared_ptr<const SpriteImage>>;
SpriteStore store;
SpriteStore store(1);
// Adding mismatched sprite image
store.setSprite("one", sprite1);
@@ -89,7 +93,7 @@ TEST(Sprite, SpriteStoreMultiple) {
const auto sprite2 = std::make_shared<SpriteImage>(8, 8, 2, std::string(16 * 16 * 4, '\0'));
using Sprites = std::map<std::string, std::shared_ptr<const SpriteImage>>;
SpriteStore store;
SpriteStore store(1);
store.setSprites({
{ "one", sprite1 }, { "two", sprite2 },
@@ -108,7 +112,7 @@ TEST(Sprite, SpriteStoreReplace) {
const auto sprite2 = std::make_shared<SpriteImage>(8, 8, 2, std::string(16 * 16 * 4, '\0'));
using Sprites = std::map<std::string, std::shared_ptr<const SpriteImage>>;
SpriteStore store;
SpriteStore store(1);
store.setSprite("sprite", sprite1);
EXPECT_EQ(sprite1, store.getSprite("sprite"));
@@ -125,7 +129,7 @@ TEST(Sprite, SpriteStoreReplaceWithDifferentDimensions) {
const auto sprite2 = std::make_shared<SpriteImage>(9, 9, 2, std::string(18 * 18 * 4, '\0'));
using Sprites = std::map<std::string, std::shared_ptr<const SpriteImage>>;
SpriteStore store;
SpriteStore store(1);
store.setSprite("sprite", sprite1);
store.setSprite("sprite", sprite2);
@@ -141,3 +145,168 @@ TEST(Sprite, SpriteStoreReplaceWithDifferentDimensions) {
EXPECT_EQ(Sprites({ { "sprite", sprite1 } }), store.getDirty());
}
using SpriteTestCallback = std::function<void(SpriteStore*, std::exception_ptr)>;
struct SpriteParams {
const std::string baseUrl;
const float pixelRatio;
};
class SpriteThread : public SpriteStore::Observer {
public:
SpriteThread(FileSource* fileSource, SpriteTestCallback callback) : callback_(callback) {
util::ThreadContext::setFileSource(fileSource);
}
void loadSprite(const SpriteParams& params) {
spriteStore_.reset(new SpriteStore(params.pixelRatio));
spriteStore_->setObserver(this);
spriteStore_->setURL(params.baseUrl);
}
void unloadSprite() {
spriteStore_->setObserver(nullptr);
spriteStore_.reset();
}
void onSpriteLoaded() override {
callback_(spriteStore_.get(), nullptr);
}
void onSpriteLoadingFailed(std::exception_ptr error) override {
callback_(spriteStore_.get(), error);
}
private:
std::unique_ptr<SpriteStore> spriteStore_;
SpriteTestCallback callback_;
};
class SpriteTest : public testing::Test {
protected:
void runTest(const SpriteParams& params, FileSource* fileSource, SpriteTestCallback callback) {
util::RunLoop loop(uv_default_loop());
async_ = std::make_unique<uv::async>(loop.get(), [&] { loop.stop(); });
async_->unref();
const util::ThreadContext context = {"Map", util::ThreadType::Map, util::ThreadPriority::Regular};
util::Thread<SpriteThread> tester(context, fileSource, callback);
tester.invoke(&SpriteThread::loadSprite, params);
uv_run(loop.get(), UV_RUN_DEFAULT);
tester.invoke(&SpriteThread::unloadSprite);
}
void stopTest() {
async_->send();
}
private:
std::unique_ptr<uv::async> async_;
};
TEST_F(SpriteTest, LoadingSuccess) {
SpriteParams params = {
"test/fixtures/resources/sprite",
1.0,
};
auto callback = [this, &params](SpriteStore* spriteStore, std::exception_ptr error) {
ASSERT_TRUE(util::ThreadContext::currentlyOn(util::ThreadType::Map));
ASSERT_TRUE(error == nullptr);
ASSERT_TRUE(!spriteStore->getDirty().empty());
ASSERT_EQ(spriteStore->pixelRatio, params.pixelRatio);
ASSERT_NE(spriteStore->pixelRatio, 1.5);
ASSERT_NE(spriteStore->pixelRatio, 2.0);
ASSERT_TRUE(spriteStore->isLoaded());
stopTest();
};
MockFileSource fileSource(MockFileSource::Success, "");
runTest(params, &fileSource, callback);
}
TEST_F(SpriteTest, LoadingFail) {
SpriteParams params = {
"test/fixtures/resources/sprite",
1.0,
};
auto callback = [this, &params](SpriteStore* spriteStore, std::exception_ptr error) {
ASSERT_TRUE(util::ThreadContext::currentlyOn(util::ThreadType::Map));
ASSERT_TRUE(error != nullptr);
ASSERT_EQ(spriteStore->pixelRatio, params.pixelRatio);
ASSERT_NE(spriteStore->pixelRatio, 1.5);
ASSERT_NE(spriteStore->pixelRatio, 2.0);
ASSERT_FALSE(spriteStore->isLoaded());
stopTest();
};
MockFileSource fileSourceFailSpriteJSON(MockFileSource::RequestFail, "sprite.json");
runTest(params, &fileSourceFailSpriteJSON, callback);
MockFileSource fileSourceFailSpriteImage(MockFileSource::RequestFail, "sprite.png");
runTest(params, &fileSourceFailSpriteImage, callback);
MockFileSource fileSourceCorruptedSpriteJSON(MockFileSource::RequestWithCorruptedData, "sprite.json");
runTest(params, &fileSourceCorruptedSpriteJSON, callback);
MockFileSource fileSourceCorruptedSpriteImage(MockFileSource::RequestWithCorruptedData, "sprite.png");
runTest(params, &fileSourceCorruptedSpriteImage, callback);
}
TEST_F(SpriteTest, LoadingCancel) {
SpriteParams params = {
"test/fixtures/resources/sprite",
1.0,
};
auto callback = [this](SpriteStore*, std::exception_ptr) {
FAIL() << "Should never be called";
};
MockFileSource fileSourceDelaySpriteJSON(MockFileSource::SuccessWithDelay, "sprite.json");
fileSourceDelaySpriteJSON.setOnRequestDelayedCallback([this]{
stopTest();
});
runTest(params, &fileSourceDelaySpriteJSON, callback);
MockFileSource fileSourceDelaySpriteImage(MockFileSource::SuccessWithDelay, "sprite.png");
fileSourceDelaySpriteImage.setOnRequestDelayedCallback([this]{
stopTest();
});
runTest(params, &fileSourceDelaySpriteImage, callback);
}
TEST_F(SpriteTest, InvalidURL) {
SpriteParams params = {
"foo bar",
1.0,
};
auto callback = [this](SpriteStore* spriteStore, std::exception_ptr error) {
ASSERT_TRUE(util::ThreadContext::currentlyOn(util::ThreadType::Map));
ASSERT_TRUE(error != nullptr);
ASSERT_EQ(spriteStore->isLoaded(), false);
stopTest();
};
MockFileSource fileSource(MockFileSource::Success, "");
runTest(params, &fileSource, callback);
}

View File

@@ -86,7 +86,6 @@
'style/pending_resources.cpp',
'style/resource_loading.cpp',
'sprite/sprite.cpp',
'sprite/sprite_atlas.cpp',
'sprite/sprite_image.cpp',
'sprite/sprite_parser.cpp',