Merge branch 'master' of github.com:mapbox/llmr-native

This commit is contained in:
Ansis Brammanis
2014-05-19 17:36:50 -04:00
40 changed files with 775 additions and 500 deletions

View File

@@ -373,7 +373,8 @@ module.exports = {
"layer": "place_label",
"field": "type",
"value": "city",
"type": "point"
"type": "point",
"size": 4
},
"place_label_city": {
"source": "mapbox streets",
@@ -452,116 +453,13 @@ module.exports = {
"feature_type": "point",
"type": "text"
},
"poi_airport": {
"poi": {
"source": "mapbox streets",
"layer": "poi_label",
"field": "maki",
"value": "airport",
"size": {
"x": 12,
"y": 12
},
"icon": "maki",
"size": 12,
"type": "point"
},
"poi_rail": {
"source": "mapbox streets",
"layer": "poi_label",
"field": "maki",
"value": "rail",
"size": {
"x": 12,
"y": 12
},
"type": "point"
},
"poi_fire_station": {
"source": "mapbox streets",
"layer": "poi_label",
"field": "maki",
"value": "fire-station",
"size": {
"x": 12,
"y": 12
},
"type": "point"
},
"poi_bus": {
"source": "mapbox streets",
"layer": "poi_label",
"field": "maki",
"value": "bus",
"size": {
"x": 12,
"y": 12
},
"type": "point"
},
"poi_restaurant": {
"source": "mapbox streets",
"layer": "poi_label",
"field": "maki",
"value": "restaurant",
"size": {
"x": 12,
"y": 12
},
"type": "point"
},
"poi_park": {
"source": "mapbox streets",
"layer": "poi_label",
"field": "maki",
"value": "park",
"size": {
"x": 12,
"y": 12
},
"type": "point"
},
"poi_playground": {
"source": "mapbox streets",
"layer": "poi_label",
"field": "maki",
"value": "playground",
"size": {
"x": 12,
"y": 12
},
"type": "point"
},
"poi_hospital": {
"source": "mapbox streets",
"layer": "poi_label",
"field": "maki",
"value": "hospital",
"size": {
"x": 12,
"y": 12
},
"type": "point"
},
"poi_cafe": {
"source": "mapbox streets",
"layer": "poi_label",
"field": "maki",
"value": "cafe",
"size": {
"x": 12,
"y": 12
},
"type": "point"
},
"poi_beer": {
"source": "mapbox streets",
"layer": "poi_label",
"field": "maki",
"value": "beer",
"size": {
"x": 12,
"y": 12
},
"type": "point"
}
},
"constants": {
"land": "#f8f4f0",
@@ -801,42 +699,12 @@ module.exports = {
}, {
"name": "road_label",
"bucket": "road_label"
}, {
"name": "poi_label",
"bucket": "poi_label"
}, {
"name": "water_label",
"bucket": "water_label"
}, {
"name": "poi_airport",
"bucket": "poi_airport"
}, {
"name": "poi_rail",
"bucket": "poi_rail"
}, {
"name": "poi_bus",
"bucket": "poi_bus"
}, {
"name": "poi_fire_station",
"bucket": "poi_fire_station"
}, {
"name": "poi_restaurant",
"bucket": "poi_restaurant"
}, {
"name": "poi_park",
"bucket": "poi_park"
}, {
"name": "poi_hospital",
"bucket": "poi_hospital"
}, {
"name": "poi_playground",
"bucket": "poi_playground"
}, {
"name": "poi_cafe",
"bucket": "poi_cafe"
}, {
"name": "poi_beer",
"bucket": "poi_beer"
"name": "poi",
"bucket": "poi"
}],
"classes": [{
"name": "default",
@@ -2010,45 +1878,7 @@ module.exports = {
0.75
],
},
"poi_airport": {
"color": "maki",
"image": "airport-12",
},
"poi_restaurant": {
"color": "maki",
"image": "restaurant-12",
},
"poi_bus": {
"color": "maki",
"image": "bus-12",
},
"poi_rail": {
"color": "maki",
"image": "rail-12",
},
"poi_fire_station": {
"color": "maki",
"image": "fire-station-12",
},
"poi_park": {
"color": "maki",
"image": "park-12",
},
"poi_hospital": {
"color": "maki",
"image": "hospital-12",
},
"poi_playground": {
"color": "maki",
"image": "playground-12",
},
"poi_cafe": {
"color": "maki",
"image": "cafe-12",
},
"poi_beer": {
"color": "maki",
"image": "beer-12",
"poi": {
},
"satellite": {
"type": "raster",

View File

@@ -204,6 +204,8 @@ namespace platform {
double elapsed() { return glfwGetTime(); }
void show_debug_image(std::string name, const char *data, size_t width, size_t height) {
glfwInit();
static GLFWwindow *debug_window = nullptr;
if (!debug_window) {
debug_window = glfwCreateWindow(width, height, name.c_str(), nullptr, nullptr);
@@ -231,5 +233,37 @@ 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 logical_width, size_t logical_height, size_t width, size_t height) {
glfwInit();
static GLFWwindow *debug_window = nullptr;
if (!debug_window) {
debug_window = glfwCreateWindow(logical_width, logical_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, logical_width, logical_height);
glfwMakeContextCurrent(debug_window);
int fb_width, fb_height;
glfwGetFramebufferSize(debug_window, &fb_width, &fb_height);
float x_scale = (float)fb_width / (float)width;
float y_scale = (float)fb_height / (float)height;
glPixelZoom(x_scale, -y_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);
}
}
}

View File

@@ -1,25 +1,25 @@
#ifndef LLMR_GEOMETRY_BUFFER
#define LLMR_GEOMETRY_BUFFER
#include <cstdlib>
#include <cassert>
#include <llmr/platform/gl.hpp>
#include <llmr/util/noncopyable.hpp>
#include <cstdlib>
#include <cassert>
#include <stdexcept>
namespace llmr {
template <
size_t item_size,
int bufferType = GL_ARRAY_BUFFER,
size_t defaultLength = 8192
size_t defaultLength = 8192,
bool retainAfterUpload = false
>
class Buffer : private util::noncopyable {
public:
~Buffer() {
if (array) {
free(array);
array = nullptr;
}
cleanup();
if (buffer != 0) {
glDeleteBuffers(1, &buffer);
buffer = 0;
@@ -33,18 +33,27 @@ public:
}
// Transfers this buffer to the GPU and binds the buffer to the GL context.
void bind() {
void bind(bool force = false) {
if (buffer == 0) {
glGenBuffers(1, &buffer);
glBindBuffer(bufferType, buffer);
force = true;
}
glBindBuffer(bufferType, buffer);
if (force) {
assert("Buffer was already deleted" && array != nullptr);
glBufferData(bufferType, pos, array, GL_STATIC_DRAW);
free(array);
array = nullptr;
} else {
glBindBuffer(bufferType, buffer);
if (!retainAfterUpload) {
cleanup();
}
}
}
void cleanup() {
if (array) {
free(array);
array = nullptr;
}
}
protected:
// increase the buffer size by at least /required/ bytes.
@@ -59,6 +68,17 @@ protected:
return static_cast<char *>(array) + (pos - itemSize);
}
// Get a pointer to the item at a given index.
inline void *getElement(size_t index) {
assert("Buffer was deleted" && array != nullptr);
if (index * itemSize >= pos) {
throw new std::runtime_error("Can't get element after array bounds");
} else {
return static_cast<char *>(array) + (index * itemSize);
}
}
public:
static const size_t itemSize = item_size;

View File

@@ -6,12 +6,13 @@
namespace llmr {
class IconVertexBuffer : public Buffer<
4 // 2 coordinates per vertex (== 4 bytes)
4 + // int16 x/y coordinates per vertex (== 4 bytes)
4 // uint16 x/y coordinates of icon in sprite (== 4 bytes)
> {
public:
typedef int16_t vertex_type;
void add(vertex_type x, vertex_type y);
void add(vertex_type x, vertex_type y, uint16_t tx, uint16_t ty);
};
}

View File

@@ -0,0 +1,74 @@
#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 SpritePosition;
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 a square icon. The getter also *creates* new square 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);
// Returns the coordinates of an image that is sourced from the sprite image.
// This getter does not create images, as the dimension of the texture us unknown if the
// sprite is not yet loaded. Instead, it returns a 0/0/0/0 rect.
Rect<dimension> getImage(const std::string &name, const Sprite &sprite);
// 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 getWidth() const { return width; }
inline float getHeight() const { return height; }
inline float getTextureWidth() const { return width * pixelRatio; }
inline float getTextureHeight() const { return height * pixelRatio; }
private:
void allocate();
Rect<SpriteAtlas::dimension> allocateImage(size_t width, size_t height);
void copy(const Rect<dimension> &dst, const SpritePosition &src, const Sprite &sprite);
public:
const dimension width = 0;
const dimension height = 0;
private:
std::mutex mtx;
float pixelRatio = 1.0f;
BinPack<dimension> bin;
std::map<std::string, Rect<dimension>> images;
std::set<std::string> uninitialized;
char *data = nullptr;
std::atomic<bool> dirty;
uint32_t texture = 0;
uint32_t filter = 0;
static const int buffer = 1;
};
};
#endif

View File

@@ -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;

View File

@@ -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,12 +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);
private:
const VectorTile vector_data;
VectorTileData& tile;
const Style& style;
GlyphAtlas& glyphAtlas;
SpriteAtlas &spriteAtlas;
Faces faces;
Placement placement;
};

View File

@@ -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 logical_width, size_t logical_height, size_t width, size_t height);
}
}

View File

@@ -20,7 +20,8 @@ class IconVertexBuffer;
class BucketDescription;
class IconShader;
class DotShader;
struct pbf;
class SpriteAtlas;
class VectorTileFeature;
class IconBucket : public Bucket {
public:
@@ -30,7 +31,7 @@ public:
virtual void render(Painter& painter, const std::string& layer_name, const Tile::ID& id);
virtual bool hasData() const;
void addGeometry(pbf& data);
void addFeature(const VectorTileFeature &feature, SpriteAtlas &sprite_atlas);
void drawIcons(IconShader& shader);
void drawIcons(DotShader& shader);

View File

@@ -13,12 +13,13 @@ public:
void setImage(int32_t image);
void setColor(const std::array<float, 4>& color);
void setPosition(const std::array<float, 2>& pos);
void setDimension(const std::array<float, 2>& dimension);
void setSize(float size);
void setRatio(float ratio);
private:
int32_t a_pos = -1;
int32_t a_tex = -1;
int32_t image = -1;
int32_t u_image = -1;
@@ -26,14 +27,14 @@ private:
std::array<float, 4> color = {{}};
int32_t u_color = -1;
std::array<float, 2> pos = {{}};
int32_t u_pos = -1;
std::array<float, 2> dimension = {{}};
int32_t u_dimension = -1;
float size = 0;
int32_t u_size = -1;
float ratio = 0;
int32_t u_ratio = -1;
};
}

View File

@@ -73,8 +73,9 @@ public:
CapType cap = CapType::None;
JoinType join = JoinType::None;
std::string font;
std::string text_field;
float font_size = 0.0f;
std::string field;
std::string icon;
float size = 0.0f;
float miter_limit = 2.0f;
float round_limit = 1.0f;
TextPathType path = TextPathType::Horizontal;

View File

@@ -65,7 +65,7 @@ struct IconClass {
std::array<FunctionProperty, 2> translate = {{ 0, 0 }};
TranslateAnchor translateAnchor = TranslateAnchor::Map;
FunctionProperty size;
Color color = {{ 0, 0, 0, 1 }};
Color color = {{ 1, 1, 1, 1 }};
FunctionProperty opacity = 1;
std::string image;
FunctionProperty radius = 0;
@@ -77,7 +77,7 @@ struct IconProperties {
std::array<float, 2> translate = {{ 0, 0 }};
TranslateAnchor translateAnchor = TranslateAnchor::Map;
float size = 0;
Color color = {{ 0, 0, 0, 1 }};
Color color = {{ 1, 1, 1, 1 }};
float opacity = 1.0;
std::string image;
float radius = 0;

View File

@@ -19,36 +19,28 @@ public:
explicit SpritePosition() {}
explicit SpritePosition(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint8_t pixelRatio = 1);
operator bool() const {
return !(width == 0 && height == 0 && x == 0 && y == 0);
}
uint16_t x = 0, y = 0;
uint16_t width = 0, height = 0;
uint8_t pixelRatio = 1;
};
class ImagePosition {
public:
explicit ImagePosition() {}
explicit ImagePosition(const vec2<uint16_t>& size, vec2<float> tl, vec2<float> br);
vec2<uint16_t> size = { 0, 0 };
vec2<float> tl = { 0, 0 };
vec2<float> br = { 0, 0 };
};
class Sprite : public std::enable_shared_from_this<Sprite> {
public:
Sprite(Map &map, float pixelRatio = 1);
void load(const std::string& base_url);
ImagePosition getPosition(const std::string& name, bool repeating = false) const;
const SpritePosition &getSpritePosition(const std::string& name) const;
bool isLoaded() const;
public:
const float pixelRatio;
Raster raster;
std::unique_ptr<util::Image> raster;
private:
void asyncParseJSON();

View File

@@ -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>

View File

@@ -8,6 +8,26 @@ namespace util {
std::string compress_png(int width, int height, void *rgba, bool flip = false);
class Image {
public:
Image(const std::string &img);
~Image();
inline const char *getData() const { return img; }
inline uint32_t getWidth() const { return width; }
inline uint32_t getHeight() const { return height; }
private:
// loaded image dimensions
uint32_t width = 0, height = 0;
// the raw image data
char *img = nullptr;
};
}
}

View File

@@ -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);
}
}
}

View File

@@ -3,6 +3,7 @@
#include <llmr/util/transition.hpp>
#include <llmr/util/texturepool.hpp>
#include <llmr/util/image.hpp>
#include <string>
#include <mutex>
@@ -45,10 +46,6 @@ public:
// texture opacity
double opacity = 0;
private:
// load raw pixels
void loadImage(const std::string& data);
private:
mutable std::mutex mtx;
@@ -62,7 +59,7 @@ private:
uint32_t filter = 0;
// the raw pixels
char *img = nullptr;
std::unique_ptr<util::Image> img;
// fade in transition
std::shared_ptr<util::transition> fade_transition = nullptr;

View File

@@ -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; }
};
}

View File

@@ -5,8 +5,14 @@
using namespace llmr;
void IconVertexBuffer::add(vertex_type x, vertex_type y) {
vertex_type *vertices = static_cast<vertex_type *>(addElement());
void IconVertexBuffer::add(vertex_type x, vertex_type y, uint16_t tx, uint16_t ty) {
void *data = addElement();
vertex_type *vertices = static_cast<vertex_type *>(data);
vertices[0] = x;
vertices[1] = y;
uint16_t *texture = static_cast<uint16_t *>(data);
texture[2] = tx;
texture[3] = ty;
}

View File

@@ -0,0 +1,263 @@
#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;
}
void copy_bitmap(const uint32_t *src, const int src_stride, const int src_x, const int src_y,
uint32_t *dst, const int dst_stride, const int dst_x, const int dst_y,
const int width, const int height) {
const int stride = width * sizeof(uint32_t);
src += src_y * src_stride + src_x;
dst += dst_y * dst_stride + dst_x;
for (int y = 0; y < height; y++, src += src_stride, dst += dst_stride) {
memcpy(dst, src, stride);
}
}
void draw_circle(uint32_t *dst, const int dst_stride, const int dst_x, const int dst_y,
const int width, const int height, const float blur,
const uint8_t r = 0xFF, const uint8_t g = 0xFF, const uint8_t b = 0xFF) {
const int sprite_stride = dst_stride;
const int radius = util::min(width, height);
for (int y = 0; y < height; y++) {
const int img_y = (dst_y + y) * sprite_stride + dst_x;
for (int x = 0; x < height; x++) {
const float dist = util::length(float(x) / radius - 0.5f, float(y) / radius - 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);
dst[img_y + x] = color;
}
}
}
Rect<SpriteAtlas::dimension> SpriteAtlas::allocateImage(size_t width, size_t height) {
// We have to allocate a new area in the bin, and store an empty image in it.
// Add a 1px border around every image.
Rect<dimension> rect = bin.allocate(width + 2 * buffer, height + 2 * buffer);
if (rect.w == 0) {
return rect;
}
rect.x += buffer;
rect.y += buffer;
rect.w -= 2 * buffer;
rect.h -= 2 * buffer;
return rect;
}
Rect<SpriteAtlas::dimension> SpriteAtlas::getIcon(const int size, const std::string &name) {
std::lock_guard<std::mutex> lock(mtx);
const std::string id = name + "-" + std::to_string(size);
auto rect_it = images.find(id);
if (rect_it != images.end()) {
return rect_it->second;
}
Rect<dimension> rect = allocateImage(size, size);
if (rect.w == 0) {
fprintf(stderr, "sprite atlas bitmap overflow\n");
return rect;
}
images.emplace(id, rect);
allocate();
// Draw an antialiased circle.
draw_circle(
reinterpret_cast<uint32_t *>(data),
width * pixelRatio,
rect.x * pixelRatio,
rect.y * pixelRatio,
size * pixelRatio,
size * pixelRatio,
1.0f / size / pixelRatio
);
uninitialized.emplace(id);
dirty = true;
return rect;
}
Rect<SpriteAtlas::dimension> SpriteAtlas::getImage(const std::string &name, const Sprite &sprite) {
std::lock_guard<std::mutex> lock(mtx);
auto rect_it = images.find(name);
if (rect_it != images.end()) {
return rect_it->second;
}
const SpritePosition &pos = sprite.getSpritePosition(name);
if (!pos.width || !pos.height) {
return Rect<dimension> { 0, 0, 0, 0 };
}
Rect<dimension> rect = allocateImage(pos.width / pos.pixelRatio, pos.height / pos.pixelRatio);
if (rect.w == 0) {
fprintf(stderr, "sprite atlas bitmap overflow\n");
return rect;
}
images.emplace(name, rect);
copy(rect, pos, sprite);
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));
}
}
void SpriteAtlas::copy(const Rect<dimension> &dst, const SpritePosition &src, const Sprite &sprite) {
if (!sprite.raster) return;
const uint32_t *src_img = reinterpret_cast<const uint32_t *>(sprite.raster->getData());
allocate();
uint32_t *dst_img = reinterpret_cast<uint32_t *>(data);
copy_bitmap(
/* source buffer */ src_img,
/* source stride */ sprite.raster->getWidth(),
/* source x */ src.x,
/* source y */ src.y,
/* dest buffer */ dst_img,
/* dest stride */ width * pixelRatio,
/* dest x */ dst.x * pixelRatio,
/* dest y */ dst.y * pixelRatio,
/* icon dimension */ src.width,
/* icon dimension */ src.height
);
dirty = true;
}
void SpriteAtlas::update(const Sprite &sprite) {
if (!sprite.isLoaded()) return;
SpriteAtlas &atlas = *this;
std::erase_if(uninitialized, [&sprite, &atlas](const std::string &name) {
Rect<dimension> dst = atlas.getImage(name, sprite);
const SpritePosition &src = sprite.getSpritePosition(name);
if (!src) {
return true;
}
if (src.width == dst.w * atlas.pixelRatio && src.height == dst.h * atlas.pixelRatio && src.pixelRatio == atlas.pixelRatio) {
atlas.copy(dst, src, sprite);
return true;
} else {
fprintf(stderr, "sprite icon dimension mismatch\n");
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, height, width * pixelRatio, height * pixelRatio);
dirty = false;
}
};
SpriteAtlas::~SpriteAtlas() {
std::lock_guard<std::mutex> lock(mtx);
if (data) {
free(data);
data = nullptr;
}
}

View File

@@ -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();

View File

@@ -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,6 +121,15 @@ 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) {
FilteredVectorTileLayer filtered_layer(layer, bucket_desc);
for (const pbf& feature_pbf : filtered_layer) {
if (obsolete()) return;
bucket->addFeature({ feature_pbf, layer }, std::forward<Args>(args)...);
}
}
std::unique_ptr<Bucket> TileParser::createFillBucket(const VectorTileLayer& layer, const BucketDescription& bucket_desc) {
std::unique_ptr<FillBucket> bucket = std::make_unique<FillBucket>(
tile.fillVertexBuffer, tile.triangleElementsBuffer, tile.lineElementsBuffer, bucket_desc);
@@ -137,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);
addBucketFeatures(bucket, layer, bucket_desc, spriteAtlas);
return obsolete() ? nullptr : std::move(bucket);
}
@@ -165,12 +175,6 @@ std::unique_ptr<Bucket> TileParser::createTextBucket(const VectorTileLayer& laye
std::unique_ptr<TextBucket> bucket = std::make_unique<TextBucket>(
tile.textVertexBuffer, tile.triangleElementsBuffer, bucket_desc, placement);
FilteredVectorTileLayer filtered_layer(layer, bucket_desc);
for (const pbf& feature_pbf : filtered_layer) {
if (obsolete()) return nullptr;
bucket->addFeature({ feature_pbf, layer }, faces, shaping);
}
addBucketFeatures(bucket, layer, bucket_desc, faces, shaping);
return std::move(bucket);
}

View File

@@ -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();

View File

@@ -5,6 +5,7 @@
#include <llmr/renderer/painter.hpp>
#include <llmr/style/style.hpp>
#include <llmr/style/sprite.hpp>
#include <llmr/map/vector_tile.hpp>
#include <llmr/platform/gl.hpp>
@@ -22,13 +23,32 @@ IconBucket::IconBucket(IconVertexBuffer& vertexBuffer,
vertex_start(vertexBuffer.index()) {
}
void IconBucket::addGeometry(pbf& geom) {
void IconBucket::addFeature(const VectorTileFeature &feature, SpriteAtlas &sprite_atlas) {
std::string field;
if (geometry.field.size()) {
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());
return;
}
field = toString(field_it->second);
} else {
field = "<circle>";
}
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;
Geometry geometry(geom);
int32_t x, y;
while ((cmd = geometry.next(x, y)) != Geometry::end) {
if (cmd == Geometry::move_to) {
vertexBuffer.add(x, y);
vertexBuffer.add(x, y, tx, ty);
} else {
fprintf(stderr, "other command than move_to in icon geometry\n");
}

View File

@@ -2,6 +2,7 @@
#include <llmr/renderer/fill_bucket.hpp>
#include <llmr/map/map.hpp>
#include <llmr/style/sprite.hpp>
#include <llmr/geometry/sprite_atlas.hpp>
using namespace llmr;
@@ -73,16 +74,17 @@ void Painter::renderFill(FillBucket& bucket, const std::string& layer_name, cons
if ((fill_color[3] >= 1.0f) == (pass == Opaque)) {
auto &sprite = map.getStyle().sprite;
if (properties.image.size() && sprite && sprite->isLoaded()) {
// Draw texture fill
ImagePosition imagePos = sprite->getPosition(properties.image, true);
if (properties.image.size() && sprite) {
auto &spriteAtlas = map.getSpriteAtlas();
Rect<uint16_t> imagePos = spriteAtlas.getImage(properties.image, *sprite);
float factor = 8.0 / std::pow(2, map.getState().getIntegerZoom() - id.z);
float mix = std::fmod(map.getState().getZoom(), 1.0);
std::array<float, 2> imageSize = {{
imagePos.size.x * factor,
imagePos.size.y *factor
imagePos.w * factor,
imagePos.h * factor
}
};
@@ -96,11 +98,17 @@ void Painter::renderFill(FillBucket& bucket, const std::string& layer_name, cons
patternShader->setMatrix(vtxMatrix);
patternShader->setOffset(offset);
patternShader->setPatternSize(imageSize);
patternShader->setPatternTopLeft({{ imagePos.tl.x, imagePos.tl.y }});
patternShader->setPatternBottomRight({{ imagePos.br.x, imagePos.br.y }});
patternShader->setPatternTopLeft({{
float(imagePos.x) / spriteAtlas.getWidth(),
float(imagePos.y) / spriteAtlas.getHeight(),
}});
patternShader->setPatternBottomRight({{
float(imagePos.x + imagePos.w) / spriteAtlas.getWidth(),
float(imagePos.y + imagePos.h) / spriteAtlas.getHeight(),
}});
patternShader->setColor(fill_color);
patternShader->setMix(mix);
sprite->raster.bind(true);
spriteAtlas.bind(true);
// Draw the actual triangles into the color & stencil buffer.
glDepthRange(strata + strata_epsilon, 1.0f);

View File

@@ -24,60 +24,29 @@ void Painter::renderIcon(IconBucket& bucket, const std::string& layer_name, cons
color[2] *= properties.opacity;
color[3] *= properties.opacity;
auto &sprite = map.getStyle().sprite;
SpritePosition spritePos;
if (properties.image.length() && sprite && sprite->isLoaded()) {
std::string sized_image = properties.image;
if (properties.size) {
sized_image.append("-");
sized_image.append(std::to_string(static_cast<int>(std::round(properties.size))));
}
spritePos = sprite->getSpritePosition(sized_image);
}
const mat4 &vtxMatrix = translatedMatrix(properties.translate, id, properties.translateAnchor);
if (!spritePos.width || !spritePos.height) {
useProgram(dotShader->program);
dotShader->setMatrix(vtxMatrix);
dotShader->setColor(color);
SpriteAtlas &spriteAtlas = map.getSpriteAtlas();
const float iconSize = (properties.radius ? properties.radius * 2 : 8) * map.getState().getPixelRatio();
dotShader->setSize(iconSize);
useProgram(iconShader->program);
iconShader->setMatrix(vtxMatrix);
iconShader->setColor(color);
iconShader->setImage(0);
iconShader->setRatio(map.getState().getPixelRatio());
iconShader->setDimension({{
spriteAtlas.getTextureWidth(),
spriteAtlas.getTextureHeight(),
}});
spriteAtlas.bind(map.getState().isChanging());
const float iconSize = bucket.geometry.size * map.getState().getPixelRatio();
iconShader->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->setPosition({{
spritePos.x + (float)spritePos.width / 2.0f,
spritePos.y + (float)spritePos.height / 2.0f,
}});
iconShader->setDimension({{
static_cast<float>(sprite->raster.width),
static_cast<float>(sprite->raster.height)
}});
sprite->raster.bind(map.getState().isChanging());
const float iconSize = util::max(spritePos.width, spritePos.height) + 2;
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);
}

View File

@@ -28,7 +28,7 @@ void Painter::renderText(TextBucket& bucket, const std::string& layer_name, cons
}
// If layerStyle.size > bucket.info.fontSize then labels may collide
float fontSize = std::fmin(properties.size, bucket.geom_desc.font_size);
float fontSize = std::fmin(properties.size, bucket.geom_desc.size);
matrix::scale(exMatrix, exMatrix, fontSize / 24.0f, fontSize / 24.0f, 1.0f);
const mat4 &vtxMatrix = translatedMatrix(properties.translate, id, properties.translateAnchor);
@@ -47,7 +47,7 @@ void Painter::renderText(TextBucket& bucket, const std::string& layer_name, cons
float angle = std::round((map.getState().getAngle() + rotate) / M_PI * 128);
// adjust min/max zooms for variable font sies
float zoomAdjust = log(fontSize / bucket.geom_desc.font_size) / log(2);
float zoomAdjust = log(fontSize / bucket.geom_desc.size) / log(2);
textShader->setAngle((int32_t)(angle + 256) % 256);
textShader->setFlip(bucket.geom_desc.path == TextPathType::Curve ? 1 : 0);

View File

@@ -102,7 +102,7 @@ void TextBucket::addGlyphs(const PlacedGlyphs &glyphs, float placementZoom,
void TextBucket::addFeature(const VectorTileFeature &feature,
const IndexedFaces &faces,
const std::map<Value, Shaping> &shapings) {
auto it_prop = feature.properties.find(geom_desc.text_field);
auto it_prop = feature.properties.find(geom_desc.field);
if (it_prop == feature.properties.end()) {
// feature does not have the correct property
return;

View File

@@ -32,7 +32,7 @@ DotShader::DotShader()
void DotShader::bind(char *offset) {
glEnableVertexAttribArray(a_pos);
glVertexAttribPointer(a_pos, 2, GL_SHORT, false, 0, offset);
glVertexAttribPointer(a_pos, 2, GL_SHORT, false, 8, offset);
}
void DotShader::setColor(const std::array<float, 4>& new_color) {

View File

@@ -1,10 +1,11 @@
uniform sampler2D u_image;
uniform vec2 u_pos;
uniform vec2 u_dimension;
uniform vec4 u_color;
uniform float u_size;
varying vec2 v_tex;
void main() {
vec2 pos = (u_pos + (gl_PointCoord - 0.5) * u_size) / u_dimension;
gl_FragColor = texture2D(u_image, pos) * u_color;
vec2 pos = (v_tex + (gl_PointCoord - 0.5) * u_size) / u_dimension;
gl_FragColor = u_color * texture2D(u_image, pos);
}

View File

@@ -1,9 +1,14 @@
attribute vec2 a_pos;
attribute vec2 a_tex;
uniform mat4 u_matrix;
uniform float u_size;
uniform float u_ratio;
varying vec2 v_tex;
void main() {
gl_Position = u_matrix * vec4(a_pos, 0, 1);
gl_PointSize = u_size;
v_tex = a_tex * u_ratio;
}

View File

@@ -17,25 +17,29 @@ IconShader::IconShader()
}
a_pos = glGetAttribLocation(program, "a_pos");
a_tex = glGetAttribLocation(program, "a_tex");
u_matrix = glGetUniformLocation(program, "u_matrix");
u_color = glGetUniformLocation(program, "u_color");
u_size = glGetUniformLocation(program, "u_size");
u_pos = glGetUniformLocation(program, "u_pos");
u_ratio = glGetUniformLocation(program, "u_ratio");
u_dimension = glGetUniformLocation(program, "u_dimension");
// fprintf(stderr, "IconShader:\n");
// fprintf(stderr, " - u_matrix: %d\n", u_matrix);
// fprintf(stderr, " - u_color: %d\n", u_color);
// fprintf(stderr, " - u_size: %d\n", u_size);
// fprintf(stderr, " - u_pos: %d\n", u_pos);
// fprintf(stderr, " - u_ratio: %d\n", u_ratio);
// fprintf(stderr, " - u_dimension: %d\n", u_dimension);
// fprintf(stderr, " - u_image: %d\n", u_image);
}
void IconShader::bind(char *offset) {
glEnableVertexAttribArray(a_pos);
glVertexAttribPointer(a_pos, 2, GL_SHORT, false, 0, offset);
glVertexAttribPointer(a_pos, 2, GL_SHORT, false, 8, offset);
glEnableVertexAttribArray(a_tex);
glVertexAttribPointer(a_tex, 2, GL_UNSIGNED_SHORT, false, 8, offset + 4);
}
void IconShader::setImage(int32_t new_image) {
@@ -59,10 +63,10 @@ void IconShader::setSize(float new_size) {
}
}
void IconShader::setPosition(const std::array<float, 2>& new_pos) {
if (pos != new_pos) {
glUniform2fv(u_pos, 1, new_pos.data());
pos = new_pos;
void IconShader::setRatio(float new_ratio) {
if (ratio != new_ratio) {
glUniform1f(u_ratio, new_ratio);
ratio = new_ratio;
}
}

View File

@@ -11,8 +11,8 @@ const shader_source llmr::shaders[SHADER_COUNT] = {
"#version 120\nuniform vec4 u_color;\nuniform float u_blur;\nvoid main ()\n{\n vec2 x_1;\n x_1 = (gl_PointCoord - 0.5);\n float tmpvar_2;\n tmpvar_2 = clamp (((\n sqrt(dot (x_1, x_1))\n - 0.5) / (\n (0.5 - u_blur)\n - 0.5)), 0.0, 1.0);\n gl_FragColor = (u_color * (tmpvar_2 * (tmpvar_2 * \n (3.0 - (2.0 * tmpvar_2))\n )));\n}\n\n",
},
{
"#version 120\nattribute vec2 a_pos;\nuniform mat4 u_matrix;\nuniform float u_size;\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}\n\n",
"#version 120\nuniform sampler2D u_image;\nuniform vec2 u_pos;\nuniform vec2 u_dimension;\nuniform vec4 u_color;\nuniform float u_size;\nvoid main ()\n{\n gl_FragColor = (texture2D (u_image, ((u_pos + \n ((gl_PointCoord - 0.5) * u_size)\n ) / u_dimension)) * u_color);\n}\n\n",
"#version 120\nattribute vec2 a_pos;\nattribute vec2 a_tex;\nuniform mat4 u_matrix;\nuniform float u_size;\nuniform float u_ratio;\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 * u_ratio);\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 = (u_color * texture2D (u_image, ((v_tex + \n ((gl_PointCoord - 0.5) * u_size)\n ) / 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",

View File

@@ -11,8 +11,8 @@ const shader_source llmr::shaders[SHADER_COUNT] = {
"precision highp float;\nuniform vec4 u_color;\nuniform float u_blur;\nvoid main ()\n{\n mediump vec2 x_1;\n x_1 = (gl_PointCoord - 0.5);\n mediump float tmpvar_2;\n tmpvar_2 = clamp (((\n sqrt(dot (x_1, x_1))\n - 0.5) / (\n (0.5 - u_blur)\n - 0.5)), 0.0, 1.0);\n gl_FragColor = (u_color * (tmpvar_2 * (tmpvar_2 * \n (3.0 - (2.0 * tmpvar_2))\n )));\n}\n\n",
},
{
"precision highp float;\nattribute vec2 a_pos;\nuniform mat4 u_matrix;\nuniform float u_size;\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}\n\n",
"precision highp float;\nuniform sampler2D u_image;\nuniform vec2 u_pos;\nuniform vec2 u_dimension;\nuniform vec4 u_color;\nuniform float u_size;\nvoid main ()\n{\n mediump vec2 tmpvar_1;\n tmpvar_1 = ((u_pos + (\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;\nattribute vec2 a_pos;\nattribute vec2 a_tex;\nuniform mat4 u_matrix;\nuniform float u_size;\nuniform float u_ratio;\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 * u_ratio);\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 = (u_color * 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",

View File

@@ -14,6 +14,6 @@ std::ostream& llmr::operator<<(std::ostream& os, const BucketDescription& bucket
os << " - cap: " << (uint32_t)bucket.geometry.cap << std::endl;
os << " - join: " << (uint32_t)bucket.geometry.join << std::endl;
os << " - font: " << bucket.geometry.font << std::endl;
os << " - font_size: " << bucket.geometry.font_size << std::endl;
os << " - size: " << bucket.geometry.size << std::endl;
return os;
}

View File

@@ -5,6 +5,7 @@
#include <string>
#include <llmr/platform/platform.hpp>
#include <llmr/util/uv.hpp>
#include <llmr/util/std.hpp>
#include <rapidjson/document.h>
@@ -18,48 +19,43 @@ SpritePosition::SpritePosition(uint16_t x, uint16_t y, uint16_t width, uint16_t
pixelRatio(pixelRatio) {
}
ImagePosition::ImagePosition(const vec2<uint16_t>& size, vec2<float> tl, vec2<float> br)
: size(size),
tl(tl),
br(br) {}
Sprite::Sprite(Map &map, float pixelRatio)
: pixelRatio(pixelRatio),
raster(map.getTexturepool()),
raster(),
map(map),
loaded(false) {
}
void Sprite::load(const std::string& base_url) {
std::shared_ptr<Sprite> sprite = shared_from_this();
std::shared_ptr<Sprite> sprite = shared_from_this();
std::string suffix = (pixelRatio > 1 ? "@2x" : "");
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 + ".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());
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 && sprite->pos.size()) {
sprite->loaded = true;
sprite->map.update();
fprintf(stderr, "sprite loaded\n");
}
}
bool Sprite::isLoaded() const {
@@ -75,7 +71,7 @@ void Sprite::asyncParseJSON() {
}
void Sprite::parseImage(std::shared_ptr<Sprite> &sprite) {
sprite->raster.load(sprite->image);
sprite->raster = std::make_unique<util::Image>(sprite->image);
sprite->image.clear();
}
@@ -107,34 +103,6 @@ void Sprite::parseJSON(std::shared_ptr<Sprite> &sprite) {
}
}
ImagePosition Sprite::getPosition(const std::string& name, bool repeating) const {
if (!isLoaded()) return ImagePosition {};
// `repeating` indicates that the image will be used in a repeating pattern
// repeating pattern images are assumed to have a 1px padding that mirrors the opposite edge
// positions for repeating images are adjusted to exclude the edge
int8_t offset = repeating ? 1 : 0;
auto it = pos.find(name);
if (it == pos.end()) return ImagePosition {};
const SpritePosition& pos = it->second;
return ImagePosition {
{
pos.width,
pos.height
},
{
(float)(pos.x + offset) / raster.width,
(float)(pos.y + offset) / raster.height
},
{
(float)(pos.x + pos.width - 2 * offset) / raster.width,
(float)(pos.y + pos.height - 2 * offset) / raster.height
}
};
}
const SpritePosition &Sprite::getSpritePosition(const std::string& name) const {
if (!isLoaded()) return empty;
auto it = pos.find(name);

View File

@@ -83,15 +83,15 @@ BucketDescription StyleParser::parseBucket(JSVal value) {
} else {
throw Style::exception("font stack must be a string");
}
} else if (name == "fontSize") {
} else if (name == "fontSize" || name == "size") {
if (value.IsNumber()) {
bucket.geometry.font_size = value.GetDouble();
bucket.geometry.size = value.GetDouble();
} else {
throw Style::exception("font size must be a number");
}
} else if (name == "text_field") {
} else if (name == "text_field" || name == "icon") {
if (value.IsString()) {
bucket.geometry.text_field = { value.GetString(), value.GetStringLength() };
bucket.geometry.field = { value.GetString(), value.GetStringLength() };
} else {
throw Style::exception("text field must be a string");
}

View File

@@ -278,7 +278,7 @@ void Placement::addFeature(TextBucket& bucket,
const float textMinDistance = info.textMinDistance;
const float rotate = info.rotate;
const float fontScale =
(tileExtent / util::tileSize) / (glyphSize / info.font_size);
(tileExtent / util::tileSize) / (glyphSize / info.size);
const float advance = measureText(faces, shaping);
Anchors anchors;

View File

@@ -1,6 +1,8 @@
#include <llmr/util/image.hpp>
#include <png.h>
#include <cassert>
std::string llmr::util::compress_png(int width, int height, void *rgba, bool flip) {
png_voidp error_ptr = 0;
@@ -48,3 +50,127 @@ std::string llmr::util::compress_png(int width, int height, void *rgba, bool fli
return result;
}
using namespace llmr::util;
struct Buffer {
Buffer(const std::string& data)
: data(data.data()), length(data.size()) {}
const char *const data = 0;
const size_t length = 0;
size_t pos = 0;
};
void readCallback(png_structp png, png_bytep data, png_size_t length) {
Buffer *reader = static_cast<Buffer *>(png_get_io_ptr(png));
// Read `length` bytes into `data`.
if (reader->pos + length > reader->length) {
png_error(png, "Read Error");
} else {
memcpy(data, reader->data + reader->pos, length);
reader->pos += length;
}
}
void errorHandler(png_structp, png_const_charp error_msg) {
throw std::runtime_error(error_msg);
}
void warningHandler(png_structp, png_const_charp error_msg) {
fprintf(stderr, "PNG: %s\n", error_msg);
}
Image::Image(const std::string &data) {
Buffer buffer(data);
if (buffer.length < 8 || !png_check_sig((const png_bytep)buffer.data, 8)) {
fprintf(stderr, "image is not a valid PNG image\n");
return;
}
png_structp png = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, errorHandler, warningHandler);
assert(png);
png_infop info = png_create_info_struct(png);
assert(info);
int depth, color, interlace;
try {
png_set_read_fn(png, (png_voidp)&buffer, readCallback);
png_read_info(png, info);
png_get_IHDR(png, info, (png_uint_32*)&width, (png_uint_32*)&height, &depth, &color, &interlace, nullptr, nullptr);
bool alpha = (color & PNG_COLOR_MASK_ALPHA) || png_get_valid(png, info, PNG_INFO_tRNS);
// From http://trac.mapnik.org/browser/trunk/src/png_reader.cpp
if (color == PNG_COLOR_TYPE_PALETTE)
png_set_expand(png);
if (color == PNG_COLOR_TYPE_GRAY)
png_set_expand(png);
if (png_get_valid(png, info, PNG_INFO_tRNS))
png_set_expand(png);
if (depth == 16)
png_set_strip_16(png);
if (depth < 8)
png_set_packing(png);
if (color == PNG_COLOR_TYPE_GRAY ||
color == PNG_COLOR_TYPE_GRAY_ALPHA)
png_set_gray_to_rgb(png);
if (interlace == PNG_INTERLACE_ADAM7)
png_set_interlace_handling(png);
// Always add an alpha channel.
if (!alpha) {
png_set_add_alpha(png, 0xFF, PNG_FILLER_AFTER);
}
double gamma;
if (png_get_gAMA(png, info, &gamma))
png_set_gamma(png, 2.2, gamma);
png_read_update_info(png, info);
png_size_t rowbytes = png_get_rowbytes(png, info);
assert(width * 4 == rowbytes);
img = (char *)malloc(width * height * 4);
char *surface = img;
assert(surface);
struct ptrs {
ptrs(size_t count) : rows(new png_bytep[count]) {}
~ptrs() { delete[] rows; }
png_bytep *rows = nullptr;
} pointers(height);
for (unsigned i = 0; i < height; ++i) {
pointers.rows[i] = (png_bytep)(surface + (i * rowbytes));
}
// Read image data
png_read_image(png, pointers.rows);
png_read_end(png, nullptr);
png_destroy_read_struct(&png, &info, nullptr);
} catch (std::exception& e) {
fprintf(stderr, "loading PNG failed: %s\n", e.what());
png_destroy_read_struct(&png, &info, nullptr);
if (img) {
free(img);
img = nullptr;
}
width = 0;
height = 0;
}
}
Image::~Image() {
if (img) {
free(img);
img = nullptr;
}
}

View File

@@ -8,6 +8,7 @@
#include <llmr/platform/gl.hpp>
#include <llmr/util/time.hpp>
#include <llmr/util/uv.hpp>
#include <llmr/util/std.hpp>
#include <png.h>
@@ -18,9 +19,7 @@ Raster::Raster(Texturepool &texturepool)
{}
Raster::~Raster() {
if (img && !textured) {
free(img);
} else if (textured) {
if (textured) {
texturepool.removeTextureID(texture);
}
}
@@ -31,130 +30,17 @@ bool Raster::isLoaded() const {
}
bool Raster::load(const std::string &data) {
loadImage(data);
img = std::make_unique<util::Image>(data);
width = img->getWidth();
height = img->getHeight();
std::lock_guard<std::mutex> lock(mtx);
if (img) {
if (img->getData()) {
loaded = true;
}
return loaded;
}
struct Buffer {
Buffer(const std::string& data)
: data(data.data()), length(data.size()) {}
const char *const data = 0;
const size_t length = 0;
size_t pos = 0;
};
void readCallback(png_structp png, png_bytep data, png_size_t length) {
Buffer *reader = static_cast<Buffer *>(png_get_io_ptr(png));
// Read `length` bytes into `data`.
if (reader->pos + length > reader->length) {
png_error(png, "Read Error");
} else {
memcpy(data, reader->data + reader->pos, length);
reader->pos += length;
}
}
void errorHandler(png_structp, png_const_charp error_msg) {
throw std::runtime_error(error_msg);
}
void warningHandler(png_structp, png_const_charp error_msg) {
fprintf(stderr, "PNG: %s\n", error_msg);
}
void Raster::loadImage(const std::string& data) {
std::lock_guard<std::mutex> lock(mtx);
Buffer buffer(data);
if (buffer.length < 8 || !png_check_sig((const png_bytep)buffer.data, 8)) {
fprintf(stderr, "image is not a valid PNG image\n");
return;
}
png_structp png = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, errorHandler, warningHandler);
assert(png);
png_infop info = png_create_info_struct(png);
assert(info);
int depth, color, interlace;
try {
png_set_read_fn(png, (png_voidp)&buffer, readCallback);
png_read_info(png, info);
png_get_IHDR(png, info, (png_uint_32*)&width, (png_uint_32*)&height, &depth, &color, &interlace, nullptr, nullptr);
bool alpha = (color & PNG_COLOR_MASK_ALPHA) || png_get_valid(png, info, PNG_INFO_tRNS);
// From http://trac.mapnik.org/browser/trunk/src/png_reader.cpp
if (color == PNG_COLOR_TYPE_PALETTE)
png_set_expand(png);
if (color == PNG_COLOR_TYPE_GRAY)
png_set_expand(png);
if (png_get_valid(png, info, PNG_INFO_tRNS))
png_set_expand(png);
if (depth == 16)
png_set_strip_16(png);
if (depth < 8)
png_set_packing(png);
if (color == PNG_COLOR_TYPE_GRAY ||
color == PNG_COLOR_TYPE_GRAY_ALPHA)
png_set_gray_to_rgb(png);
if (interlace == PNG_INTERLACE_ADAM7)
png_set_interlace_handling(png);
// Always add an alpha channel.
if (!alpha) {
png_set_add_alpha(png, 0xFF, PNG_FILLER_AFTER);
}
double gamma;
if (png_get_gAMA(png, info, &gamma))
png_set_gamma(png, 2.2, gamma);
png_read_update_info(png, info);
png_size_t rowbytes = png_get_rowbytes(png, info);
assert(width * 4 == rowbytes);
img = (char *)malloc(width * height * 4);
char *surface = img;
assert(surface);
struct ptrs {
ptrs(size_t count) : rows(new png_bytep[count]) {}
~ptrs() { delete[] rows; }
png_bytep *rows = nullptr;
} pointers(height);
for (unsigned i = 0; i < height; ++i) {
pointers.rows[i] = (png_bytep)(surface + (i * rowbytes));
}
// Read image data
png_read_image(png, pointers.rows);
png_read_end(png, nullptr);
png_destroy_read_struct(&png, &info, nullptr);
} catch (std::exception& e) {
fprintf(stderr, "loading PNG failed: %s\n", e.what());
png_destroy_read_struct(&png, &info, nullptr);
if (img) {
free(img);
img = nullptr;
}
width = 0;
height = 0;
}
}
void Raster::bind(bool linear) {
if (!width || !height) {
@@ -167,9 +53,8 @@ void Raster::bind(bool linear) {
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);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, img);
free(img);
img = nullptr;
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, img->getData());
img.reset();
textured = true;
} else if (textured) {
glBindTexture(GL_TEXTURE_2D, texture);