[core] Split style image collection from SpriteAtlas
This commit is contained in:
committed by
John Firebaugh
parent
c80f3e9d29
commit
cc9f040a2d
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
40
src/mbgl/sprite/sprite_image_collection.cpp
Normal file
40
src/mbgl/sprite/sprite_image_collection.cpp
Normal file
@@ -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
|
||||
25
src/mbgl/sprite/sprite_image_collection.hpp
Normal file
25
src/mbgl/sprite/sprite_image_collection.hpp
Normal file
@@ -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
|
||||
107
src/mbgl/sprite/sprite_loader.cpp
Normal file
107
src/mbgl/sprite/sprite_loader.cpp
Normal file
@@ -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
|
||||
46
src/mbgl/sprite/sprite_loader.hpp
Normal file
46
src/mbgl/sprite/sprite_loader.hpp
Normal file
@@ -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
|
||||
24
src/mbgl/sprite/sprite_loader_observer.hpp
Normal file
24
src/mbgl/sprite/sprite_loader_observer.hpp
Normal file
@@ -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
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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);
|
||||
|
||||
@@ -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_) {
|
||||
Image::Image(PremultipliedImage &&image,
|
||||
const float pixelRatio,
|
||||
bool sdf)
|
||||
: impl(std::make_shared<Impl>(std::move(image), pixelRatio, 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");
|
||||
}
|
||||
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
|
||||
|
||||
22
src/mbgl/style/image_impl.cpp
Normal file
22
src/mbgl/style/image_impl.cpp
Normal file
@@ -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
|
||||
25
src/mbgl/style/image_impl.hpp
Normal file
25
src/mbgl/style/image_impl.hpp
Normal file
@@ -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
|
||||
@@ -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>
|
||||
|
||||
@@ -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) {}
|
||||
|
||||
@@ -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.
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user