summaryrefslogtreecommitdiff
path: root/src/mbgl/sprite
diff options
context:
space:
mode:
authorJohn Firebaugh <john.firebaugh@gmail.com>2017-06-07 12:45:35 -0700
committerJohn Firebaugh <john.firebaugh@gmail.com>2017-06-13 10:18:43 -0700
commit0b687312071305c050d97e04fef1c80193f443c5 (patch)
tree64c20efaa17fefef9f902811a000fd6e425c849b /src/mbgl/sprite
parent92252849c1a2ddf7887d1908841fa3c90dd59766 (diff)
downloadqtlocation-mapboxgl-0b687312071305c050d97e04fef1c80193f443c5.tar.gz
[core] Per-bucket icon atlases
Diffstat (limited to 'src/mbgl/sprite')
-rw-r--r--src/mbgl/sprite/sprite_atlas.cpp256
-rw-r--r--src/mbgl/sprite/sprite_atlas.hpp137
-rw-r--r--src/mbgl/sprite/sprite_loader.cpp4
3 files changed, 2 insertions, 395 deletions
diff --git a/src/mbgl/sprite/sprite_atlas.cpp b/src/mbgl/sprite/sprite_atlas.cpp
deleted file mode 100644
index 69f2b0ce71..0000000000
--- a/src/mbgl/sprite/sprite_atlas.cpp
+++ /dev/null
@@ -1,256 +0,0 @@
-#include <mbgl/sprite/sprite_atlas.hpp>
-#include <mbgl/style/image_impl.hpp>
-#include <mbgl/gl/context.hpp>
-#include <mbgl/util/logging.hpp>
-#include <mbgl/util/platform.hpp>
-#include <mbgl/util/math.hpp>
-#include <mbgl/util/std.hpp>
-#include <mbgl/util/constants.hpp>
-#include <mbgl/util/exception.hpp>
-
-#include <cassert>
-#include <cmath>
-#include <algorithm>
-
-namespace mbgl {
-
-// When copied into the atlas texture, image data is padded by one pixel on each side. Icon
-// images are padded with fully transparent pixels, while pattern images are padded with a
-// copy of the image data wrapped from the opposite side. In both cases, this ensures the
-// correct behavior of GL_LINEAR texture sampling mode.
-static constexpr uint16_t padding = 1;
-
-SpriteAtlasElement::SpriteAtlasElement(const mapbox::Bin& bin, const style::Image::Impl& image)
- : sdf(image.sdf),
- pixelRatio(image.pixelRatio),
- textureRect(
- bin.x + padding,
- bin.y + padding,
- bin.w - padding * 2,
- bin.h - padding * 2
- ) {
-}
-
-static mapbox::ShelfPack::ShelfPackOptions shelfPackOptions() {
- mapbox::ShelfPack::ShelfPackOptions options;
- options.autoResize = true;
- return options;
-}
-
-SpriteAtlas::SpriteAtlas()
- : shelfPack(64, 64, shelfPackOptions()) {
-}
-
-SpriteAtlas::~SpriteAtlas() = default;
-
-void SpriteAtlas::onSpriteLoaded() {
- loaded = true;
- for (auto requestor : requestors) {
- requestor->onIconsAvailable(buildIconMap());
- }
- requestors.clear();
-}
-
-void SpriteAtlas::addImage(Immutable<style::Image::Impl> image_) {
- assert(entries.find(image_->id) == entries.end());
- entries.emplace(image_->id, Entry { image_ });
- icons.clear();
-}
-
-void SpriteAtlas::updateImage(Immutable<style::Image::Impl> image_) {
- assert(entries.find(image_->id) != entries.end());
- Entry& entry = entries.at(image_->id);
-
- // Style::addImage should prevent changing size.
- assert(entry.image->image.size == image_->image.size);
-
- entry.image = std::move(image_);
-
- if (entry.iconBin) {
- copy(entry, &Entry::iconBin);
- }
-
- if (entry.patternBin) {
- copy(entry, &Entry::patternBin);
- }
-
- icons.clear();
-}
-
-void SpriteAtlas::removeImage(const std::string& id) {
- assert(entries.find(id) != entries.end());
- Entry& entry = entries.at(id);
-
- if (entry.iconBin) {
- shelfPack.unref(*entry.iconBin);
- }
-
- if (entry.patternBin) {
- shelfPack.unref(*entry.patternBin);
- }
-
- entries.erase(id);
- icons.clear();
-}
-
-const style::Image::Impl* SpriteAtlas::getImage(const std::string& id) const {
- const auto it = entries.find(id);
- if (it != entries.end()) {
- return it->second.image.get();
- }
- if (!entries.empty()) {
- Log::Info(Event::Sprite, "Can't find sprite named '%s'", id.c_str());
- }
- return nullptr;
-}
-
-void SpriteAtlas::getIcons(IconRequestor& requestor, IconDependencies dependencies) {
- // If the sprite has been loaded, or if all the icon dependencies are already present
- // (i.e. if they've been addeded via runtime styling), then notify the requestor immediately.
- // Otherwise, delay notification until the sprite is loaded. At that point, if any of the
- // dependencies are still unavailable, we'll just assume they are permanently missing.
- bool hasAllDependencies = true;
- if (!isLoaded()) {
- for (const auto& dependency : dependencies) {
- if (entries.find(dependency) == entries.end()) {
- hasAllDependencies = false;
- }
- }
- }
- if (isLoaded() || hasAllDependencies) {
- requestor.onIconsAvailable(buildIconMap());
- } else {
- requestors.insert(&requestor);
- }
-}
-
-void SpriteAtlas::removeRequestor(IconRequestor& requestor) {
- requestors.erase(&requestor);
-}
-
-optional<SpriteAtlasElement> SpriteAtlas::getIcon(const std::string& id) {
- return getImage(id, &Entry::iconBin);
-}
-
-optional<SpriteAtlasElement> SpriteAtlas::getPattern(const std::string& id) {
- return getImage(id, &Entry::patternBin);
-}
-
-optional<SpriteAtlasElement> SpriteAtlas::getImage(const std::string& id, mapbox::Bin* Entry::*entryBin) {
- auto it = entries.find(id);
- if (it == entries.end()) {
- if (!entries.empty()) {
- Log::Info(Event::Sprite, "Can't find sprite named '%s'", id.c_str());
- }
- return {};
- }
-
- Entry& entry = it->second;
-
- if (entry.*entryBin) {
- assert(entry.image.get());
- return SpriteAtlasElement {
- *(entry.*entryBin),
- *entry.image
- };
- }
-
- const uint16_t width = entry.image->image.size.width + padding * 2;
- const uint16_t height = entry.image->image.size.height + padding * 2;
-
- mapbox::Bin* bin = shelfPack.packOne(-1, width, height);
- if (!bin) {
- if (debug::spriteWarnings) {
- Log::Warning(Event::Sprite, "sprite atlas bitmap overflow");
- }
- return {};
- }
-
- entry.*entryBin = bin;
- copy(entry, entryBin);
-
- return SpriteAtlasElement {
- *bin,
- *entry.image
- };
-}
-
-Size SpriteAtlas::getPixelSize() const {
- return Size {
- static_cast<uint32_t>(shelfPack.width()),
- static_cast<uint32_t>(shelfPack.height())
- };
-}
-
-void SpriteAtlas::copy(const Entry& entry, mapbox::Bin* Entry::*entryBin) {
- if (!image.valid()) {
- image = PremultipliedImage(getPixelSize());
- image.fill(0);
- } else if (image.size != getPixelSize()) {
- PremultipliedImage newImage(getPixelSize());
- PremultipliedImage::copy(image, newImage, { 0, 0 }, { 0, 0 }, image.size);
- image = std::move(newImage);
- }
-
- const PremultipliedImage& src = entry.image->image;
- const mapbox::Bin& bin = *(entry.*entryBin);
-
- const uint32_t x = bin.x + padding;
- const uint32_t y = bin.y + padding;
- const uint32_t w = src.size.width;
- const uint32_t h = src.size.height;
-
- PremultipliedImage::copy(src, image, { 0, 0 }, { x, y }, { w, h });
-
- if (entryBin == &Entry::patternBin) {
- // Add 1 pixel wrapped padding on each side of the image.
- PremultipliedImage::copy(src, image, { 0, h - 1 }, { x, y - 1 }, { w, 1 }); // T
- PremultipliedImage::copy(src, image, { 0, 0 }, { x, y + h }, { w, 1 }); // B
- PremultipliedImage::copy(src, image, { w - 1, 0 }, { x - 1, y }, { 1, h }); // L
- PremultipliedImage::copy(src, image, { 0, 0 }, { x + w, y }, { 1, h }); // R
- }
-
- dirty = true;
-}
-
-IconMap SpriteAtlas::buildIconMap() {
- if (icons.empty()) {
- for (const auto& entry : entries) {
- icons.emplace(std::piecewise_construct,
- std::forward_as_tuple(entry.first),
- std::forward_as_tuple(*getIcon(entry.first)));
-
- }
- }
- return icons;
-}
-
-void SpriteAtlas::upload(gl::Context& context, gl::TextureUnit unit) {
- if (!texture) {
- texture = context.createTexture(image, unit);
- } else if (dirty) {
- context.updateTexture(*texture, image, unit);
- }
-
-#if not MBGL_USE_GLES2
-// if (dirty) {
-// platform::showColorDebugImage("Sprite Atlas",
-// reinterpret_cast<const char*>(image.data.get()), size.width,
-// size.height, image.size.width, image.size.height);
-// }
-#endif // MBGL_USE_GLES2
-
- dirty = false;
-}
-
-void SpriteAtlas::bind(bool linear, gl::Context& context, gl::TextureUnit unit) {
- upload(context, unit);
- context.bindTexture(*texture, unit,
- linear ? gl::TextureFilter::Linear : gl::TextureFilter::Nearest);
-}
-
-void SpriteAtlas::dumpDebugLogs() const {
- Log::Info(Event::General, "SpriteAtlas::loaded: %d", loaded);
-}
-
-} // namespace mbgl
diff --git a/src/mbgl/sprite/sprite_atlas.hpp b/src/mbgl/sprite/sprite_atlas.hpp
deleted file mode 100644
index 1dbef86f7e..0000000000
--- a/src/mbgl/sprite/sprite_atlas.hpp
+++ /dev/null
@@ -1,137 +0,0 @@
-#pragma once
-
-#include <mbgl/gl/texture.hpp>
-#include <mbgl/util/noncopyable.hpp>
-#include <mbgl/util/optional.hpp>
-#include <mbgl/util/rect.hpp>
-#include <mbgl/style/image.hpp>
-
-#include <mapbox/shelf-pack.hpp>
-
-#include <string>
-#include <set>
-#include <unordered_map>
-#include <array>
-#include <memory>
-
-namespace mbgl {
-
-namespace gl {
-class Context;
-} // namespace gl
-
-class SpriteAtlasElement {
-public:
- SpriteAtlasElement(const mapbox::Bin&, const style::Image::Impl&);
-
- bool sdf;
- float pixelRatio;
- Rect<uint16_t> textureRect;
-
- std::array<uint16_t, 2> tl() const {
- return {{
- textureRect.x,
- textureRect.y
- }};
- }
-
- std::array<uint16_t, 2> br() const {
- return {{
- static_cast<uint16_t>(textureRect.x + textureRect.w),
- static_cast<uint16_t>(textureRect.y + textureRect.h)
- }};
- }
-
- std::array<float, 2> displaySize() const {
- return {{
- textureRect.w / pixelRatio,
- textureRect.h / pixelRatio,
- }};
- }
-};
-
-using IconMap = std::unordered_map<std::string, SpriteAtlasElement>;
-using IconDependencies = std::set<std::string>;
-
-class IconRequestor {
-public:
- virtual ~IconRequestor() = default;
- virtual void onIconsAvailable(IconMap) = 0;
-};
-
-class SpriteAtlas : public util::noncopyable {
-public:
- SpriteAtlas();
- ~SpriteAtlas();
-
- void onSpriteLoaded();
-
- bool isLoaded() const {
- return loaded;
- }
-
- void dumpDebugLogs() const;
-
- const style::Image::Impl* getImage(const std::string&) const;
-
- void addImage(Immutable<style::Image::Impl>);
- void updateImage(Immutable<style::Image::Impl>);
- void removeImage(const std::string&);
-
- void getIcons(IconRequestor&, IconDependencies);
- void removeRequestor(IconRequestor&);
-
- // Ensure that the atlas contains the named image suitable for rendering as an icon, and
- // return its metrics. The image will be padded on each side with a one pixel wide transparent
- // strip, but the returned metrics are exclusive of this padding.
- optional<SpriteAtlasElement> getIcon(const std::string& name);
-
- // Ensure that the atlas contains the named image suitable for rendering as an pattern, and
- // return its metrics. The image will be padded on each side with a one pixel wide copy of
- // pixels from the opposite side, but the returned metrics are exclusive of this padding.
- optional<SpriteAtlasElement> getPattern(const std::string& name);
-
- // Binds the atlas texture to the GPU, and uploads data if it is out of date.
- void bind(bool linear, gl::Context&, gl::TextureUnit unit);
-
- // Uploads the texture to the GPU to be available when we need it. This is a lazy operation;
- // the texture is only bound when the data is out of date (=dirty).
- void upload(gl::Context&, gl::TextureUnit unit);
-
- Size getPixelSize() const;
-
- // Only for use in tests.
- const PremultipliedImage& getAtlasImage() const {
- return image;
- }
-
-private:
- bool loaded = false;
-
- struct Entry {
- Immutable<style::Image::Impl> 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
- // a single pixel transparent border, and the one for the pattern image has a single
- // pixel border wrapped from the opposite side.
- mapbox::Bin* iconBin = nullptr;
- mapbox::Bin* patternBin = nullptr;
- };
-
- optional<SpriteAtlasElement> getImage(const std::string& name, mapbox::Bin* Entry::*bin);
- void copy(const Entry&, mapbox::Bin* Entry::*bin);
-
- IconMap buildIconMap();
-
- std::unordered_map<std::string, Entry> entries;
- mapbox::ShelfPack shelfPack;
- PremultipliedImage image;
- mbgl::optional<gl::Texture> texture;
- bool dirty = true;
-
- std::set<IconRequestor*> requestors;
- IconMap icons;
-};
-
-} // namespace mbgl
diff --git a/src/mbgl/sprite/sprite_loader.cpp b/src/mbgl/sprite/sprite_loader.cpp
index 7c5fe40e05..60ece5ed73 100644
--- a/src/mbgl/sprite/sprite_loader.cpp
+++ b/src/mbgl/sprite/sprite_loader.cpp
@@ -20,9 +20,9 @@ namespace mbgl {
static SpriteLoaderObserver nullObserver;
struct SpriteLoader::Loader {
- Loader(Scheduler& scheduler, SpriteLoader& spriteAtlas)
+ Loader(Scheduler& scheduler, SpriteLoader& imageManager)
: mailbox(std::make_shared<Mailbox>(*util::RunLoop::Get())),
- worker(scheduler, ActorRef<SpriteLoader>(spriteAtlas, mailbox)) {
+ worker(scheduler, ActorRef<SpriteLoader>(imageManager, mailbox)) {
}
std::shared_ptr<const std::string> image;