diff options
author | Konstantin Käfer <mail@kkaefer.com> | 2014-05-19 10:48:11 +0200 |
---|---|---|
committer | Konstantin Käfer <mail@kkaefer.com> | 2014-05-19 10:48:36 +0200 |
commit | 13575330f7d32630f6dbf4244405c46b453152fe (patch) | |
tree | c5bc7f3a25774c2d8f6783d77fbd0654e0554acc | |
parent | 24bd0bb667e6c2570838cd64992ffa388e8fb941 (diff) | |
download | qtlocation-mapboxgl-13575330f7d32630f6dbf4244405c46b453152fe.tar.gz |
use sprite atlas
-rw-r--r-- | common/glfw_view.cpp | 29 | ||||
-rw-r--r-- | include/llmr/geometry/sprite_atlas.hpp | 68 | ||||
-rw-r--r-- | include/llmr/map/map.hpp | 2 | ||||
-rw-r--r-- | include/llmr/map/tile_parser.hpp | 6 | ||||
-rw-r--r-- | include/llmr/platform/platform.hpp | 5 | ||||
-rw-r--r-- | include/llmr/renderer/icon_bucket.hpp | 4 | ||||
-rw-r--r-- | include/llmr/style/style.hpp | 1 | ||||
-rw-r--r-- | include/llmr/util/math.hpp | 16 | ||||
-rw-r--r-- | include/llmr/util/raster.hpp | 2 | ||||
-rw-r--r-- | include/llmr/util/rect.hpp | 5 | ||||
-rw-r--r-- | src/geometry/sprite_atlas.cpp | 235 | ||||
-rw-r--r-- | src/map/map.cpp | 15 | ||||
-rw-r--r-- | src/map/tile_parser.cpp | 11 | ||||
-rw-r--r-- | src/map/vector_tile_data.cpp | 2 | ||||
-rw-r--r-- | src/renderer/icon_bucket.cpp | 19 | ||||
-rw-r--r-- | src/renderer/painter_icon.cpp | 49 | ||||
-rw-r--r-- | src/shader/icon.fragment.glsl | 2 | ||||
-rw-r--r-- | src/shader/shaders_gl.cpp | 2 | ||||
-rw-r--r-- | src/shader/shaders_gles2.cpp | 2 | ||||
-rw-r--r-- | src/style/sprite.cpp | 52 |
20 files changed, 440 insertions, 87 deletions
diff --git a/common/glfw_view.cpp b/common/glfw_view.cpp index 7ec5d3b083..2542e6cf93 100644 --- a/common/glfw_view.cpp +++ b/common/glfw_view.cpp @@ -231,5 +231,34 @@ void show_debug_image(std::string name, const char *data, size_t width, size_t h glfwMakeContextCurrent(current_window); } + +void show_color_debug_image(std::string name, const char *data, size_t width, size_t height) { + static GLFWwindow *debug_window = nullptr; + if (!debug_window) { + debug_window = glfwCreateWindow(width, height, name.c_str(), nullptr, nullptr); + if (!debug_window) { + glfwTerminate(); + fprintf(stderr, "Failed to initialize window\n"); + exit(1); + } + } + + GLFWwindow *current_window = glfwGetCurrentContext(); + + glfwSetWindowSize(debug_window, width, height); + glfwMakeContextCurrent(debug_window); + + int fb_width, fb_height; + glfwGetFramebufferSize(debug_window, &fb_width, &fb_height); + float scale = (float)fb_width / (float)width; + + glPixelZoom(scale, -scale); + glRasterPos2f(-1.0f, 1.0f); + glDrawPixels(width, height, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, data); + glfwSwapBuffers(debug_window); + + glfwMakeContextCurrent(current_window); +} + } } diff --git a/include/llmr/geometry/sprite_atlas.hpp b/include/llmr/geometry/sprite_atlas.hpp new file mode 100644 index 0000000000..e6557846af --- /dev/null +++ b/include/llmr/geometry/sprite_atlas.hpp @@ -0,0 +1,68 @@ +#ifndef LLMR_GEOMETRY_SPRITE_ATLAS +#define LLMR_GEOMETRY_SPRITE_ATLAS + +#include <llmr/geometry/binpack.hpp> + +#include <string> +#include <map> +#include <mutex> +#include <atomic> +#include <set> + +namespace llmr { + +class Sprite; + +class SpriteAtlas { +public: + typedef uint16_t dimension; + +public: + // Add way to construct this from another SpriteAtlas (e.g. with another pixelRatio) + SpriteAtlas(dimension width, dimension height); + ~SpriteAtlas(); + + // Changes the pixel ratio. + bool resize(float newRatio); + + // Update uninitialized sprites in this atlas from the given sprite. + void update(const Sprite &sprite); + + // Returns the coordinates of the icon. The getter also *creates* new icons in the atlas + // if they don't exist, but they'll be default-initialized with a a black circle. + Rect<dimension> getIcon(int size, const std::string &name); + + // Updates or creates an icon with a given size and name. The data must be a + // a (pixelRatio * size)^2 * 4 byte long RGBA image buffer. + Rect<dimension> setIcon(int size, const std::string &name, const std::string &icon); + + // Binds the image buffer of this sprite atlas to the GPU, and uploads data if it is out + // of date. + void bind(bool linear = false); + + inline float getTextureWidth() const { return width * pixelRatio; } + inline float getTextureHeight() const { return height * pixelRatio; } + +private: + void allocate(); + +public: + const dimension width = 0; + const dimension height = 0; + +private: + std::mutex mtx; + float pixelRatio = 1.0f; + BinPack<dimension> bin; + std::map<int, std::map<std::string, Rect<dimension>>> index; + std::set<std::pair<int, std::string>> uninitialized; + char *data = nullptr; + std::atomic<bool> dirty; + uint32_t texture = 0; + uint32_t filter = 0; + static const int buffer = 1; +}; + +}; + +#endif diff --git a/include/llmr/map/map.hpp b/include/llmr/map/map.hpp index 4600e1a967..87d64b0733 100644 --- a/include/llmr/map/map.hpp +++ b/include/llmr/map/map.hpp @@ -92,6 +92,7 @@ public: inline const TransformState &getState() const { return state; } inline const Style &getStyle() const { return style; } inline GlyphAtlas &getGlyphAtlas() { return glyphAtlas; } + inline SpriteAtlas &getSpriteAtlas() { return spriteAtlas; } inline uv_loop_t *getLoop() { return loop; } inline time getAnimationTime() const { return animationTime; } inline Texturepool &getTexturepool() { return texturepool; } @@ -143,6 +144,7 @@ private: Texturepool texturepool; Style style; GlyphAtlas glyphAtlas; + SpriteAtlas spriteAtlas; Painter painter; std::map<std::string, const std::unique_ptr<Source>> sources; diff --git a/include/llmr/map/tile_parser.hpp b/include/llmr/map/tile_parser.hpp index 12f3da8ff2..86545e4f76 100644 --- a/include/llmr/map/tile_parser.hpp +++ b/include/llmr/map/tile_parser.hpp @@ -9,13 +9,14 @@ namespace llmr { class Style; class GlyphAtlas; +class SpriteAtlas; class LayerDescription; class Bucket; class TileParser { public: - TileParser(const std::string& data, VectorTileData& tile, const Style& style, GlyphAtlas& glyphAtlas); + TileParser(const std::string& data, VectorTileData& tile, const Style& style, GlyphAtlas& glyphAtlas, SpriteAtlas &spriteAtlas); private: bool obsolete() const; @@ -27,13 +28,14 @@ private: std::unique_ptr<Bucket> createIconBucket(const VectorTileLayer& layer, const BucketDescription& bucket_desc); std::unique_ptr<Bucket> createTextBucket(const VectorTileLayer& layer, const BucketDescription& bucket_desc); template <class Bucket> void addBucketFeatures(Bucket& bucket, const VectorTileLayer& layer, const BucketDescription& bucket_desc); - template <class Bucket, typename... Args> void addBucketFeatures(Bucket& bucket, const VectorTileLayer& layer, const BucketDescription& bucket_desc, Args... args); + template <class Bucket, typename ...Args> void addBucketFeatures(Bucket& bucket, const VectorTileLayer& layer, const BucketDescription& bucket_desc, Args&& ...args); private: const VectorTile vector_data; VectorTileData& tile; const Style& style; GlyphAtlas& glyphAtlas; + SpriteAtlas &spriteAtlas; Faces faces; Placement placement; }; diff --git a/include/llmr/platform/platform.hpp b/include/llmr/platform/platform.hpp index 4f7b4ed693..dc8f7b5faf 100644 --- a/include/llmr/platform/platform.hpp +++ b/include/llmr/platform/platform.hpp @@ -32,8 +32,11 @@ std::shared_ptr<Request> request_http(const std::string &url, // Cancels an HTTP request. void cancel_request_http(const std::shared_ptr<Request> &req); -// Shows an RGBA image with the specified dimensions in a named window. +// Shows an alpha image with the specified dimensions in a named window. void show_debug_image(std::string name, const char *data, size_t width, size_t height); + +// Shows an alpha image with the specified dimensions in a named window. +void show_color_debug_image(std::string name, const char *data, size_t width, size_t height); } } diff --git a/include/llmr/renderer/icon_bucket.hpp b/include/llmr/renderer/icon_bucket.hpp index ce90edce30..0878622ec3 100644 --- a/include/llmr/renderer/icon_bucket.hpp +++ b/include/llmr/renderer/icon_bucket.hpp @@ -20,7 +20,7 @@ class IconVertexBuffer; class BucketDescription; class IconShader; class DotShader; -class Sprite; +class SpriteAtlas; class VectorTileFeature; class IconBucket : public Bucket { @@ -31,7 +31,7 @@ public: virtual void render(Painter& painter, const std::string& layer_name, const Tile::ID& id); virtual bool hasData() const; - void addFeature(const VectorTileFeature &feature, const std::shared_ptr<Sprite> &sprite); + void addFeature(const VectorTileFeature &feature, SpriteAtlas &sprite_atlas); void drawIcons(IconShader& shader); void drawIcons(DotShader& shader); diff --git a/include/llmr/style/style.hpp b/include/llmr/style/style.hpp index 8fcb99732e..d9c8c22bda 100644 --- a/include/llmr/style/style.hpp +++ b/include/llmr/style/style.hpp @@ -7,6 +7,7 @@ #include <llmr/style/bucket_description.hpp> #include <llmr/style/layer_description.hpp> #include <llmr/style/class_description.hpp> +#include <llmr/geometry/sprite_atlas.hpp> #include <map> #include <vector> diff --git a/include/llmr/util/math.hpp b/include/llmr/util/math.hpp index 531a13a1a5..ab8e392c48 100644 --- a/include/llmr/util/math.hpp +++ b/include/llmr/util/math.hpp @@ -87,12 +87,28 @@ inline T dist(const S1& a, const S2& b) { return c; } +template <typename T> +inline T length(T a, T b) { + return std::sqrt(a * a + b * b); +} + // Take the magnitude of vector a. template <typename T = double, typename S> inline T mag(const S& a) { return std::sqrt(a.x * a.x + a.y * a.y); } +template <typename T> +T clamp(T value, T min, T max) { + return value < min ? min : (value > max ? max : value); +} + +template <typename T> +T smoothstep(T edge0, T edge1, T x) { + T t = clamp((x - edge0) / (edge1 - edge0), T(0), T(1)); + return t * t * (T(3) - T(2) * t); +} + } } diff --git a/include/llmr/util/raster.hpp b/include/llmr/util/raster.hpp index ccdffeef37..0ad29dc7a2 100644 --- a/include/llmr/util/raster.hpp +++ b/include/llmr/util/raster.hpp @@ -32,6 +32,8 @@ public: bool needsTransition() const; void updateTransitions(time now); + inline const char *getData() const { return img; } + public: // loaded image dimensions uint32_t width = 0, height = 0; diff --git a/include/llmr/util/rect.hpp b/include/llmr/util/rect.hpp index 56bab2c9c9..2e142c3018 100644 --- a/include/llmr/util/rect.hpp +++ b/include/llmr/util/rect.hpp @@ -9,6 +9,11 @@ struct Rect { T x = 0, y = 0; T w = 0, h = 0; + template <typename Number> + Rect operator *(Number value) const { + return Rect(x * value, y * value, w * value, h * value); + } + operator bool() const { return w == 0 || h == 0; } }; } diff --git a/src/geometry/sprite_atlas.cpp b/src/geometry/sprite_atlas.cpp new file mode 100644 index 0000000000..80212c1dfb --- /dev/null +++ b/src/geometry/sprite_atlas.cpp @@ -0,0 +1,235 @@ +#include <llmr/geometry/sprite_atlas.hpp> +#include <llmr/platform/gl.hpp> +#include <llmr/platform/platform.hpp> +#include <llmr/util/math.hpp> +#include <llmr/util/std.hpp> + +#include <llmr/style/sprite.hpp> + +#include <cassert> +#include <cmath> +#include <algorithm> + + +using namespace llmr; + +SpriteAtlas::SpriteAtlas(dimension width, dimension height) + : width(width), + height(height), + bin(width, height), + dirty(true) { +} + +bool SpriteAtlas::resize(const float newRatio) { + if (pixelRatio == newRatio) return false; + + std::lock_guard<std::mutex> lock(mtx); + + const float oldRatio = pixelRatio; + pixelRatio = newRatio; + + if (data) { + char *old_data = data; + + data = nullptr; + allocate(); + + dimension w = static_cast<dimension>(width * newRatio); + dimension h = static_cast<dimension>(height * newRatio); + float s = std::pow(oldRatio / newRatio, 2); + + // Basic image scaling. TODO: Replace this with better image scaling. + uint32_t *img_new = reinterpret_cast<uint32_t *>(data); + uint32_t *img_old = reinterpret_cast<uint32_t *>(old_data); + for (size_t i = 0, length = w * h; i < length; i++) { + img_new[i] = img_old[static_cast<size_t>(s * i)]; + } + + free(old_data); + dirty = true; + } + + return dirty; +} + +Rect<SpriteAtlas::dimension> SpriteAtlas::getIcon(const int size, const std::string &name) { + std::lock_guard<std::mutex> lock(mtx); + + std::map<std::string, Rect<dimension>> &size_index = index[size]; + + auto rect_it = size_index.find(name); + if (rect_it != size_index.end()) { + return rect_it->second; + } + + // We have to allocate a new area in the bin, and store an empty image in it. + // Add a 1px border around every image. + const dimension pack_size = size + 2 * buffer; + + Rect<dimension> rect = bin.allocate(pack_size, pack_size); + if (rect.w == 0) { + fprintf(stderr, "sprite atlas bitmap overflow"); + return rect; + } + + size_index.emplace(name, rect); + + allocate(); + + + // Draw an antialiased circle. + const int img_size = size * pixelRatio; + const int img_offset_x = (rect.x + buffer) * pixelRatio; + const int img_offset_y = (rect.y + buffer) * pixelRatio; + + uint32_t *sprite_img = reinterpret_cast<uint32_t *>(data); + const float blur = 1.5f / size; + + const uint8_t r = 0x7F; + const uint8_t g = 0x7F; + const uint8_t b = 0x7F; + + const int sprite_stride = width * pixelRatio; + for (int y = 0; y < img_size; y++) { + const int img_y = (img_offset_y + y) * sprite_stride + img_offset_x; + for (int x = 0; x < img_size; x++) { + + const float dist = util::length(float(x) / img_size - 0.5f, float(y) / img_size - 0.5f); + const float t = util::smoothstep(0.5f, 0.5f - blur, dist); + const uint8_t alpha = t * 255; + + uint32_t color = (uint32_t(r * t) << 0) | + (uint32_t(g * t) << 8) | + (uint32_t(b * t) << 16) | + (uint32_t(alpha) << 24); + sprite_img[img_y + x] = color; + } + } + + uninitialized.emplace(size, name); + + dirty = true; + + return rect; +} + +void SpriteAtlas::allocate() { + if (!data) { + dimension w = static_cast<dimension>(width * pixelRatio); + dimension h = static_cast<dimension>(height * pixelRatio); + data = (char *)calloc(w * h, sizeof(uint32_t)); + } +} + +Rect<SpriteAtlas::dimension> SpriteAtlas::setIcon(const int size, const std::string &name, const std::string &icon) { + Rect<dimension> rect = getIcon(size, name); + + // Copy the bitmap + const int img_size = size * pixelRatio; + const int img_offset_x = (rect.x + buffer) * pixelRatio; + const int img_offset_y = (rect.y + buffer) * pixelRatio; + + if (std::pow(size * pixelRatio, 2) * 4 /* rgba */ != icon.size()) { fprintf(stderr, "mismatched icon buffer size!"); } + + const uint32_t *icon_img = reinterpret_cast<const uint32_t *>(icon.data()); + uint32_t *sprite_img = reinterpret_cast<uint32_t *>(data); + + const int sprite_stride = width * pixelRatio; + const int icon_stride = size * pixelRatio; + for (size_t y = 0; y < img_size; y++) { + const int img_y = (img_offset_y + y) * sprite_stride + img_offset_x; + const int icon_y = y * icon_stride; + for (size_t x = 0; x < img_size; x++) { + sprite_img[img_y + x] = icon_img[icon_y + x]; + } + } + + dirty = true; + + return rect; +} + +void SpriteAtlas::update(const Sprite &sprite) { + if (!sprite.isLoaded()) return; + + SpriteAtlas &atlas = *this; + std::erase_if(uninitialized, [&sprite, &atlas](const std::pair<int, std::string> &pair) { + const int &size = pair.first; + const std::string &name = pair.second; + const SpritePosition &src = sprite.getSpritePosition(name + "-" + std::to_string(size)); + if (src.width == size && src.height == size && src.pixelRatio == atlas.pixelRatio) { + const uint32_t *src_img = reinterpret_cast<const uint32_t *>(sprite.raster.getData()); + + uint32_t *dst_img = reinterpret_cast<uint32_t *>(atlas.data); + Rect<dimension> dst = atlas.getIcon(size, name); + dst.x = (dst.x + buffer) * atlas.pixelRatio; + dst.y = (dst.y + buffer) * atlas.pixelRatio; + dst.w = (dst.w - 2 * buffer) * atlas.pixelRatio; + dst.h = (dst.h - 2 * buffer) * atlas.pixelRatio; + + + const int src_image_stride = sprite.raster.width; + const int dst_image_stride = atlas.width * atlas.pixelRatio; + const int src_stride = src.width * src.pixelRatio; + + for (int y = 0; y < dst.h; y++) { + const int src_pos = (src.y * src.pixelRatio + y) * src_image_stride + src.x * src.pixelRatio; + const int dst_pos = (dst.y + y) * dst_image_stride + dst.x; + + // TODO: this is crashing + // memcpy(dst_img + dst_pos, src_img + src_pos, src_stride * sizeof(uint32_t)); + } + + + return true; + } else { + return false; + } + }); +} + +void SpriteAtlas::bind(bool linear) { + if (!texture) { + glGenTextures(1, &texture); + glBindTexture(GL_TEXTURE_2D, texture); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + } else { + glBindTexture(GL_TEXTURE_2D, texture); + } + + GLuint filter_val = linear ? GL_LINEAR : GL_NEAREST; + if (filter_val != filter) { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter_val); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter_val); + filter = filter_val; + } + + if (dirty) { + std::lock_guard<std::mutex> lock(mtx); + allocate(); + glTexImage2D( + GL_TEXTURE_2D, // GLenum target + 0, // GLint level + GL_RGBA, // GLint internalformat + width * pixelRatio, // GLsizei width + height * pixelRatio, // GLsizei height + 0, // GLint border + GL_RGBA, // GLenum format + GL_UNSIGNED_BYTE, // GLenum type + data // const GLvoid * data + ); + + platform::show_color_debug_image("Sprite Atlas", data, width * pixelRatio, height * pixelRatio); + + dirty = false; + } +}; + +SpriteAtlas::~SpriteAtlas() { + std::lock_guard<std::mutex> lock(mtx); + if (data) { + free(data); + data = nullptr; + } +} diff --git a/src/map/map.cpp b/src/map/map.cpp index 79a26305e4..4a16ac417a 100644 --- a/src/map/map.cpp +++ b/src/map/map.cpp @@ -20,6 +20,7 @@ Map::Map(View& view) texturepool(), style(), glyphAtlas(1024, 1024), + spriteAtlas(512, 512), painter(*this), loop(uv_loop_new()) { @@ -175,7 +176,16 @@ void Map::resize(uint16_t width, uint16_t height, float ratio) { } void Map::resize(uint16_t width, uint16_t height, float ratio, uint16_t fb_width, uint16_t fb_height) { + bool changed = false; + if (transform.resize(width, height, ratio, fb_width, fb_height)) { + changed = true; + } + if (spriteAtlas.resize(ratio)) { + changed = true; + } + + if (changed) { update(); } } @@ -393,6 +403,11 @@ void Map::prepare() { style.sprite->load(kSpriteURL); } + // Allow the sprite atlas to potentially pull new sprite images if needed. + if (style.sprite && style.sprite->isLoaded()) { + spriteAtlas.update(*style.sprite); + } + updateTiles(); updateClippingIDs(); diff --git a/src/map/tile_parser.cpp b/src/map/tile_parser.cpp index 61957e6074..67a32d4cba 100644 --- a/src/map/tile_parser.cpp +++ b/src/map/tile_parser.cpp @@ -14,11 +14,12 @@ using namespace llmr; -TileParser::TileParser(const std::string& data, VectorTileData& tile, const Style& style, GlyphAtlas& glyphAtlas) +TileParser::TileParser(const std::string& data, VectorTileData& tile, const Style& style, GlyphAtlas& glyphAtlas, SpriteAtlas &spriteAtlas) : vector_data(pbf((const uint8_t *)data.data(), data.size())), tile(tile), style(style), glyphAtlas(glyphAtlas), + spriteAtlas(spriteAtlas), placement(tile.id.z) { parseGlyphs(); parseStyleLayers(style.layers); @@ -120,12 +121,12 @@ void TileParser::addBucketFeatures(Bucket& bucket, const VectorTileLayer& layer, } } -template <class Bucket, typename... Args> -void TileParser::addBucketFeatures(Bucket& bucket, const VectorTileLayer& layer, const BucketDescription& bucket_desc, Args... args) { +template <class Bucket, typename ...Args> +void TileParser::addBucketFeatures(Bucket& bucket, const VectorTileLayer& layer, const BucketDescription& bucket_desc, Args&& ...args) { FilteredVectorTileLayer filtered_layer(layer, bucket_desc); for (const pbf& feature_pbf : filtered_layer) { if (obsolete()) return; - bucket->addFeature({ feature_pbf, layer }, args...); + bucket->addFeature({ feature_pbf, layer }, std::forward<Args>(args)...); } } @@ -146,7 +147,7 @@ std::unique_ptr<Bucket> TileParser::createLineBucket(const VectorTileLayer& laye std::unique_ptr<Bucket> TileParser::createIconBucket(const VectorTileLayer& layer, const BucketDescription& bucket_desc) { std::unique_ptr<IconBucket> bucket = std::make_unique<IconBucket>( tile.iconVertexBuffer, bucket_desc); - addBucketFeatures(bucket, layer, bucket_desc, style.sprite); + addBucketFeatures(bucket, layer, bucket_desc, spriteAtlas); return obsolete() ? nullptr : std::move(bucket); } diff --git a/src/map/vector_tile_data.cpp b/src/map/vector_tile_data.cpp index 84fc4ffc4d..337e2fcc35 100644 --- a/src/map/vector_tile_data.cpp +++ b/src/map/vector_tile_data.cpp @@ -22,7 +22,7 @@ void VectorTileData::parse() { // Parsing creates state that is encapsulated in TileParser. While parsing, // the TileParser object writes results into this objects. All other state // is going to be discarded afterwards. - TileParser parser(data, *this, map.getStyle(), map.getGlyphAtlas()); + TileParser parser(data, *this, map.getStyle(), map.getGlyphAtlas(), map.getSpriteAtlas()); } catch (const std::exception& ex) { fprintf(stderr, "[%p] exception [%d/%d/%d]... failed: %s\n", this, id.z, id.x, id.y, ex.what()); cancel(); diff --git a/src/renderer/icon_bucket.cpp b/src/renderer/icon_bucket.cpp index 060e1e0167..9d1505c03f 100644 --- a/src/renderer/icon_bucket.cpp +++ b/src/renderer/icon_bucket.cpp @@ -23,27 +23,18 @@ IconBucket::IconBucket(IconVertexBuffer& vertexBuffer, vertex_start(vertexBuffer.index()) { } -void IconBucket::addFeature(const VectorTileFeature &feature, const std::shared_ptr<Sprite> &sprite) { - // TODO: We somehow need to reparse the stylesheet, or maintain a mapping of id => sprite name - // For now, we're just not showing points if the sprite was not yet loaded at *parse time* of - // the tile. - if (!sprite || !sprite->isLoaded()) return; - +void IconBucket::addFeature(const VectorTileFeature &feature, SpriteAtlas &sprite_atlas) { auto field_it = feature.properties.find(geometry.field); if (field_it == feature.properties.end()) { - fprintf(stderr, "feature doesn't contain field '%s'\n", geometry.field.c_str()); + // fprintf(stderr, "feature doesn't contain field '%s'\n", geometry.field.c_str()); return; } std::string field = toString(field_it->second); - if (geometry.size) { - field.append("-"); - field.append(std::to_string(static_cast<int>(std::round(geometry.size)))); - } - const SpritePosition &pos = sprite->getSpritePosition(field); - const uint16_t tx = pos.x + pos.width / 2; - const uint16_t ty = pos.y + pos.height / 2; + const Rect<uint16_t> rect = sprite_atlas.getIcon(geometry.size, field); + const uint16_t tx = rect.x + rect.w / 2; + const uint16_t ty = rect.y + rect.h / 2; Geometry::command cmd; pbf geom = feature.geometry; diff --git a/src/renderer/painter_icon.cpp b/src/renderer/painter_icon.cpp index 0590aeb1d7..53d775a176 100644 --- a/src/renderer/painter_icon.cpp +++ b/src/renderer/painter_icon.cpp @@ -26,44 +26,27 @@ void Painter::renderIcon(IconBucket& bucket, const std::string& layer_name, cons const mat4 &vtxMatrix = translatedMatrix(properties.translate, id, properties.translateAnchor); - const std::shared_ptr<Sprite> &sprite = map.getStyle().sprite; + SpriteAtlas &spriteAtlas = map.getSpriteAtlas(); - if (!sprite || !sprite->isLoaded()) { - useProgram(dotShader->program); - dotShader->setMatrix(vtxMatrix); - dotShader->setColor(color); + useProgram(iconShader->program); + iconShader->setMatrix(vtxMatrix); + iconShader->setColor(color); + iconShader->setImage(0); - const float iconSize = (properties.radius ? properties.radius * 2 : 8) * map.getState().getPixelRatio(); - dotShader->setSize(iconSize); -#ifndef GL_ES_VERSION_2_0 - glPointSize(iconSize); - glEnable(GL_POINT_SPRITE); -#endif - dotShader->setBlur((properties.blur ? properties.blur : 1.5) / iconSize); - - glDepthRange(strata, 1.0f); - bucket.drawIcons(*dotShader); - } else { - useProgram(iconShader->program); - iconShader->setMatrix(vtxMatrix); - iconShader->setColor(color); - iconShader->setImage(0); - - iconShader->setDimension({{ - static_cast<float>(sprite->raster.width), - static_cast<float>(sprite->raster.height) - }}); + iconShader->setDimension({{ + spriteAtlas.getTextureWidth(), + spriteAtlas.getTextureHeight(), + }}); - sprite->raster.bind(map.getState().isChanging()); + spriteAtlas.bind(map.getState().isChanging()); - const float iconSize = bucket.geometry.size * map.getState().getPixelRatio(); - iconShader->setSize(iconSize); + const float iconSize = bucket.geometry.size * map.getState().getPixelRatio(); + iconShader->setSize(iconSize); #ifndef GL_ES_VERSION_2_0 - glPointSize(iconSize); - glEnable(GL_POINT_SPRITE); + glPointSize(iconSize); + glEnable(GL_POINT_SPRITE); #endif - glDepthRange(strata, 1.0f); - bucket.drawIcons(*iconShader); - } + glDepthRange(strata, 1.0f); + bucket.drawIcons(*iconShader); } diff --git a/src/shader/icon.fragment.glsl b/src/shader/icon.fragment.glsl index aacbb6a54b..a3191b3a8d 100644 --- a/src/shader/icon.fragment.glsl +++ b/src/shader/icon.fragment.glsl @@ -7,5 +7,5 @@ varying vec2 v_tex; void main() { vec2 pos = (v_tex + (gl_PointCoord - 0.5) * u_size) / u_dimension; - gl_FragColor = texture2D(u_image, pos) * u_color; + gl_FragColor = texture2D(u_image, pos); } diff --git a/src/shader/shaders_gl.cpp b/src/shader/shaders_gl.cpp index aebd5bfac4..8473296f22 100644 --- a/src/shader/shaders_gl.cpp +++ b/src/shader/shaders_gl.cpp @@ -12,7 +12,7 @@ const shader_source llmr::shaders[SHADER_COUNT] = { }, { "#version 120\nattribute vec2 a_pos;\nattribute vec2 a_tex;\nuniform mat4 u_matrix;\nuniform float u_size;\nvarying vec2 v_tex;\nvoid main ()\n{\n vec4 tmpvar_1;\n tmpvar_1.zw = vec2(0.0, 1.0);\n tmpvar_1.xy = a_pos;\n gl_Position = (u_matrix * tmpvar_1);\n gl_PointSize = u_size;\n v_tex = a_tex;\n}\n\n", - "#version 120\nuniform sampler2D u_image;\nuniform vec2 u_dimension;\nuniform vec4 u_color;\nuniform float u_size;\nvarying vec2 v_tex;\nvoid main ()\n{\n gl_FragColor = (texture2D (u_image, ((v_tex + \n ((gl_PointCoord - 0.5) * u_size)\n ) / u_dimension)) * u_color);\n}\n\n", + "#version 120\nuniform sampler2D u_image;\nuniform vec2 u_dimension;\nuniform float u_size;\nvarying vec2 v_tex;\nvoid main ()\n{\n gl_FragColor = texture2D (u_image, ((v_tex + (\n (gl_PointCoord - 0.5)\n * u_size)) / u_dimension));\n}\n\n", }, { "#version 120\nattribute vec2 a_pos;\nattribute vec2 a_extrude;\nattribute float a_linesofar;\nuniform mat4 u_matrix;\nuniform mat4 u_exmatrix;\nuniform float u_ratio;\nuniform vec2 u_linewidth;\nvarying vec2 v_normal;\nvarying float v_linesofar;\nvoid main ()\n{\n vec2 normal_1;\n vec2 tmpvar_2;\n tmpvar_2 = (vec2(mod (a_pos, 2.0)));\n normal_1.x = tmpvar_2.x;\n normal_1.y = sign((tmpvar_2.y - 0.5));\n v_normal = normal_1;\n vec4 tmpvar_3;\n tmpvar_3.zw = vec2(0.0, 0.0);\n tmpvar_3.xy = ((u_linewidth.x * a_extrude) * 0.015873);\n vec4 tmpvar_4;\n tmpvar_4.zw = vec2(0.0, 1.0);\n tmpvar_4.xy = floor((a_pos * 0.5));\n gl_Position = ((u_matrix * tmpvar_4) + (u_exmatrix * tmpvar_3));\n v_linesofar = (a_linesofar * u_ratio);\n}\n\n", diff --git a/src/shader/shaders_gles2.cpp b/src/shader/shaders_gles2.cpp index 185e4c0a87..db78f46570 100644 --- a/src/shader/shaders_gles2.cpp +++ b/src/shader/shaders_gles2.cpp @@ -12,7 +12,7 @@ const shader_source llmr::shaders[SHADER_COUNT] = { }, { "precision highp float;\nattribute vec2 a_pos;\nattribute vec2 a_tex;\nuniform mat4 u_matrix;\nuniform float u_size;\nvarying vec2 v_tex;\nvoid main ()\n{\n vec4 tmpvar_1;\n tmpvar_1.zw = vec2(0.0, 1.0);\n tmpvar_1.xy = a_pos;\n gl_Position = (u_matrix * tmpvar_1);\n gl_PointSize = u_size;\n v_tex = a_tex;\n}\n\n", - "precision highp float;\nuniform sampler2D u_image;\nuniform vec2 u_dimension;\nuniform vec4 u_color;\nuniform float u_size;\nvarying vec2 v_tex;\nvoid main ()\n{\n mediump vec2 tmpvar_1;\n tmpvar_1 = ((v_tex + (\n (gl_PointCoord - 0.5)\n * u_size)) / u_dimension);\n lowp vec4 tmpvar_2;\n tmpvar_2 = (texture2D (u_image, tmpvar_1) * u_color);\n gl_FragColor = tmpvar_2;\n}\n\n", + "precision highp float;\nuniform sampler2D u_image;\nuniform vec2 u_dimension;\nuniform float u_size;\nvarying vec2 v_tex;\nvoid main ()\n{\n mediump vec2 tmpvar_1;\n tmpvar_1 = ((v_tex + (\n (gl_PointCoord - 0.5)\n * u_size)) / u_dimension);\n lowp vec4 tmpvar_2;\n tmpvar_2 = texture2D (u_image, tmpvar_1);\n gl_FragColor = tmpvar_2;\n}\n\n", }, { "precision highp float;\nattribute vec2 a_pos;\nattribute vec2 a_extrude;\nattribute float a_linesofar;\nuniform mat4 u_matrix;\nuniform mat4 u_exmatrix;\nuniform float u_ratio;\nuniform vec2 u_linewidth;\nvarying vec2 v_normal;\nvarying float v_linesofar;\nvoid main ()\n{\n vec2 normal_1;\n vec2 tmpvar_2;\n tmpvar_2 = (vec2(mod (a_pos, 2.0)));\n normal_1.x = tmpvar_2.x;\n normal_1.y = sign((tmpvar_2.y - 0.5));\n v_normal = normal_1;\n vec4 tmpvar_3;\n tmpvar_3.zw = vec2(0.0, 0.0);\n tmpvar_3.xy = ((u_linewidth.x * a_extrude) * 0.015873);\n vec4 tmpvar_4;\n tmpvar_4.zw = vec2(0.0, 1.0);\n tmpvar_4.xy = floor((a_pos * 0.5));\n gl_Position = ((u_matrix * tmpvar_4) + (u_exmatrix * tmpvar_3));\n v_linesofar = (a_linesofar * u_ratio);\n}\n\n", diff --git a/src/style/sprite.cpp b/src/style/sprite.cpp index 93dd6bd248..fb3a959f98 100644 --- a/src/style/sprite.cpp +++ b/src/style/sprite.cpp @@ -31,35 +31,35 @@ Sprite::Sprite(Map &map, float pixelRatio) } void Sprite::load(const std::string& base_url) { - std::shared_ptr<Sprite> sprite = shared_from_this(); - - std::string suffix = (pixelRatio > 1 ? "@2x" : ""); - - platform::request_http(base_url + suffix + ".json", [sprite](platform::Response *res) { - if (res->code == 200) { - sprite->body.swap(res->body); - sprite->asyncParseJSON(); - } else { - fprintf(stderr, "failed to load sprite\n"); - } - }, map.getLoop()); - - platform::request_http(base_url + suffix + ".png", [sprite](platform::Response *res) { - if (res->code == 200) { - sprite->image.swap(res->body); - sprite->asyncParseImage(); - } else { - fprintf(stderr, "failed to load sprite image\n"); - } - }, map.getLoop()); + std::shared_ptr<Sprite> sprite = shared_from_this(); + + std::string suffix = (pixelRatio > 1 ? "@2x" : ""); + + platform::request_http(base_url + suffix + ".json", [sprite](platform::Response *res) { + if (res->code == 200) { + sprite->body.swap(res->body); + sprite->asyncParseJSON(); + } else { + fprintf(stderr, "failed to load sprite\n"); + } + }, map.getLoop()); + + platform::request_http(base_url + suffix + ".png", [sprite](platform::Response *res) { + if (res->code == 200) { + sprite->image.swap(res->body); + sprite->asyncParseImage(); + } else { + fprintf(stderr, "failed to load sprite image\n"); + } + }, map.getLoop()); } void Sprite::complete(std::shared_ptr<Sprite> &sprite) { - if (sprite->raster.isLoaded() && sprite->pos.size()) { - sprite->loaded = true; - sprite->map.update(); - fprintf(stderr, "sprite loaded\n"); - } + if (sprite->raster.isLoaded() && sprite->pos.size()) { + sprite->loaded = true; + sprite->map.update(); + fprintf(stderr, "sprite loaded\n"); + } } bool Sprite::isLoaded() const { |