#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace mbgl { namespace { SpriteLoaderObserver nullObserver; } struct SpriteLoader::Data { std::shared_ptr image; std::shared_ptr json; std::unique_ptr jsonRequest; std::unique_ptr spriteRequest; }; SpriteLoader::SpriteLoader(float pixelRatio_, const TaggedScheduler& threadPool_) : pixelRatio(pixelRatio_), observer(&nullObserver), threadPool(threadPool_) {} SpriteLoader::~SpriteLoader() = default; void SpriteLoader::load(const std::optional sprite, FileSource& fileSource) { if (!sprite) { // Treat a non-existent sprite as a successfully loaded empty sprite. observer->onSpriteLoaded(std::nullopt, {}); return; } observer->onSpriteRequested(sprite); std::string id = sprite->id; std::string url = sprite->spriteURL; dataMap[id] = std::make_unique(); dataMap[id]->jsonRequest = fileSource.request(Resource::spriteJSON(url, pixelRatio), [this, sprite](Response res) { std::lock_guard lock(dataMapMutex); Data* data = dataMap[sprite->id].get(); if (res.error) { observer->onSpriteError(sprite, std::make_exception_ptr(std::runtime_error(res.error->message))); } else if (res.notModified) { return; } else if (res.noContent) { data->json = std::make_shared(); emitSpriteLoadedIfComplete(*sprite); } else { // Only trigger a sprite loaded event we got new data. assert(data->json != res.data); data->json = std::move(res.data); emitSpriteLoadedIfComplete(*sprite); } }); dataMap[id]->spriteRequest = fileSource.request( Resource::spriteImage(url, pixelRatio), [this, sprite](Response res) { std::lock_guard lock(dataMapMutex); Data* data = dataMap[sprite->id].get(); if (res.error) { observer->onSpriteError(sprite, std::make_exception_ptr(std::runtime_error(res.error->message))); } else if (res.notModified) { return; } else if (res.noContent) { data->image = std::make_shared(); emitSpriteLoadedIfComplete(*sprite); } else { assert(dataMap[sprite->id]->image != res.data); data->image = std::move(res.data); emitSpriteLoadedIfComplete(*sprite); } }); } void SpriteLoader::emitSpriteLoadedIfComplete(style::Sprite sprite) { Data* data = dataMap[sprite.id].get(); assert(data); if (!data->image || !data->json) { return; } struct ParseResult { std::vector> images; std::exception_ptr error; }; threadPool.scheduleAndReplyValue( /* parseClosure */ [sprite = sprite, image = data->image, json = data->json]() -> ParseResult { try { return {.images = parseSprite(sprite.id, *image, *json), .error = nullptr}; } catch (...) { return {.images = {}, .error = std::current_exception()}; } }, /* resultClosure */ [this, sprite = sprite, factory = weakFactory.makeWeakPtr()](ParseResult result) { if (auto guard = factory.lock(); factory) { if (result.error) { observer->onSpriteError(std::optional(sprite), result.error); } else { observer->onSpriteLoaded(std::optional(sprite), std::move(result.images)); } } }); } void SpriteLoader::setObserver(SpriteLoaderObserver* observer_) { observer = observer_; } } // namespace mbgl