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