summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKonstantin Käfer <mail@kkaefer.com>2014-05-19 10:48:11 +0200
committerKonstantin Käfer <mail@kkaefer.com>2014-05-19 10:48:36 +0200
commit13575330f7d32630f6dbf4244405c46b453152fe (patch)
treec5bc7f3a25774c2d8f6783d77fbd0654e0554acc
parent24bd0bb667e6c2570838cd64992ffa388e8fb941 (diff)
downloadqtlocation-mapboxgl-13575330f7d32630f6dbf4244405c46b453152fe.tar.gz
use sprite atlas
-rw-r--r--common/glfw_view.cpp29
-rw-r--r--include/llmr/geometry/sprite_atlas.hpp68
-rw-r--r--include/llmr/map/map.hpp2
-rw-r--r--include/llmr/map/tile_parser.hpp6
-rw-r--r--include/llmr/platform/platform.hpp5
-rw-r--r--include/llmr/renderer/icon_bucket.hpp4
-rw-r--r--include/llmr/style/style.hpp1
-rw-r--r--include/llmr/util/math.hpp16
-rw-r--r--include/llmr/util/raster.hpp2
-rw-r--r--include/llmr/util/rect.hpp5
-rw-r--r--src/geometry/sprite_atlas.cpp235
-rw-r--r--src/map/map.cpp15
-rw-r--r--src/map/tile_parser.cpp11
-rw-r--r--src/map/vector_tile_data.cpp2
-rw-r--r--src/renderer/icon_bucket.cpp19
-rw-r--r--src/renderer/painter_icon.cpp49
-rw-r--r--src/shader/icon.fragment.glsl2
-rw-r--r--src/shader/shaders_gl.cpp2
-rw-r--r--src/shader/shaders_gles2.cpp2
-rw-r--r--src/style/sprite.cpp52
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 {