1 #include <mbgl/renderer/image_manager.hpp>
2 #include <mbgl/util/logging.hpp>
3 #include <mbgl/gl/context.hpp>
4
5 namespace mbgl {
6
setLoaded(bool loaded_)7 void ImageManager::setLoaded(bool loaded_) {
8 if (loaded == loaded_) {
9 return;
10 }
11
12 loaded = loaded_;
13
14 if (loaded) {
15 for (const auto& entry : requestors) {
16 notify(*entry.first, entry.second);
17 }
18 requestors.clear();
19 }
20 }
21
isLoaded() const22 bool ImageManager::isLoaded() const {
23 return loaded;
24 }
25
addImage(Immutable<style::Image::Impl> image_)26 void ImageManager::addImage(Immutable<style::Image::Impl> image_) {
27 assert(images.find(image_->id) == images.end());
28 images.emplace(image_->id, std::move(image_));
29 }
30
updateImage(Immutable<style::Image::Impl> image_)31 void ImageManager::updateImage(Immutable<style::Image::Impl> image_) {
32 removeImage(image_->id);
33 addImage(std::move(image_));
34 }
35
removeImage(const std::string & id)36 void ImageManager::removeImage(const std::string& id) {
37 assert(images.find(id) != images.end());
38 images.erase(id);
39
40 auto it = patterns.find(id);
41 if (it != patterns.end()) {
42 // Clear pattern from the atlas image.
43 const uint32_t x = it->second.bin->x;
44 const uint32_t y = it->second.bin->y;
45 const uint32_t w = it->second.bin->w;
46 const uint32_t h = it->second.bin->h;
47 PremultipliedImage::clear(atlasImage, { x, y }, { w, h });
48
49 shelfPack.unref(*it->second.bin);
50 patterns.erase(it);
51 }
52 }
53
getImage(const std::string & id) const54 const style::Image::Impl* ImageManager::getImage(const std::string& id) const {
55 const auto it = images.find(id);
56 if (it != images.end()) {
57 return it->second.get();
58 }
59 return nullptr;
60 }
61
getImages(ImageRequestor & requestor,ImageRequestPair && pair)62 void ImageManager::getImages(ImageRequestor& requestor, ImageRequestPair&& pair) {
63 // If the sprite has been loaded, or if all the icon dependencies are already present
64 // (i.e. if they've been addeded via runtime styling), then notify the requestor immediately.
65 // Otherwise, delay notification until the sprite is loaded. At that point, if any of the
66 // dependencies are still unavailable, we'll just assume they are permanently missing.
67 bool hasAllDependencies = true;
68 if (!isLoaded()) {
69 for (const auto& dependency : pair.first) {
70 if (images.find(dependency) == images.end()) {
71 hasAllDependencies = false;
72 }
73 }
74 }
75 if (isLoaded() || hasAllDependencies) {
76 notify(requestor, std::move(pair));
77 } else {
78 requestors.emplace(&requestor, std::move(pair));
79 }
80 }
81
removeRequestor(ImageRequestor & requestor)82 void ImageManager::removeRequestor(ImageRequestor& requestor) {
83 requestors.erase(&requestor);
84 }
85
notify(ImageRequestor & requestor,const ImageRequestPair & pair) const86 void ImageManager::notify(ImageRequestor& requestor, const ImageRequestPair& pair) const {
87 ImageMap response;
88
89 for (const auto& dependency : pair.first) {
90 auto it = images.find(dependency);
91 if (it != images.end()) {
92 response.emplace(*it);
93 }
94 }
95
96 requestor.onImagesAvailable(response, pair.second);
97 }
98
dumpDebugLogs() const99 void ImageManager::dumpDebugLogs() const {
100 Log::Info(Event::General, "ImageManager::loaded: %d", loaded);
101 }
102
103 // When copied into the atlas texture, image data is padded by one pixel on each side. Icon
104 // images are padded with fully transparent pixels, while pattern images are padded with a
105 // copy of the image data wrapped from the opposite side. In both cases, this ensures the
106 // correct behavior of GL_LINEAR texture sampling mode.
107 static constexpr uint16_t padding = 1;
108
shelfPackOptions()109 static mapbox::ShelfPack::ShelfPackOptions shelfPackOptions() {
110 mapbox::ShelfPack::ShelfPackOptions options;
111 options.autoResize = true;
112 return options;
113 }
114
ImageManager()115 ImageManager::ImageManager()
116 : shelfPack(64, 64, shelfPackOptions()) {
117 }
118
119 ImageManager::~ImageManager() = default;
120
getPattern(const std::string & id)121 optional<ImagePosition> ImageManager::getPattern(const std::string& id) {
122 auto it = patterns.find(id);
123 if (it != patterns.end()) {
124 return it->second.position;
125 }
126
127 const style::Image::Impl* image = getImage(id);
128 if (!image) {
129 return {};
130 }
131
132 const uint16_t width = image->image.size.width + padding * 2;
133 const uint16_t height = image->image.size.height + padding * 2;
134
135 mapbox::Bin* bin = shelfPack.packOne(-1, width, height);
136 if (!bin) {
137 return {};
138 }
139
140 atlasImage.resize(getPixelSize());
141
142 const PremultipliedImage& src = image->image;
143
144 const uint32_t x = bin->x + padding;
145 const uint32_t y = bin->y + padding;
146 const uint32_t w = src.size.width;
147 const uint32_t h = src.size.height;
148
149 PremultipliedImage::copy(src, atlasImage, { 0, 0 }, { x, y }, { w, h });
150
151 // Add 1 pixel wrapped padding on each side of the image.
152 PremultipliedImage::copy(src, atlasImage, { 0, h - 1 }, { x, y - 1 }, { w, 1 }); // T
153 PremultipliedImage::copy(src, atlasImage, { 0, 0 }, { x, y + h }, { w, 1 }); // B
154 PremultipliedImage::copy(src, atlasImage, { w - 1, 0 }, { x - 1, y }, { 1, h }); // L
155 PremultipliedImage::copy(src, atlasImage, { 0, 0 }, { x + w, y }, { 1, h }); // R
156
157 dirty = true;
158
159 return patterns.emplace(id, Pattern { bin, { *bin, *image } }).first->second.position;
160 }
161
getPixelSize() const162 Size ImageManager::getPixelSize() const {
163 return Size {
164 static_cast<uint32_t>(shelfPack.width()),
165 static_cast<uint32_t>(shelfPack.height())
166 };
167 }
168
upload(gl::Context & context,gl::TextureUnit unit)169 void ImageManager::upload(gl::Context& context, gl::TextureUnit unit) {
170 if (!atlasTexture) {
171 atlasTexture = context.createTexture(atlasImage, unit);
172 } else if (dirty) {
173 context.updateTexture(*atlasTexture, atlasImage, unit);
174 }
175
176 dirty = false;
177 }
178
bind(gl::Context & context,gl::TextureUnit unit)179 void ImageManager::bind(gl::Context& context, gl::TextureUnit unit) {
180 upload(context, unit);
181 context.bindTexture(*atlasTexture, unit, gl::TextureFilter::Linear);
182 }
183
184 } // namespace mbgl
185