summaryrefslogtreecommitdiff
path: root/src/mbgl/geometry
diff options
context:
space:
mode:
Diffstat (limited to 'src/mbgl/geometry')
-rw-r--r--src/mbgl/geometry/anchor.hpp25
-rw-r--r--src/mbgl/geometry/binpack.hpp100
-rw-r--r--src/mbgl/geometry/buffer.hpp118
-rw-r--r--src/mbgl/geometry/debug_font_buffer.cpp42
-rw-r--r--src/mbgl/geometry/debug_font_buffer.hpp17
-rw-r--r--src/mbgl/geometry/debug_font_data.hpp206
-rw-r--r--src/mbgl/geometry/elements_buffer.cpp21
-rw-r--r--src/mbgl/geometry/elements_buffer.hpp64
-rw-r--r--src/mbgl/geometry/fill_buffer.cpp13
-rw-r--r--src/mbgl/geometry/fill_buffer.hpp21
-rw-r--r--src/mbgl/geometry/geometry.hpp77
-rw-r--r--src/mbgl/geometry/glyph_atlas.cpp167
-rw-r--r--src/mbgl/geometry/glyph_atlas.hpp54
-rw-r--r--src/mbgl/geometry/icon_buffer.cpp35
-rw-r--r--src/mbgl/geometry/icon_buffer.hpp22
-rw-r--r--src/mbgl/geometry/line_buffer.cpp23
-rw-r--r--src/mbgl/geometry/line_buffer.hpp39
-rw-r--r--src/mbgl/geometry/resample.cpp62
-rw-r--r--src/mbgl/geometry/resample.hpp13
-rw-r--r--src/mbgl/geometry/sprite_atlas.cpp261
-rw-r--r--src/mbgl/geometry/sprite_atlas.hpp82
-rw-r--r--src/mbgl/geometry/static_vertex_buffer.cpp14
-rw-r--r--src/mbgl/geometry/static_vertex_buffer.hpp26
-rw-r--r--src/mbgl/geometry/text_buffer.cpp33
-rw-r--r--src/mbgl/geometry/text_buffer.hpp25
-rw-r--r--src/mbgl/geometry/vao.cpp55
-rw-r--r--src/mbgl/geometry/vao.hpp73
27 files changed, 1688 insertions, 0 deletions
diff --git a/src/mbgl/geometry/anchor.hpp b/src/mbgl/geometry/anchor.hpp
new file mode 100644
index 0000000000..d30394f0b9
--- /dev/null
+++ b/src/mbgl/geometry/anchor.hpp
@@ -0,0 +1,25 @@
+#ifndef MBGL_GEOMETRY_ANCHOR
+#define MBGL_GEOMETRY_ANCHOR
+
+#include <vector>
+
+namespace mbgl {
+
+struct Anchor {
+ float x = 0.0f;
+ float y = 0.0f;
+ float angle = 0.0f;
+ float scale = 0.0f;
+ int segment = -1;
+
+ explicit Anchor(float x_, float y_, float angle_, float scale_)
+ : x(x_), y(y_), angle(angle_), scale(scale_) {}
+ explicit Anchor(float x_, float y_, float angle_, float scale_, int segment_)
+ : x(x_), y(y_), angle(angle_), scale(scale_), segment(segment_) {}
+};
+
+typedef std::vector<Anchor> Anchors;
+
+}
+
+#endif \ No newline at end of file
diff --git a/src/mbgl/geometry/binpack.hpp b/src/mbgl/geometry/binpack.hpp
new file mode 100644
index 0000000000..9aadaa202c
--- /dev/null
+++ b/src/mbgl/geometry/binpack.hpp
@@ -0,0 +1,100 @@
+#ifndef MBGL_GEOMETRY_BINPACK
+#define MBGL_GEOMETRY_BINPACK
+
+#include <mbgl/util/noncopyable.hpp>
+#include <mbgl/util/rect.hpp>
+#include <cstdint>
+#include <list>
+
+namespace mbgl {
+
+template <typename T>
+class BinPack : private util::noncopyable {
+public:
+ BinPack(T width, T height)
+ : free(1, Rect<uint16_t>{ 0, 0, width, height }) {}
+public:
+ Rect<T> allocate(T width, T height) {
+ // Find the smallest free rect angle
+ auto smallest = free.end();
+ for (auto it = free.begin(); it != free.end(); ++it) {
+ const Rect<T>& ref = *it;
+ const Rect<T>& rect = *smallest;
+ if (width <= ref.w && height <= ref.h) {
+ if (smallest == free.end() || (ref.y <= rect.y && ref.x <= rect.x)) {
+ smallest = it;
+ }
+ }
+ }
+
+ if (smallest == free.end()) {
+ // There's no space left for this char.
+ return Rect<uint16_t>{ 0, 0, 0, 0 };
+ } else {
+ Rect<T> rect = *smallest;
+ free.erase(smallest);
+
+ // Shorter/Longer Axis Split Rule (SAS)
+ // http://clb.demon.fi/files/RectangleBinPack.pdf p. 15
+ // Ignore the dimension of R and just split long the shorter dimension
+ // See Also: http://www.cs.princeton.edu/~chazelle/pubs/blbinpacking.pdf
+ if (rect.w < rect.h) {
+ // split horizontally
+ // +--+---+
+ // |__|___| <-- b1
+ // +------+ <-- b2
+ if (rect.w > width) free.emplace_back(rect.x + width, rect.y, rect.w - width, height);
+ if (rect.h > height) free.emplace_back(rect.x, rect.y + height, rect.w, rect.h - height);
+ } else {
+ // split vertically
+ // +--+---+
+ // |__| | <-- b1
+ // +--|---+ <-- b2
+ if (rect.w > width) free.emplace_back(rect.x + width, rect.y, rect.w - width, rect.h);
+ if (rect.h > height) free.emplace_back(rect.x, rect.y + height, width, rect.h - height);
+ }
+
+ return Rect<uint16_t>{ rect.x, rect.y, width, height };
+ }
+ }
+
+
+ void release(Rect<T> rect) {
+ // Simple algorithm to recursively merge the newly released cell with its
+ // neighbor. This doesn't merge more than two cells at a time, and fails
+ // for complicated merges.
+ for (auto it = free.begin(); it != free.end(); ++it) {
+ Rect<T> ref = *it;
+ if (ref.y == rect.y && ref.h == rect.h && ref.x + ref.w == rect.x) {
+ ref.w += rect.w;
+ }
+ else if (ref.x == rect.x && ref.w == rect.w && ref.y + ref.h == rect.y) {
+ ref.h += rect.h;
+ }
+ else if (rect.y == ref.y && rect.h == ref.h && rect.x + rect.w == ref.x) {
+ ref.x = rect.x;
+ ref.w += rect.w;
+ }
+ else if (rect.x == ref.x && rect.w == ref.w && rect.y + rect.h == ref.y) {
+ ref.y = rect.y;
+ ref.h += rect.h;
+ } else {
+ continue;
+ }
+
+ free.erase(it);
+ release(ref);
+ return;
+
+ }
+
+ free.emplace_back(rect);
+ };
+
+private:
+ std::list<Rect<T>> free;
+};
+
+}
+
+#endif
diff --git a/src/mbgl/geometry/buffer.hpp b/src/mbgl/geometry/buffer.hpp
new file mode 100644
index 0000000000..80cc6b9d1a
--- /dev/null
+++ b/src/mbgl/geometry/buffer.hpp
@@ -0,0 +1,118 @@
+#ifndef MBGL_GEOMETRY_BUFFER
+#define MBGL_GEOMETRY_BUFFER
+
+#include <mbgl/platform/gl.hpp>
+#include <mbgl/util/noncopyable.hpp>
+
+#include <cstdlib>
+#include <cassert>
+#include <stdexcept>
+
+namespace mbgl {
+
+template <
+ size_t item_size,
+ int bufferType = GL_ARRAY_BUFFER,
+ size_t defaultLength = 8192,
+ bool retainAfterUpload = false
+>
+class Buffer : private util::noncopyable {
+public:
+ ~Buffer() {
+ cleanup();
+ if (buffer != 0) {
+ glDeleteBuffers(1, &buffer);
+ buffer = 0;
+ }
+ }
+
+ // Returns the number of elements in this buffer. This is not the number of
+ // bytes, but rather the number of coordinates with associated information.
+ inline size_t index() const {
+ return pos / itemSize;
+ }
+
+ inline bool empty() const {
+ return pos == 0;
+ }
+
+ // Transfers this buffer to the GPU and binds the buffer to the GL context.
+ void bind(bool force = false) {
+ if (buffer == 0) {
+ glGenBuffers(1, &buffer);
+ force = true;
+ }
+ glBindBuffer(bufferType, buffer);
+ if (force) {
+ if (array == nullptr) {
+ throw std::runtime_error("Buffer was already deleted or doesn't contain elements");
+ }
+
+ glBufferData(bufferType, pos, array, GL_STATIC_DRAW);
+ if (!retainAfterUpload) {
+ cleanup();
+ }
+ }
+ }
+
+ void cleanup() {
+ if (array) {
+ free(array);
+ array = nullptr;
+ }
+ }
+
+ inline GLuint getID() const {
+ return buffer;
+ }
+
+protected:
+ // increase the buffer size by at least /required/ bytes.
+ inline void *addElement() {
+ if (buffer != 0) {
+ throw std::runtime_error("Can't add elements after buffer was bound to GPU");
+ }
+ if (length < pos + itemSize) {
+ while (length < pos + itemSize) length += defaultLength;
+ array = realloc(array, length);
+ if (array == nullptr) {
+ throw std::runtime_error("Buffer reallocation failed");
+ }
+ }
+ pos += itemSize;
+ return static_cast<char *>(array) + (pos - itemSize);
+ }
+
+ // Get a pointer to the item at a given index.
+ inline void *getElement(size_t i) {
+ if (array == nullptr) {
+ throw std::runtime_error("Buffer was already deleted or doesn't contain elements");
+ }
+
+ if (i * itemSize >= pos) {
+ throw new std::runtime_error("Can't get element after array bounds");
+ } else {
+ return static_cast<char *>(array) + (i * itemSize);
+ }
+ }
+
+public:
+ static const size_t itemSize = item_size;
+
+private:
+ // CPU buffer
+ void *array = nullptr;
+
+ // Byte position where we are writing.
+ size_t pos = 0;
+
+ // Number of bytes that are valid in this buffer.
+ size_t length = 0;
+
+ // GL buffer ID
+ GLuint buffer = 0;
+};
+
+}
+
+#endif
diff --git a/src/mbgl/geometry/debug_font_buffer.cpp b/src/mbgl/geometry/debug_font_buffer.cpp
new file mode 100644
index 0000000000..1ec71463e5
--- /dev/null
+++ b/src/mbgl/geometry/debug_font_buffer.cpp
@@ -0,0 +1,42 @@
+#include <mbgl/geometry/debug_font_buffer.hpp>
+#include <mbgl/geometry/debug_font_data.hpp>
+
+#include <mbgl/platform/gl.hpp>
+#include <cmath>
+#include <cstring>
+
+using namespace mbgl;
+
+void DebugFontBuffer::addText(const char *text, double left, double baseline, double scale) {
+ uint16_t *coords = nullptr;
+
+ const size_t len = strlen(text);
+ for (size_t i = 0; i < len; ++i) {
+ if (text[i] < 32 || (unsigned char)(text[i]) >= 127) {
+ continue;
+ }
+
+ const glyph& glyph = simplex[text[i] - 32];
+
+ int16_t prev_x = -1, prev_y = -1, prev = false;
+ for (int32_t j = 0; j < glyph.length; j += 2) {
+ if (glyph.data[j] == -1 && glyph.data[j + 1] == -1) {
+ prev = false;
+ } else {
+ int16_t x = std::round(left + glyph.data[j] * scale);
+ int16_t y = std::round(baseline - glyph.data[j + 1] * scale);
+ if (prev) {
+ coords = static_cast<uint16_t *>(addElement());
+ coords[0] = prev_x;
+ coords[1] = prev_y;
+
+ coords = static_cast<uint16_t *>(addElement());
+ coords[0] = x;
+ coords[1] = y;
+ }
+ prev_x = x; prev_y = y; prev = true;
+ }
+ }
+ left += glyph.width * scale;
+ }
+}
diff --git a/src/mbgl/geometry/debug_font_buffer.hpp b/src/mbgl/geometry/debug_font_buffer.hpp
new file mode 100644
index 0000000000..802b5dbaac
--- /dev/null
+++ b/src/mbgl/geometry/debug_font_buffer.hpp
@@ -0,0 +1,17 @@
+#ifndef MBGL_GEOMETRY_DEBUG_FONT_BUFFER
+#define MBGL_GEOMETRY_DEBUG_FONT_BUFFER
+
+#include <mbgl/geometry/buffer.hpp>
+
+namespace mbgl {
+
+class DebugFontBuffer : public Buffer<
+ 4 // 2 bytes per coordinate, 2 coordinates
+> {
+public:
+ void addText(const char *text, double left, double baseline, double scale = 1);
+};
+
+}
+
+#endif
diff --git a/src/mbgl/geometry/debug_font_data.hpp b/src/mbgl/geometry/debug_font_data.hpp
new file mode 100644
index 0000000000..26c54cb480
--- /dev/null
+++ b/src/mbgl/geometry/debug_font_data.hpp
@@ -0,0 +1,206 @@
+// This is an implementation file, so omit include guards.
+
+#include <cstdint>
+#include <map>
+
+const int8_t simplex_1[] = { 5, 21, 5, 7, -1, -1, 5, 2, 4, 1, 5, 0, 6, 1, 5, 2 };
+const int8_t simplex_2[] = { 4, 21, 4, 14, -1, -1, 12, 21, 12, 14 };
+const int8_t simplex_3[] = { 11, 25, 4, -7, -1, -1, 17, 25, 10, -7, -1, -1, 4, 12, 18, 12, -1, -1, 3, 6, 17, 6 };
+const int8_t simplex_4[] = { 8, 25, 8, -4, -1, -1, 12, 25, 12, -4, -1, -1, 17, 18, 15, 20, 12, 21, 8, 21, 5, 20, 3, 18, 3, 16, 4, 14, 5, 13, 7, 12, 13, 10, 15, 9, 16, 8, 17, 6, 17, 3, 15, 1, 12, 0, 8, 0, 5, 1, 3, 3 };
+const int8_t simplex_5[] = { 21, 21, 3, 0, -1, -1, 8, 21, 10, 19, 10, 17, 9, 15, 7, 14, 5, 14, 3, 16, 3, 18, 4, 20, 6, 21, 8, 21, 10, 20, 13, 19, 16, 19, 19, 20, 21, 21, -1, -1, 17, 7, 15, 6, 14, 4, 14, 2, 16, 0, 18, 0, 20, 1, 21, 3, 21, 5, 19, 7, 17, 7 };
+const int8_t simplex_6[] = { 23, 12, 23, 13, 22, 14, 21, 14, 20, 13, 19, 11, 17, 6, 15, 3, 13, 1, 11, 0, 7, 0, 5, 1, 4, 2, 3, 4, 3, 6, 4, 8, 5, 9, 12, 13, 13, 14, 14, 16, 14, 18, 13, 20, 11, 21, 9, 20, 8, 18, 8, 16, 9, 13, 11, 10, 16, 3, 18, 1, 20, 0, 22, 0, 23, 1, 23, 2 };
+const int8_t simplex_7[] = { 5, 19, 4, 20, 5, 21, 6, 20, 6, 18, 5, 16, 4, 15 };
+const int8_t simplex_8[] = { 11, 25, 9, 23, 7, 20, 5, 16, 4, 11, 4, 7, 5, 2, 7, -2, 9, -5, 11, -7 };
+const int8_t simplex_9[] = { 3, 25, 5, 23, 7, 20, 9, 16, 10, 11, 10, 7, 9, 2, 7, -2, 5, -5, 3, -7 };
+const int8_t simplex_10[] = { 8, 21, 8, 9, -1, -1, 3, 18, 13, 12, -1, -1, 13, 18, 3, 12 };
+const int8_t simplex_11[] = { 13, 18, 13, 0, -1, -1, 4, 9, 22, 9 };
+const int8_t simplex_12[] = { 6, 1, 5, 0, 4, 1, 5, 2, 6, 1, 6, -1, 5, -3, 4, -4 };
+const int8_t simplex_13[] = { 4, 9, 22, 9 };
+const int8_t simplex_14[] = { 5, 2, 4, 1, 5, 0, 6, 1, 5, 2 };
+const int8_t simplex_15[] = { 20, 25, 2, -7 };
+const int8_t simplex_16[] = { 9, 21, 6, 20, 4, 17, 3, 12, 3, 9, 4, 4, 6, 1, 9, 0, 11, 0, 14, 1, 16, 4, 17, 9, 17, 12, 16, 17, 14, 20, 11, 21, 9, 21 };
+const int8_t simplex_17[] = { 6, 17, 8, 18, 11, 21, 11, 0 };
+const int8_t simplex_18[] = { 4, 16, 4, 17, 5, 19, 6, 20, 8, 21, 12, 21, 14, 20, 15, 19, 16, 17, 16, 15, 15, 13, 13, 10, 3, 0, 17, 0 };
+const int8_t simplex_19[] = { 5, 21, 16, 21, 10, 13, 13, 13, 15, 12, 16, 11, 17, 8, 17, 6, 16, 3, 14, 1, 11, 0, 8, 0, 5, 1, 4, 2, 3, 4 };
+const int8_t simplex_20[] = { 13, 21, 3, 7, 18, 7, -1, -1, 13, 21, 13, 0 };
+const int8_t simplex_21[] = { 15, 21, 5, 21, 4, 12, 5, 13, 8, 14, 11, 14, 14, 13, 16, 11, 17, 8, 17, 6, 16, 3, 14, 1, 11, 0, 8, 0, 5, 1, 4, 2, 3, 4 };
+const int8_t simplex_22[] = { 16, 18, 15, 20, 12, 21, 10, 21, 7, 20, 5, 17, 4, 12, 4, 7, 5, 3, 7, 1, 10, 0, 11, 0, 14, 1, 16, 3, 17, 6, 17, 7, 16, 10, 14, 12, 11, 13, 10, 13, 7, 12, 5, 10, 4, 7 };
+const int8_t simplex_23[] = { 17, 21, 7, 0, -1, -1, 3, 21, 17, 21 };
+const int8_t simplex_24[] = { 8, 21, 5, 20, 4, 18, 4, 16, 5, 14, 7, 13, 11, 12, 14, 11, 16, 9, 17, 7, 17, 4, 16, 2, 15, 1, 12, 0, 8, 0, 5, 1, 4, 2, 3, 4, 3, 7, 4, 9, 6, 11, 9, 12, 13, 13, 15, 14, 16, 16, 16, 18, 15, 20, 12, 21, 8, 21 };
+const int8_t simplex_25[] = { 16, 14, 15, 11, 13, 9, 10, 8, 9, 8, 6, 9, 4, 11, 3, 14, 3, 15, 4, 18, 6, 20, 9, 21, 10, 21, 13, 20, 15, 18, 16, 14, 16, 9, 15, 4, 13, 1, 10, 0, 8, 0, 5, 1, 4, 3 };
+const int8_t simplex_26[] = { 5, 14, 4, 13, 5, 12, 6, 13, 5, 14, -1, -1, 5, 2, 4, 1, 5, 0, 6, 1, 5, 2 };
+const int8_t simplex_27[] = { 5, 14, 4, 13, 5, 12, 6, 13, 5, 14, -1, -1, 6, 1, 5, 0, 4, 1, 5, 2, 6, 1, 6, -1, 5, -3, 4, -4 };
+const int8_t simplex_28[] = { 20, 18, 4, 9, 20, 0 };
+const int8_t simplex_29[] = { 4, 12, 22, 12, -1, -1, 4, 6, 22, 6 };
+const int8_t simplex_30[] = { 4, 18, 20, 9, 4, 0 };
+const int8_t simplex_31[] = { 3, 16, 3, 17, 4, 19, 5, 20, 7, 21, 11, 21, 13, 20, 14, 19, 15, 17, 15, 15, 14, 13, 13, 12, 9, 10, 9, 7, -1, -1, 9, 2, 8, 1, 9, 0, 10, 1, 9, 2 };
+const int8_t simplex_32[] = { 18, 13, 17, 15, 15, 16, 12, 16, 10, 15, 9, 14, 8, 11, 8, 8, 9, 6, 11, 5, 14, 5, 16, 6, 17, 8, -1, -1, 12, 16, 10, 14, 9, 11, 9, 8, 10, 6, 11, 5, -1, -1, 18, 16, 17, 8, 17, 6, 19, 5, 21, 5, 23, 7, 24, 10, 24, 12, 23, 15, 22, 17, 20, 19, 18, 20, 15, 21, 12, 21, 9, 20, 7, 19, 5, 17, 4, 15, 3, 12, 3, 9, 4, 6, 5, 4, 7, 2, 9, 1, 12, 0, 15, 0, 18, 1, 20, 2, 21, 3, -1, -1, 19, 16, 18, 8, 18, 6, 19, 5 };
+const int8_t simplex_33[] = { 9, 21, 1, 0, -1, -1, 9, 21, 17, 0, -1, -1, 4, 7, 14, 7 };
+const int8_t simplex_34[] = { 4, 21, 4, 0, -1, -1, 4, 21, 13, 21, 16, 20, 17, 19, 18, 17, 18, 15, 17, 13, 16, 12, 13, 11, -1, -1, 4, 11, 13, 11, 16, 10, 17, 9, 18, 7, 18, 4, 17, 2, 16, 1, 13, 0, 4, 0 };
+const int8_t simplex_35[] = { 18, 16, 17, 18, 15, 20, 13, 21, 9, 21, 7, 20, 5, 18, 4, 16, 3, 13, 3, 8, 4, 5, 5, 3, 7, 1, 9, 0, 13, 0, 15, 1, 17, 3, 18, 5 };
+const int8_t simplex_36[] = { 4, 21, 4, 0, -1, -1, 4, 21, 11, 21, 14, 20, 16, 18, 17, 16, 18, 13, 18, 8, 17, 5, 16, 3, 14, 1, 11, 0, 4, 0 };
+const int8_t simplex_37[] = { 4, 21, 4, 0, -1, -1, 4, 21, 17, 21, -1, -1, 4, 11, 12, 11, -1, -1, 4, 0, 17, 0 };
+const int8_t simplex_38[] = { 4, 21, 4, 0, -1, -1, 4, 21, 17, 21, -1, -1, 4, 11, 12, 11 };
+const int8_t simplex_39[] = { 18, 16, 17, 18, 15, 20, 13, 21, 9, 21, 7, 20, 5, 18, 4, 16, 3, 13, 3, 8, 4, 5, 5, 3, 7, 1, 9, 0, 13, 0, 15, 1, 17, 3, 18, 5, 18, 8, -1, -1, 13, 8, 18, 8 };
+const int8_t simplex_40[] = { 4, 21, 4, 0, -1, -1, 18, 21, 18, 0, -1, -1, 4, 11, 18, 11 };
+const int8_t simplex_41[] = { 4, 21, 4, 0 };
+const int8_t simplex_42[] = { 12, 21, 12, 5, 11, 2, 10, 1, 8, 0, 6, 0, 4, 1, 3, 2, 2, 5, 2, 7 };
+const int8_t simplex_43[] = { 4, 21, 4, 0, -1, -1, 18, 21, 4, 7, -1, -1, 9, 12, 18, 0 };
+const int8_t simplex_44[] = { 4, 21, 4, 0, -1, -1, 4, 0, 16, 0 };
+const int8_t simplex_45[] = { 4, 21, 4, 0, -1, -1, 4, 21, 12, 0, -1, -1, 20, 21, 12, 0, -1, -1, 20, 21, 20, 0 };
+const int8_t simplex_46[] = { 4, 21, 4, 0, -1, -1, 4, 21, 18, 0, -1, -1, 18, 21, 18, 0 };
+const int8_t simplex_47[] = { 9, 21, 7, 20, 5, 18, 4, 16, 3, 13, 3, 8, 4, 5, 5, 3, 7, 1, 9, 0, 13, 0, 15, 1, 17, 3, 18, 5, 19, 8, 19, 13, 18, 16, 17, 18, 15, 20, 13, 21, 9, 21 };
+const int8_t simplex_48[] = { 4, 21, 4, 0, -1, -1, 4, 21, 13, 21, 16, 20, 17, 19, 18, 17, 18, 14, 17, 12, 16, 11, 13, 10, 4, 10 };
+const int8_t simplex_49[] = { 9, 21, 7, 20, 5, 18, 4, 16, 3, 13, 3, 8, 4, 5, 5, 3, 7, 1, 9, 0, 13, 0, 15, 1, 17, 3, 18, 5, 19, 8, 19, 13, 18, 16, 17, 18, 15, 20, 13, 21, 9, 21, -1, -1, 12, 4, 18, -2 };
+const int8_t simplex_50[] = { 4, 21, 4, 0, -1, -1, 4, 21, 13, 21, 16, 20, 17, 19, 18, 17, 18, 15, 17, 13, 16, 12, 13, 11, 4, 11, -1, -1, 11, 11, 18, 0 };
+const int8_t simplex_51[] = { 17, 18, 15, 20, 12, 21, 8, 21, 5, 20, 3, 18, 3, 16, 4, 14, 5, 13, 7, 12, 13, 10, 15, 9, 16, 8, 17, 6, 17, 3, 15, 1, 12, 0, 8, 0, 5, 1, 3, 3 };
+const int8_t simplex_52[] = { 8, 21, 8, 0, -1, -1, 1, 21, 15, 21 };
+const int8_t simplex_53[] = { 4, 21, 4, 6, 5, 3, 7, 1, 10, 0, 12, 0, 15, 1, 17, 3, 18, 6, 18, 21 };
+const int8_t simplex_54[] = { 1, 21, 9, 0, -1, -1, 17, 21, 9, 0 };
+const int8_t simplex_55[] = { 2, 21, 7, 0, -1, -1, 12, 21, 7, 0, -1, -1, 12, 21, 17, 0, -1, -1, 22, 21, 17, 0 };
+const int8_t simplex_56[] = { 3, 21, 17, 0, -1, -1, 17, 21, 3, 0 };
+const int8_t simplex_57[] = { 1, 21, 9, 11, 9, 0, -1, -1, 17, 21, 9, 11 };
+const int8_t simplex_58[] = { 17, 21, 3, 0, -1, -1, 3, 21, 17, 21, -1, -1, 3, 0, 17, 0 };
+const int8_t simplex_59[] = { 4, 25, 4, -7, -1, -1, 5, 25, 5, -7, -1, -1, 4, 25, 11, 25, -1, -1, 4, -7, 11, -7 };
+const int8_t simplex_60[] = { 0, 21, 14, -3 };
+const int8_t simplex_61[] = { 9, 25, 9, -7, -1, -1, 10, 25, 10, -7, -1, -1, 3, 25, 10, 25, -1, -1, 3, -7, 10, -7 };
+const int8_t simplex_62[] = { 6, 15, 8, 18, 10, 15, -1, -1, 3, 12, 8, 17, 13, 12, -1, -1, 8, 17, 8, 0 };
+const int8_t simplex_63[] = { 0, -2, 16, -2 };
+const int8_t simplex_64[] = { 6, 21, 5, 20, 4, 18, 4, 16, 5, 15, 6, 16, 5, 17 };
+const int8_t simplex_65[] = { 15, 14, 15, 0, -1, -1, 15, 11, 13, 13, 11, 14, 8, 14, 6, 13, 4, 11, 3, 8, 3, 6, 4, 3, 6, 1, 8, 0, 11, 0, 13, 1, 15, 3 };
+const int8_t simplex_66[] = { 4, 21, 4, 0, -1, -1, 4, 11, 6, 13, 8, 14, 11, 14, 13, 13, 15, 11, 16, 8, 16, 6, 15, 3, 13, 1, 11, 0, 8, 0, 6, 1, 4, 3 };
+const int8_t simplex_67[] = { 15, 11, 13, 13, 11, 14, 8, 14, 6, 13, 4, 11, 3, 8, 3, 6, 4, 3, 6, 1, 8, 0, 11, 0, 13, 1, 15, 3 };
+const int8_t simplex_68[] = { 15, 21, 15, 0, -1, -1, 15, 11, 13, 13, 11, 14, 8, 14, 6, 13, 4, 11, 3, 8, 3, 6, 4, 3, 6, 1, 8, 0, 11, 0, 13, 1, 15, 3 };
+const int8_t simplex_69[] = { 3, 8, 15, 8, 15, 10, 14, 12, 13, 13, 11, 14, 8, 14, 6, 13, 4, 11, 3, 8, 3, 6, 4, 3, 6, 1, 8, 0, 11, 0, 13, 1, 15, 3 };
+const int8_t simplex_70[] = { 10, 21, 8, 21, 6, 20, 5, 17, 5, 0, -1, -1, 2, 14, 9, 14 };
+const int8_t simplex_71[] = { 15, 14, 15, -2, 14, -5, 13, -6, 11, -7, 8, -7, 6, -6, -1, -1, 15, 11, 13, 13, 11, 14, 8, 14, 6, 13, 4, 11, 3, 8, 3, 6, 4, 3, 6, 1, 8, 0, 11, 0, 13, 1, 15, 3 };
+const int8_t simplex_72[] = { 4, 21, 4, 0, -1, -1, 4, 10, 7, 13, 9, 14, 12, 14, 14, 13, 15, 10, 15, 0 };
+const int8_t simplex_73[] = { 3, 21, 4, 20, 5, 21, 4, 22, 3, 21, -1, -1, 4, 14, 4, 0 };
+const int8_t simplex_74[] = { 5, 21, 6, 20, 7, 21, 6, 22, 5, 21, -1, -1, 6, 14, 6, -3, 5, -6, 3, -7, 1, -7 };
+const int8_t simplex_75[] = { 4, 21, 4, 0, -1, -1, 14, 14, 4, 4, -1, -1, 8, 8, 15, 0 };
+const int8_t simplex_76[] = { 4, 21, 4, 0 };
+const int8_t simplex_77[] = { 4, 14, 4, 0, -1, -1, 4, 10, 7, 13, 9, 14, 12, 14, 14, 13, 15, 10, 15, 0, -1, -1, 15, 10, 18, 13, 20, 14, 23, 14, 25, 13, 26, 10, 26, 0 };
+const int8_t simplex_78[] = { 4, 14, 4, 0, -1, -1, 4, 10, 7, 13, 9, 14, 12, 14, 14, 13, 15, 10, 15, 0 };
+const int8_t simplex_79[] = { 8, 14, 6, 13, 4, 11, 3, 8, 3, 6, 4, 3, 6, 1, 8, 0, 11, 0, 13, 1, 15, 3, 16, 6, 16, 8, 15, 11, 13, 13, 11, 14, 8, 14 };
+const int8_t simplex_80[] = { 4, 14, 4, -7, -1, -1, 4, 11, 6, 13, 8, 14, 11, 14, 13, 13, 15, 11, 16, 8, 16, 6, 15, 3, 13, 1, 11, 0, 8, 0, 6, 1, 4, 3 };
+const int8_t simplex_81[] = { 15, 14, 15, -7, -1, -1, 15, 11, 13, 13, 11, 14, 8, 14, 6, 13, 4, 11, 3, 8, 3, 6, 4, 3, 6, 1, 8, 0, 11, 0, 13, 1, 15, 3 };
+const int8_t simplex_82[] = { 4, 14, 4, 0, -1, -1, 4, 8, 5, 11, 7, 13, 9, 14, 12, 14 };
+const int8_t simplex_83[] = { 14, 11, 13, 13, 10, 14, 7, 14, 4, 13, 3, 11, 4, 9, 6, 8, 11, 7, 13, 6, 14, 4, 14, 3, 13, 1, 10, 0, 7, 0, 4, 1, 3, 3 };
+const int8_t simplex_84[] = { 5, 21, 5, 4, 6, 1, 8, 0, 10, 0, -1, -1, 2, 14, 9, 14 };
+const int8_t simplex_85[] = { 4, 14, 4, 4, 5, 1, 7, 0, 10, 0, 12, 1, 15, 4, -1, -1, 15, 14, 15, 0 };
+const int8_t simplex_86[] = { 2, 14, 8, 0, -1, -1, 14, 14, 8, 0 };
+const int8_t simplex_87[] = { 3, 14, 7, 0, -1, -1, 11, 14, 7, 0, -1, -1, 11, 14, 15, 0, -1, -1, 19, 14, 15, 0 };
+const int8_t simplex_88[] = { 3, 14, 14, 0, -1, -1, 14, 14, 3, 0 };
+const int8_t simplex_89[] = { 2, 14, 8, 0, -1, -1, 14, 14, 8, 0, 6, -4, 4, -6, 2, -7, 1, -7 };
+const int8_t simplex_90[] = { 14, 14, 3, 0, -1, -1, 3, 14, 14, 14, -1, -1, 3, 0, 14, 0 };
+const int8_t simplex_91[] = { 9, 25, 7, 24, 6, 23, 5, 21, 5, 19, 6, 17, 7, 16, 8, 14, 8, 12, 6, 10, -1, -1, 7, 24, 6, 22, 6, 20, 7, 18, 8, 17, 9, 15, 9, 13, 8, 11, 4, 9, 8, 7, 9, 5, 9, 3, 8, 1, 7, 0, 6, -2, 6, -4, 7, -6, -1, -1, 6, 8, 8, 6, 8, 4, 7, 2, 6, 1, 5, -1, 5, -3, 6, -5, 7, -6, 9, -7 };
+const int8_t simplex_92[] = { 4, 25, 4, -7 };
+const int8_t simplex_93[] = { 5, 25, 7, 24, 8, 23, 9, 21, 9, 19, 8, 17, 7, 16, 6, 14, 6, 12, 8, 10, -1, -1, 7, 24, 8, 22, 8, 20, 7, 18, 6, 17, 5, 15, 5, 13, 6, 11, 10, 9, 6, 7, 5, 5, 5, 3, 6, 1, 7, 0, 8, -2, 8, -4, 7, -6, -1, -1, 8, 8, 6, 6, 6, 4, 7, 2, 8, 1, 9, -1, 9, -3, 8, -5, 7, -6, 5, -7 };
+const int8_t simplex_94[] = { 3, 6, 3, 8, 4, 11, 6, 12, 8, 12, 10, 11, 14, 8, 16, 7, 18, 7, 20, 8, 21, 10, -1, -1, 3, 8, 4, 10, 6, 11, 8, 11, 10, 10, 14, 7, 16, 6, 18, 6, 20, 7, 21, 10, 21, 12 };
+
+struct glyph {
+ uint8_t width;
+ uint8_t length;
+ const int8_t *data;
+};
+
+// Font data From Hershey Simplex Font
+// http://paulbourke.net/dataformats/hershey/
+
+const glyph simplex[] = {
+ /* 32 */ { 16, 0, nullptr },
+ /* 33 ! */ { 10, sizeof(simplex_1), simplex_1 },
+ /* 34 " */ { 16, sizeof(simplex_2), simplex_2 },
+ /* 35 # */ { 21, sizeof(simplex_3), simplex_3 },
+ /* 36 $ */ { 20, sizeof(simplex_4), simplex_4 },
+ /* 37 % */ { 24, sizeof(simplex_5), simplex_5 },
+ /* 38 & */ { 26, sizeof(simplex_6), simplex_6 },
+ /* 39 ' */ { 10, sizeof(simplex_7), simplex_7 },
+ /* 40 ( */ { 14, sizeof(simplex_8), simplex_8 },
+ /* 41 ) */ { 14, sizeof(simplex_9), simplex_9 },
+ /* 42 * */ { 16, sizeof(simplex_10), simplex_10 },
+ /* 43 + */ { 26, sizeof(simplex_11), simplex_11 },
+ /* 44 , */ { 10, sizeof(simplex_12), simplex_12 },
+ /* 45 - */ { 26, sizeof(simplex_13), simplex_13 },
+ /* 46 . */ { 10, sizeof(simplex_14), simplex_14 },
+ /* 47 / */ { 22, sizeof(simplex_15), simplex_15 },
+ /* 48 0 */ { 20, sizeof(simplex_16), simplex_16 },
+ /* 49 1 */ { 20, sizeof(simplex_17), simplex_17 },
+ /* 50 2 */ { 20, sizeof(simplex_18), simplex_18 },
+ /* 51 3 */ { 20, sizeof(simplex_19), simplex_19 },
+ /* 52 4 */ { 20, sizeof(simplex_20), simplex_20 },
+ /* 53 5 */ { 20, sizeof(simplex_21), simplex_21 },
+ /* 54 6 */ { 20, sizeof(simplex_22), simplex_22 },
+ /* 55 7 */ { 20, sizeof(simplex_23), simplex_23 },
+ /* 56 8 */ { 20, sizeof(simplex_24), simplex_24 },
+ /* 57 9 */ { 20, sizeof(simplex_25), simplex_25 },
+ /* 58 : */ { 10, sizeof(simplex_26), simplex_26 },
+ /* 59 ; */ { 10, sizeof(simplex_27), simplex_27 },
+ /* 60 < */ { 24, sizeof(simplex_28), simplex_28 },
+ /* 61 = */ { 26, sizeof(simplex_29), simplex_29 },
+ /* 62 > */ { 24, sizeof(simplex_30), simplex_30 },
+ /* 63 ? */ { 18, sizeof(simplex_31), simplex_31 },
+ /* 64 @ */ { 27, sizeof(simplex_32), simplex_32 },
+ /* 65 A */ { 18, sizeof(simplex_33), simplex_33 },
+ /* 66 B */ { 21, sizeof(simplex_34), simplex_34 },
+ /* 67 C */ { 21, sizeof(simplex_35), simplex_35 },
+ /* 68 D */ { 21, sizeof(simplex_36), simplex_36 },
+ /* 69 E */ { 19, sizeof(simplex_37), simplex_37 },
+ /* 70 F */ { 18, sizeof(simplex_38), simplex_38 },
+ /* 71 G */ { 21, sizeof(simplex_39), simplex_39 },
+ /* 72 H */ { 22, sizeof(simplex_40), simplex_40 },
+ /* 73 I */ { 8, sizeof(simplex_41), simplex_41 },
+ /* 74 J */ { 16, sizeof(simplex_42), simplex_42 },
+ /* 75 K */ { 21, sizeof(simplex_43), simplex_43 },
+ /* 76 L */ { 17, sizeof(simplex_44), simplex_44 },
+ /* 77 M */ { 24, sizeof(simplex_45), simplex_45 },
+ /* 78 N */ { 22, sizeof(simplex_46), simplex_46 },
+ /* 79 O */ { 22, sizeof(simplex_47), simplex_47 },
+ /* 80 P */ { 21, sizeof(simplex_48), simplex_48 },
+ /* 81 Q */ { 22, sizeof(simplex_49), simplex_49 },
+ /* 82 R */ { 21, sizeof(simplex_50), simplex_50 },
+ /* 83 S */ { 20, sizeof(simplex_51), simplex_51 },
+ /* 84 T */ { 16, sizeof(simplex_52), simplex_52 },
+ /* 85 U */ { 22, sizeof(simplex_53), simplex_53 },
+ /* 86 V */ { 18, sizeof(simplex_54), simplex_54 },
+ /* 87 W */ { 24, sizeof(simplex_55), simplex_55 },
+ /* 88 X */ { 20, sizeof(simplex_56), simplex_56 },
+ /* 89 Y */ { 18, sizeof(simplex_57), simplex_57 },
+ /* 90 Z */ { 20, sizeof(simplex_58), simplex_58 },
+ /* 91 [ */ { 14, sizeof(simplex_59), simplex_59 },
+ /* 92 \ */ { 14, sizeof(simplex_60), simplex_60 },
+ /* 93 ] */ { 14, sizeof(simplex_61), simplex_61 },
+ /* 94 ^ */ { 16, sizeof(simplex_62), simplex_62 },
+ /* 95 _ */ { 16, sizeof(simplex_63), simplex_63 },
+ /* 96 ` */ { 10, sizeof(simplex_64), simplex_64 },
+ /* 97 a */ { 19, sizeof(simplex_65), simplex_65 },
+ /* 98 b */ { 19, sizeof(simplex_66), simplex_66 },
+ /* 99 c */ { 18, sizeof(simplex_67), simplex_67 },
+ /* 100 d */ { 19, sizeof(simplex_68), simplex_68 },
+ /* 101 e */ { 18, sizeof(simplex_69), simplex_69 },
+ /* 102 f */ { 12, sizeof(simplex_70), simplex_70 },
+ /* 103 g */ { 19, sizeof(simplex_71), simplex_71 },
+ /* 104 h */ { 19, sizeof(simplex_72), simplex_72 },
+ /* 105 i */ { 8, sizeof(simplex_73), simplex_73 },
+ /* 106 j */ { 10, sizeof(simplex_74), simplex_74 },
+ /* 107 k */ { 17, sizeof(simplex_75), simplex_75 },
+ /* 108 l */ { 8, sizeof(simplex_76), simplex_76 },
+ /* 109 m */ { 30, sizeof(simplex_77), simplex_77 },
+ /* 110 n */ { 19, sizeof(simplex_78), simplex_78 },
+ /* 111 o */ { 19, sizeof(simplex_79), simplex_79 },
+ /* 112 p */ { 19, sizeof(simplex_80), simplex_80 },
+ /* 113 q */ { 19, sizeof(simplex_81), simplex_81 },
+ /* 114 r */ { 13, sizeof(simplex_82), simplex_82 },
+ /* 115 s */ { 17, sizeof(simplex_83), simplex_83 },
+ /* 116 t */ { 12, sizeof(simplex_84), simplex_84 },
+ /* 117 u */ { 19, sizeof(simplex_85), simplex_85 },
+ /* 118 v */ { 16, sizeof(simplex_86), simplex_86 },
+ /* 119 w */ { 22, sizeof(simplex_87), simplex_87 },
+ /* 120 x */ { 17, sizeof(simplex_88), simplex_88 },
+ /* 121 y */ { 16, sizeof(simplex_89), simplex_89 },
+ /* 122 z */ { 17, sizeof(simplex_90), simplex_90 },
+ /* 123 { */ { 14, sizeof(simplex_91), simplex_91 },
+ /* 124 | */ { 8, sizeof(simplex_92), simplex_92 },
+ /* 125 } */ { 14, sizeof(simplex_93), simplex_93 },
+ /* 126 ~ */ { 24, sizeof(simplex_94), simplex_94 },
+};
diff --git a/src/mbgl/geometry/elements_buffer.cpp b/src/mbgl/geometry/elements_buffer.cpp
new file mode 100644
index 0000000000..79af1b7e35
--- /dev/null
+++ b/src/mbgl/geometry/elements_buffer.cpp
@@ -0,0 +1,21 @@
+#include <mbgl/geometry/elements_buffer.hpp>
+
+using namespace mbgl;
+
+void TriangleElementsBuffer::add(element_type a, element_type b, element_type c) {
+ element_type *elements = static_cast<element_type *>(addElement());
+ elements[0] = a;
+ elements[1] = b;
+ elements[2] = c;
+}
+
+void LineElementsBuffer::add(element_type a, element_type b) {
+ element_type *elements = static_cast<element_type *>(addElement());
+ elements[0] = a;
+ elements[1] = b;
+}
+
+void PointElementsBuffer::add(element_type a) {
+ uint16_t *data = static_cast<element_type *>(addElement());
+ data[0] = a;
+}
diff --git a/src/mbgl/geometry/elements_buffer.hpp b/src/mbgl/geometry/elements_buffer.hpp
new file mode 100644
index 0000000000..9255337cb5
--- /dev/null
+++ b/src/mbgl/geometry/elements_buffer.hpp
@@ -0,0 +1,64 @@
+#ifndef MBGL_GEOMETRY_TRIANGLE_ELEMENTS_BUFFER
+#define MBGL_GEOMETRY_TRIANGLE_ELEMENTS_BUFFER
+
+#include <mbgl/geometry/buffer.hpp>
+#include <mbgl/geometry/vao.hpp>
+
+#include <mbgl/util/noncopyable.hpp>
+
+#include <array>
+
+namespace mbgl {
+
+template <int count>
+struct ElementGroup : public util::noncopyable {
+ std::array<VertexArrayObject, count> array;
+ uint32_t vertex_length;
+ uint32_t elements_length;
+
+ ElementGroup() : vertex_length(0), elements_length(0) {}
+ ElementGroup(uint32_t vertex_length_, uint32_t elements_length_)
+ : vertex_length(vertex_length_),
+ elements_length(elements_length_) {
+ }
+
+ ElementGroup(ElementGroup &&rhs) noexcept
+ : array(std::move(rhs.array)),
+ vertex_length(rhs.vertex_length),
+ elements_length(rhs.elements_length) {};
+};
+
+class TriangleElementsBuffer : public Buffer<
+ 6, // bytes per triangle (3 * unsigned short == 6 bytes)
+ GL_ELEMENT_ARRAY_BUFFER
+> {
+public:
+ typedef uint16_t element_type;
+
+ void add(element_type a, element_type b, element_type c);
+};
+
+
+class LineElementsBuffer : public Buffer<
+ 4, // bytes per triangle (2 * unsigned short == 6 bytes)
+ GL_ELEMENT_ARRAY_BUFFER
+> {
+public:
+ typedef uint16_t element_type;
+
+ void add(element_type a, element_type b);
+};
+
+class PointElementsBuffer : public Buffer<
+ 2, // bytes per point (1 unsigned short)
+ GL_ELEMENT_ARRAY_BUFFER
+> {
+public:
+ typedef uint16_t element_type;
+
+ void add(element_type a);
+};
+
+}
+
+#endif
diff --git a/src/mbgl/geometry/fill_buffer.cpp b/src/mbgl/geometry/fill_buffer.cpp
new file mode 100644
index 0000000000..3392699431
--- /dev/null
+++ b/src/mbgl/geometry/fill_buffer.cpp
@@ -0,0 +1,13 @@
+#include <mbgl/geometry/fill_buffer.hpp>
+
+#include <mbgl/platform/gl.hpp>
+
+#include <climits>
+
+using namespace mbgl;
+
+void FillVertexBuffer::add(vertex_type x, vertex_type y) {
+ vertex_type *vertices = static_cast<vertex_type *>(addElement());
+ vertices[0] = x;
+ vertices[1] = y;
+}
diff --git a/src/mbgl/geometry/fill_buffer.hpp b/src/mbgl/geometry/fill_buffer.hpp
new file mode 100644
index 0000000000..2cd1637fa1
--- /dev/null
+++ b/src/mbgl/geometry/fill_buffer.hpp
@@ -0,0 +1,21 @@
+#ifndef MBGL_GEOMETRY_FILL_BUFFER
+#define MBGL_GEOMETRY_FILL_BUFFER
+
+#include <mbgl/geometry/buffer.hpp>
+#include <vector>
+#include <cstdint>
+
+namespace mbgl {
+
+class FillVertexBuffer : public Buffer<
+ 4 // bytes per coordinates (2 * unsigned short == 4 bytes)
+> {
+public:
+ typedef int16_t vertex_type;
+
+ void add(vertex_type x, vertex_type y);
+};
+
+}
+
+#endif
diff --git a/src/mbgl/geometry/geometry.hpp b/src/mbgl/geometry/geometry.hpp
new file mode 100644
index 0000000000..484d17b36d
--- /dev/null
+++ b/src/mbgl/geometry/geometry.hpp
@@ -0,0 +1,77 @@
+#ifndef MBGL_GEOMETRY_GEOMETRY
+#define MBGL_GEOMETRY_GEOMETRY
+
+#include <mbgl/util/pbf.hpp>
+#include <mbgl/util/noncopyable.hpp>
+
+#include <cstdlib>
+
+namespace mbgl {
+
+class Geometry : private util::noncopyable {
+
+public:
+ inline explicit Geometry(pbf& data);
+
+ enum command : uint8_t {
+ end = 0,
+ move_to = 1,
+ line_to = 2,
+ close = 7
+ };
+
+ inline command next(int32_t &rx, int32_t &ry);
+
+private:
+ pbf& data;
+ uint8_t cmd;
+ uint32_t length;
+ int32_t x, y;
+ int32_t ox, oy;
+};
+
+Geometry::Geometry(pbf& data_)
+ : data(data_),
+ cmd(1),
+ length(0),
+ x(0), y(0),
+ ox(0), oy(0) {}
+
+Geometry::command Geometry::next(int32_t &rx, int32_t &ry) {
+ if (data.data < data.end) {
+ if (length == 0) {
+ uint32_t cmd_length = static_cast<uint32_t>(data.varint());
+ cmd = cmd_length & 0x7;
+ length = cmd_length >> 3;
+ }
+
+ --length;
+
+ if (cmd == move_to || cmd == line_to) {
+ rx = (x += data.svarint());
+ ry = (y += data.svarint());
+
+ if (cmd == move_to) {
+ ox = x;
+ oy = y;
+ return move_to;
+ } else {
+ return line_to;
+ }
+ } else if (cmd == close) {
+ rx = ox;
+ ry = oy;
+ return close;
+ } else {
+ fprintf(stderr, "unknown command: %d\n", cmd);
+ // TODO: gracefully handle geometry parse failures
+ return end;
+ }
+ } else {
+ return end;
+ }
+}
+
+}
+
+#endif
diff --git a/src/mbgl/geometry/glyph_atlas.cpp b/src/mbgl/geometry/glyph_atlas.cpp
new file mode 100644
index 0000000000..bafcf2b000
--- /dev/null
+++ b/src/mbgl/geometry/glyph_atlas.cpp
@@ -0,0 +1,167 @@
+#include <mbgl/geometry/glyph_atlas.hpp>
+#include <mbgl/map/vector_tile.hpp>
+
+#include <mbgl/platform/gl.hpp>
+#include <mbgl/platform/platform.hpp>
+
+#include <cassert>
+#include <algorithm>
+
+
+using namespace mbgl;
+
+GlyphAtlas::GlyphAtlas(uint16_t width_, uint16_t height_)
+ : width(width_),
+ height(height_),
+ bin(width_, height_),
+ data(new char[width_ *height_]),
+ dirty(true) {
+}
+
+Rect<uint16_t> GlyphAtlas::addGlyph(uint64_t tile_id, const std::string& face_name,
+ const SDFGlyph& glyph)
+{
+ std::lock_guard<std::mutex> lock(mtx);
+ return addGlyph_impl(tile_id, face_name, glyph);
+}
+
+Rect<uint16_t> GlyphAtlas::addGlyph_impl(uint64_t tile_id, const std::string& face_name,
+ const SDFGlyph& glyph)
+{
+ // Use constant value for now.
+ const uint8_t buffer = 3;
+
+ std::map<uint32_t, GlyphValue>& face = index[face_name];
+ std::map<uint32_t, GlyphValue>::iterator it = face.find(glyph.id);
+
+ // The glyph is already in this texture.
+ if (it != face.end()) {
+ GlyphValue& value = it->second;
+ value.ids.insert(tile_id);
+ return value.rect;
+ }
+
+ // The glyph bitmap has zero width.
+ if (!glyph.bitmap.size()) {
+ return Rect<uint16_t>{ 0, 0, 0, 0 };
+ }
+
+ uint16_t buffered_width = glyph.metrics.width + buffer * 2;
+ uint16_t buffered_height = glyph.metrics.height + buffer * 2;
+
+ // Add a 1px border around every image.
+ uint16_t pack_width = buffered_width;
+ uint16_t pack_height = buffered_height;
+
+ // Increase to next number divisible by 4, but at least 1.
+ // This is so we can scale down the texture coordinates and pack them
+ // into 2 bytes rather than 4 bytes.
+ pack_width += (4 - pack_width % 4);
+ pack_height += (4 - pack_height % 4);
+
+ Rect<uint16_t> rect = bin.allocate(pack_width, pack_height);
+ if (rect.w == 0) {
+ fprintf(stderr, "glyph bitmap overflow");
+ return rect;
+ }
+
+ assert(rect.x + rect.w <= width);
+ assert(rect.y + rect.h <= height);
+
+ face.emplace(glyph.id, GlyphValue { rect, tile_id });
+
+ // Copy the bitmap
+ char *target = data.get();
+ const char *source = glyph.bitmap.data();
+ for (uint32_t y = 0; y < buffered_height; y++) {
+ uint32_t y1 = width * (rect.y + y) + rect.x;
+ uint32_t y2 = buffered_width * y;
+ for (uint32_t x = 0; x < buffered_width; x++) {
+ target[y1 + x] = source[y2 + x];
+ }
+ }
+
+ dirty = true;
+
+ return rect;
+}
+
+void GlyphAtlas::addGlyphs(uint64_t tileid, std::u32string const& text, std::string const& stackname, FontStack const& fontStack, GlyphPositions & face)
+{
+ std::lock_guard<std::mutex> lock(mtx);
+
+ std::map<uint32_t, SDFGlyph> const& sdfs = fontStack.getSDFs();
+ for (uint32_t chr : text)
+ {
+ auto sdf_it = sdfs.find(chr);
+ if (sdf_it != sdfs.end())
+ {
+ SDFGlyph const& sdf = sdf_it->second;
+ Rect<uint16_t> rect = addGlyph_impl(tileid, stackname, sdf);
+ face.emplace(chr, Glyph{rect, sdf.metrics});
+ }
+ }
+}
+
+void GlyphAtlas::removeGlyphs(uint64_t tile_id) {
+ std::lock_guard<std::mutex> lock(mtx);
+
+ for (auto& faces : index) {
+ std::map<uint32_t, GlyphValue>& face = faces.second;
+ for (auto it = face.begin(); it != face.end(); /* we advance in the body */) {
+ GlyphValue& value = it->second;
+ value.ids.erase(tile_id);
+
+ if (!value.ids.size()) {
+ const Rect<uint16_t>& rect = value.rect;
+
+ // Clear out the bitmap.
+ char *target = data.get();
+ for (uint32_t y = 0; y < rect.h; y++) {
+ uint32_t y1 = width * (rect.y + y) + rect.x;
+ for (uint32_t x = 0; x < rect.w; x++) {
+ target[y1 + x] = 0;
+ }
+ }
+
+ dirty = true;
+
+ bin.release(rect);
+
+ // Make sure to post-increment the iterator: This will return the
+ // current iterator, but will go to the next position before we
+ // erase the element from the map. That way, the iterator stays
+ // valid.
+ face.erase(it++);
+ } else {
+ ++it;
+ }
+ }
+ }
+}
+
+void GlyphAtlas::bind() {
+ if (!texture) {
+ glGenTextures(1, &texture);
+ glBindTexture(GL_TEXTURE_2D, texture);
+#ifndef GL_ES_VERSION_2_0
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
+#endif
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ 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);
+ }
+
+ if (dirty) {
+ std::lock_guard<std::mutex> lock(mtx);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, width, height, 0, GL_ALPHA, GL_UNSIGNED_BYTE, data.get());
+ dirty = false;
+
+#if defined(DEBUG)
+ // platform::show_debug_image("Glyph Atlas", data, width, height);
+#endif
+ }
+};
diff --git a/src/mbgl/geometry/glyph_atlas.hpp b/src/mbgl/geometry/glyph_atlas.hpp
new file mode 100644
index 0000000000..7b3c223fe5
--- /dev/null
+++ b/src/mbgl/geometry/glyph_atlas.hpp
@@ -0,0 +1,54 @@
+#ifndef MBGL_GEOMETRY_GLYPH_ATLAS
+#define MBGL_GEOMETRY_GLYPH_ATLAS
+
+#include <mbgl/geometry/binpack.hpp>
+#include <mbgl/text/glyph_store.hpp>
+#include <mbgl/util/noncopyable.hpp>
+
+#include <string>
+#include <set>
+#include <map>
+#include <mutex>
+#include <atomic>
+
+namespace mbgl {
+
+class GlyphAtlas : public util::noncopyable {
+public:
+
+private:
+ struct GlyphValue {
+ GlyphValue(const Rect<uint16_t>& rect_, uint64_t id)
+ : rect(rect_), ids({ id }) {}
+ Rect<uint16_t> rect;
+ std::set<uint64_t> ids;
+ };
+
+ Rect<uint16_t> addGlyph_impl(uint64_t tile_id, const std::string& face_name,
+ const SDFGlyph& glyph);
+public:
+ GlyphAtlas(uint16_t width, uint16_t height);
+
+ Rect<uint16_t> addGlyph(uint64_t tile_id, const std::string& face_name,
+ const SDFGlyph& glyph);
+ void addGlyphs(uint64_t tileid, std::u32string const& text, std::string const& stackname,
+ FontStack const& fontStack, GlyphPositions & face);
+ void removeGlyphs(uint64_t tile_id);
+ void bind();
+
+public:
+ const uint16_t width = 0;
+ const uint16_t height = 0;
+
+private:
+ std::mutex mtx;
+ BinPack<uint16_t> bin;
+ std::map<std::string, std::map<uint32_t, GlyphValue>> index;
+ std::unique_ptr<char[]> data;
+ std::atomic<bool> dirty;
+ uint32_t texture = 0;
+};
+
+};
+
+#endif
diff --git a/src/mbgl/geometry/icon_buffer.cpp b/src/mbgl/geometry/icon_buffer.cpp
new file mode 100644
index 0000000000..c571dfa69e
--- /dev/null
+++ b/src/mbgl/geometry/icon_buffer.cpp
@@ -0,0 +1,35 @@
+#include <mbgl/geometry/icon_buffer.hpp>
+#include <mbgl/platform/gl.hpp>
+#include <mbgl/util/math.hpp>
+
+#include <cmath>
+
+namespace mbgl {
+
+const double IconVertexBuffer::angleFactor = 128.0 / M_PI;
+
+size_t IconVertexBuffer::add(int16_t x, int16_t y, float ox, float oy, int16_t tx, int16_t ty, float angle, float minzoom, std::array<float, 2> range, float maxzoom, float labelminzoom) {
+ const size_t idx = index();
+ void *data = addElement();
+
+ int16_t *shorts = static_cast<int16_t *>(data);
+ shorts[0] /* pos */ = x;
+ shorts[1] /* pos */ = y;
+ shorts[2] /* offset */ = std::round(ox * 64); // use 1/64 pixels for placement
+ shorts[3] /* offset */ = std::round(oy * 64);
+
+ uint8_t *ubytes = static_cast<uint8_t *>(data);
+ ubytes[8] /* labelminzoom */ = labelminzoom * 10;
+ ubytes[9] /* minzoom */ = minzoom * 10; // 1/10 zoom levels: z16 == 160.
+ ubytes[10] /* maxzoom */ = std::fmin(maxzoom, 25) * 10; // 1/10 zoom levels: z16 == 160.
+ ubytes[11] /* angle */ = (int16_t)std::round(angle * angleFactor) % 256;
+ ubytes[12] /* rangeend */ = util::max((int16_t)std::round(range[0] * angleFactor), (int16_t)0) % 256;
+ ubytes[13] /* rangestart */ = util::min((int16_t)std::round(range[1] * angleFactor), (int16_t)255) % 256;
+
+ shorts[8] /* tex */ = tx;
+ shorts[9] /* tex */ = ty;
+
+ return idx;
+}
+
+}
diff --git a/src/mbgl/geometry/icon_buffer.hpp b/src/mbgl/geometry/icon_buffer.hpp
new file mode 100644
index 0000000000..08c9687004
--- /dev/null
+++ b/src/mbgl/geometry/icon_buffer.hpp
@@ -0,0 +1,22 @@
+#ifndef MBGL_GEOMETRY_ICON_BUFFER
+#define MBGL_GEOMETRY_ICON_BUFFER
+
+#include <mbgl/geometry/buffer.hpp>
+
+#include <array>
+
+namespace mbgl {
+
+ class IconVertexBuffer : public Buffer<
+ 20
+ > {
+ public:
+ static const double angleFactor;
+
+ size_t add(int16_t x, int16_t y, float ox, float oy, int16_t tx, int16_t ty, float angle, float minzoom, std::array<float, 2> range, float maxzoom, float labelminzoom);
+
+ };
+
+}
+
+#endif
diff --git a/src/mbgl/geometry/line_buffer.cpp b/src/mbgl/geometry/line_buffer.cpp
new file mode 100644
index 0000000000..50a6e66b93
--- /dev/null
+++ b/src/mbgl/geometry/line_buffer.cpp
@@ -0,0 +1,23 @@
+#include <mbgl/geometry/line_buffer.hpp>
+#include <mbgl/platform/gl.hpp>
+
+#include <cmath>
+
+using namespace mbgl;
+
+size_t LineVertexBuffer::add(vertex_type x, vertex_type y, float ex, float ey, int8_t tx, int8_t ty, int32_t linesofar) {
+ size_t idx = index();
+ void *data = addElement();
+
+ int16_t *coords = static_cast<int16_t *>(data);
+ coords[0] = (x * 2) | tx;
+ coords[1] = (y * 2) | ty;
+
+ int8_t *extrude = static_cast<int8_t *>(data);
+ extrude[4] = std::round(extrudeScale * ex);
+ extrude[5] = std::round(extrudeScale * ey);
+
+ coords[3] = linesofar;
+
+ return idx;
+}
diff --git a/src/mbgl/geometry/line_buffer.hpp b/src/mbgl/geometry/line_buffer.hpp
new file mode 100644
index 0000000000..1c217b59d2
--- /dev/null
+++ b/src/mbgl/geometry/line_buffer.hpp
@@ -0,0 +1,39 @@
+#ifndef MBGL_GEOMETRY_LINE_BUFFER
+#define MBGL_GEOMETRY_LINE_BUFFER
+
+#include <mbgl/geometry/buffer.hpp>
+
+namespace mbgl {
+
+class LineVertexBuffer : public Buffer<
+ 8 // 2 coordinates per vertex + 1 linesofar + 1 extrude coord pair == 4 (== 8 bytes)
+> {
+public:
+ typedef int16_t vertex_type;
+
+ /*
+ * Scale the extrusion vector so that the normal length is this value.
+ * Contains the "texture" normals (-1..1). This is distinct from the extrude
+ * normals for line joins, because the x-value remains 0 for the texture
+ * normal array, while the extrude normal actually moves the vertex to create
+ * the acute/bevelled line join.
+ */
+ static const int8_t extrudeScale = 63;
+
+ /*
+ * Add a vertex to this buffer
+ *
+ * @param {number} x vertex position
+ * @param {number} y vertex position
+ * @param {number} ex extrude normal
+ * @param {number} ey extrude normal
+ * @param {number} tx texture normal
+ * @param {number} ty texture normal
+ */
+ size_t add(vertex_type x, vertex_type y, float ex, float ey, int8_t tx, int8_t ty, int32_t linesofar = 0);
+};
+
+
+}
+
+#endif
diff --git a/src/mbgl/geometry/resample.cpp b/src/mbgl/geometry/resample.cpp
new file mode 100644
index 0000000000..abb3ef1e3c
--- /dev/null
+++ b/src/mbgl/geometry/resample.cpp
@@ -0,0 +1,62 @@
+#include <mbgl/geometry/resample.hpp>
+
+#include <mbgl/util/interpolate.hpp>
+
+#include <cmath>
+
+namespace mbgl {
+
+const float minScale = 0.5f;
+const std::array<std::vector<float>, 4> minScaleArrays = {{
+ /*1:*/ { minScale },
+ /*2:*/ { minScale, 2 },
+ /*4:*/ { minScale, 4, 2, 4 },
+ /*8:*/ { minScale, 8, 4, 8, 2, 8, 4, 8 }
+}};
+
+
+Anchors resample(const std::vector<Coordinate> &vertices, float spacing,
+ const float /*minScale*/, float maxScale, const float tilePixelRatio,
+ const int start) {
+
+ maxScale = std::round(std::fmax(std::fmin(8.0f, maxScale / 2.0f), 1.0f));
+ spacing *= tilePixelRatio / maxScale;
+ const size_t index = util::clamp<size_t>(std::floor(std::log(maxScale) / std::log(2)), 0, minScaleArrays.size() - 1);
+ const std::vector<float> &minScales = minScaleArrays[index];
+ const size_t len = minScales.size();
+
+ float distance = 0.0f;
+ float markedDistance = 0.0f;
+ int added = start;
+
+ Anchors points;
+
+ auto end = vertices.end() - 1;
+ int i = 0;
+ for (auto it = vertices.begin(); it != end; it++, i++) {
+ const Coordinate &a = *(it), b = *(it + 1);
+
+ float segmentDist = util::dist<float>(a, b);
+ float angle = util::angle_to(b, a);
+
+ while (markedDistance + spacing < distance + segmentDist) {
+ markedDistance += spacing;
+
+ float t = (markedDistance - distance) / segmentDist,
+ x = util::interpolate(a.x, b.x, t),
+ y = util::interpolate(a.y, b.y, t),
+ s = minScales[added % len];
+
+ if (x >= 0 && x < 4096 && y >= 0 && y < 4096) {
+ points.emplace_back(x, y, angle, s, i);
+ }
+
+ added++;
+ }
+
+ distance += segmentDist;
+ }
+
+ return points;
+}
+}
diff --git a/src/mbgl/geometry/resample.hpp b/src/mbgl/geometry/resample.hpp
new file mode 100644
index 0000000000..bcfe4ca53d
--- /dev/null
+++ b/src/mbgl/geometry/resample.hpp
@@ -0,0 +1,13 @@
+#ifndef MBGL_GEOMETRY_INTERPOLATE
+#define MBGL_GEOMETRY_INTERPOLATE
+
+#include <mbgl/geometry/anchor.hpp>
+#include <mbgl/util/math.hpp>
+
+namespace mbgl {
+
+Anchors resample(const std::vector<Coordinate> &vertices, float spacing,
+ float minScale, float maxScale, float tilePixelRatio, int start = 0);
+}
+
+#endif
diff --git a/src/mbgl/geometry/sprite_atlas.cpp b/src/mbgl/geometry/sprite_atlas.cpp
new file mode 100644
index 0000000000..7dc8f60ae6
--- /dev/null
+++ b/src/mbgl/geometry/sprite_atlas.cpp
@@ -0,0 +1,261 @@
+#include <mbgl/geometry/sprite_atlas.hpp>
+#include <mbgl/platform/gl.hpp>
+#include <mbgl/platform/platform.hpp>
+#include <mbgl/util/math.hpp>
+#include <mbgl/util/std.hpp>
+#include <mbgl/util/constants.hpp>
+
+#include <mbgl/map/sprite.hpp>
+
+#include <cassert>
+#include <cmath>
+#include <algorithm>
+
+
+using namespace mbgl;
+
+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::recursive_mutex> lock(mtx);
+
+ const float oldRatio = pixelRatio;
+ pixelRatio = newRatio;
+
+ if (data) {
+ uint32_t *old_data = data;
+
+ data = nullptr;
+ allocate();
+
+ const int old_w = width * oldRatio;
+ const int old_h = height * oldRatio;
+ const int new_w = width * newRatio;
+ const int new_h = height * newRatio;
+
+ // Basic image scaling. TODO: Replace this with better image scaling.
+ uint32_t *img_new = reinterpret_cast<uint32_t *>(data);
+ const uint32_t *img_old = reinterpret_cast<const uint32_t *>(old_data);
+
+ for (int y = 0; y < new_h; y++) {
+ const int old_yoffset = ((y * old_h) / new_h) * old_w;
+ const int new_yoffset = y * new_w;
+ for (int x = 0; x < new_w; x++) {
+ const int old_x = (x * old_w) / new_w;
+ img_new[new_yoffset + x] = img_old[old_yoffset + old_x];
+ }
+ }
+
+ ::operator delete(old_data);
+ dirty = true;
+
+ // Mark all sprite images as in need of update
+ for (const auto &pair : images) {
+ uninitialized.emplace(pair.first);
+ }
+ }
+
+ 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) {
+ 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) {
+ for (int x = 0; x < width; x++) {
+ dst[x] = src[x];
+ }
+ }
+}
+
+Rect<SpriteAtlas::dimension> SpriteAtlas::allocateImage(size_t pixel_width, size_t pixel_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(pixel_width + 2 * buffer, pixel_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::getImage(const std::string& name) {
+ std::lock_guard<std::recursive_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) {
+ if (debug::spriteWarnings) {
+ fprintf(stderr, "[WARNING] sprite atlas bitmap overflow\n");
+ }
+ return rect;
+ }
+
+ images.emplace(name, rect);
+
+ copy(rect, pos);
+
+ return rect;
+}
+
+SpriteAtlasPosition SpriteAtlas::getPosition(const std::string& name, bool repeating) {
+ std::lock_guard<std::recursive_mutex> lock(mtx);
+ // `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
+ Rect<dimension> rect = getImage(name);
+ const int r = repeating ? 1 : 0;
+ return SpriteAtlasPosition {
+ {{ float(rect.w) / pixelRatio, float(rect.h) / pixelRatio }},
+ {{ float(rect.x + r) / width, float(rect.y + r) / height }},
+ {{ float(rect.x + rect.w - 2*r) / width, float(rect.y + rect.h - 2*r) / height }}
+ };
+}
+
+void SpriteAtlas::allocate() {
+ if (!data) {
+ dimension w = static_cast<dimension>(width * pixelRatio);
+ dimension h = static_cast<dimension>(height * pixelRatio);
+ data = static_cast<uint32_t*>(::operator new(w * h * sizeof(uint32_t)));
+ std::fill(data, data + w * h, 0);
+ }
+}
+
+void SpriteAtlas::copy(const Rect<dimension>& dst, const SpritePosition& src) {
+ if (!sprite->raster) return;
+ const uint32_t *src_img = reinterpret_cast<const uint32_t *>(sprite->raster->getData());
+ if (!src_img) return;
+ 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::setSprite(util::ptr<Sprite> sprite_) {
+ std::lock_guard<std::recursive_mutex> lock(mtx);
+
+ sprite = sprite_;
+
+ if (!sprite->isLoaded()) return;
+
+ util::erase_if(uninitialized, [this](const std::string &name) {
+ Rect<dimension> dst = getImage(name);
+ const SpritePosition& src = sprite->getSpritePosition(name);
+ if (!src) {
+ if (debug::spriteWarnings) {
+ fprintf(stderr, "[WARNING] sprite doesn't have image with name '%s'\n", name.c_str());
+ }
+ return true;
+ }
+
+ if (src.width == dst.w * pixelRatio && src.height == dst.h * pixelRatio && src.pixelRatio == pixelRatio) {
+ copy(dst, src);
+ return true;
+ } else {
+ if (debug::spriteWarnings) {
+ fprintf(stderr, "[WARNING] sprite icon dimension mismatch\n");
+ }
+ return false;
+ }
+ });
+}
+
+void SpriteAtlas::bind(bool linear) {
+ bool first = false;
+ if (!texture) {
+ glGenTextures(1, &texture);
+ glBindTexture(GL_TEXTURE_2D, texture);
+#ifndef GL_ES_VERSION_2_0
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
+#endif
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ first = true;
+ } 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::recursive_mutex> lock(mtx);
+ allocate();
+
+ if (first) {
+ 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
+ );
+ } else {
+ glTexSubImage2D(
+ GL_TEXTURE_2D, // GLenum target
+ 0, // GLint level
+ 0, // GLint xoffset
+ 0, // GLint yoffset
+ width * pixelRatio, // GLsizei width
+ height * pixelRatio, // GLsizei height
+ GL_RGBA, // GLenum format
+ GL_UNSIGNED_BYTE, // GLenum type
+ data // const GLvoid *pixels
+ );
+ }
+
+ dirty = false;
+ }
+};
+
+SpriteAtlas::~SpriteAtlas() {
+ std::lock_guard<std::recursive_mutex> lock(mtx);
+
+ glDeleteTextures(1, &texture);
+ texture = 0;
+ ::operator delete(data), data = nullptr;
+}
diff --git a/src/mbgl/geometry/sprite_atlas.hpp b/src/mbgl/geometry/sprite_atlas.hpp
new file mode 100644
index 0000000000..9e0fe995bb
--- /dev/null
+++ b/src/mbgl/geometry/sprite_atlas.hpp
@@ -0,0 +1,82 @@
+#ifndef MBGL_GEOMETRY_SPRITE_ATLAS
+#define MBGL_GEOMETRY_SPRITE_ATLAS
+
+#include <mbgl/geometry/binpack.hpp>
+
+#include <mbgl/util/noncopyable.hpp>
+#include <mbgl/util/ptr.hpp>
+
+#include <string>
+#include <map>
+#include <mutex>
+#include <atomic>
+#include <set>
+#include <array>
+
+namespace mbgl {
+
+class Sprite;
+class SpritePosition;
+
+struct SpriteAtlasPosition {
+ std::array<float, 2> size;
+ std::array<float, 2> tl;
+ std::array<float, 2> br;
+};
+
+class SpriteAtlas : public util::noncopyable {
+public:
+ typedef uint16_t dimension;
+
+ // 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);
+
+ // Changes the source sprite.
+ void setSprite(util::ptr<Sprite> sprite);
+
+ // Returns the coordinates of an image that is sourced from the sprite image.
+ // This getter attempts to read the image from the sprite if it is already loaded.
+ // In that case, it copies it into the sprite atlas and returns the dimensions.
+ // Otherwise, it returns a 0/0/0/0 rect.
+ Rect<dimension> getImage(const std::string& name);
+
+ SpriteAtlasPosition getPosition(const std::string& name, bool repeating = false);
+
+ // 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; }
+ inline float getPixelRatio() const { return pixelRatio; }
+
+ const dimension width = 0;
+ const dimension height = 0;
+
+private:
+ void allocate();
+ Rect<SpriteAtlas::dimension> allocateImage(size_t width, size_t height);
+ void copy(const Rect<dimension>& dst, const SpritePosition& src);
+
+ std::recursive_mutex mtx;
+ float pixelRatio = 1.0f;
+ BinPack<dimension> bin;
+ util::ptr<Sprite> sprite;
+ std::map<std::string, Rect<dimension>> images;
+ std::set<std::string> uninitialized;
+ uint32_t *data = nullptr;
+ std::atomic<bool> dirty;
+ uint32_t texture = 0;
+ uint32_t filter = 0;
+ static const int buffer = 1;
+};
+
+};
+
+#endif
diff --git a/src/mbgl/geometry/static_vertex_buffer.cpp b/src/mbgl/geometry/static_vertex_buffer.cpp
new file mode 100644
index 0000000000..c86211c50f
--- /dev/null
+++ b/src/mbgl/geometry/static_vertex_buffer.cpp
@@ -0,0 +1,14 @@
+#include <mbgl/geometry/static_vertex_buffer.hpp>
+#include <mbgl/platform/gl.hpp>
+
+namespace mbgl {
+
+StaticVertexBuffer::StaticVertexBuffer(std::initializer_list<std::pair<int16_t, int16_t>> init) {
+ for (const std::pair<int16_t, int16_t> &vertex : init) {
+ vertex_type *vertices = static_cast<vertex_type *>(addElement());
+ vertices[0] = vertex.first;
+ vertices[1] = vertex.second;
+ }
+}
+
+}
diff --git a/src/mbgl/geometry/static_vertex_buffer.hpp b/src/mbgl/geometry/static_vertex_buffer.hpp
new file mode 100644
index 0000000000..ce932269f0
--- /dev/null
+++ b/src/mbgl/geometry/static_vertex_buffer.hpp
@@ -0,0 +1,26 @@
+#ifndef MBGL_GEOMETRY_STATIC_VERTEX_BUFFER
+#define MBGL_GEOMETRY_STATIC_VERTEX_BUFFER
+
+#include <mbgl/geometry/buffer.hpp>
+
+#include <vector>
+#include <cstddef>
+#include <cstdint>
+#include <cmath>
+
+namespace mbgl {
+
+class StaticVertexBuffer : public Buffer<
+ 4, // bytes per vertex (2 * signed short == 4 bytes)
+ GL_ARRAY_BUFFER,
+ 32 // default length
+> {
+public:
+ typedef int16_t vertex_type;
+
+ StaticVertexBuffer(std::initializer_list<std::pair<int16_t, int16_t>> init);
+};
+
+}
+
+#endif
diff --git a/src/mbgl/geometry/text_buffer.cpp b/src/mbgl/geometry/text_buffer.cpp
new file mode 100644
index 0000000000..295ff02efa
--- /dev/null
+++ b/src/mbgl/geometry/text_buffer.cpp
@@ -0,0 +1,33 @@
+#include <mbgl/geometry/text_buffer.hpp>
+#include <mbgl/platform/gl.hpp>
+#include <mbgl/util/math.hpp>
+
+#include <cmath>
+
+using namespace mbgl;
+
+const double TextVertexBuffer::angleFactor = 128.0 / M_PI;
+
+size_t TextVertexBuffer::add(int16_t x, int16_t y, float ox, float oy, uint16_t tx, uint16_t ty, float angle, float minzoom, std::array<float, 2> range, float maxzoom, float labelminzoom) {
+ size_t idx = index();
+ void *data = addElement();
+
+ int16_t *shorts = static_cast<int16_t *>(data);
+ shorts[0] = x;
+ shorts[1] = y;
+ shorts[2] = std::round(ox * 64); // use 1/64 pixels for placement
+ shorts[3] = std::round(oy * 64);
+
+ uint8_t *ubytes = static_cast<uint8_t *>(data);
+ ubytes[8] = labelminzoom * 10;
+ ubytes[9] = minzoom * 10; // 1/10 zoom levels: z16 == 160.
+ ubytes[10] = fmin(maxzoom, 25) * 10; // 1/10 zoom levels: z16 == 160.
+ ubytes[11] = (int16_t)round(angle * angleFactor) % 256;
+ ubytes[12] = util::max((int16_t)std::round(range[0] * angleFactor), (int16_t)0) % 256;
+ ubytes[13] = util::min((int16_t)std::round(range[1] * angleFactor), (int16_t)255) % 256;
+
+ ubytes[14] = tx / 4;
+ ubytes[15] = ty / 4;
+
+ return idx;
+}
diff --git a/src/mbgl/geometry/text_buffer.hpp b/src/mbgl/geometry/text_buffer.hpp
new file mode 100644
index 0000000000..4687b32f97
--- /dev/null
+++ b/src/mbgl/geometry/text_buffer.hpp
@@ -0,0 +1,25 @@
+#ifndef MBGL_GEOMETRY_TEXT_BUFFER
+#define MBGL_GEOMETRY_TEXT_BUFFER
+
+#include <mbgl/geometry/buffer.hpp>
+#include <array>
+
+namespace mbgl {
+
+class TextVertexBuffer : public Buffer <
+ 16,
+ GL_ARRAY_BUFFER,
+ 32768
+> {
+public:
+ typedef int16_t vertex_type;
+
+ static const double angleFactor;
+
+ size_t add(int16_t x, int16_t y, float ox, float oy, uint16_t tx, uint16_t ty, float angle, float minzoom, std::array<float, 2> range, float maxzoom, float labelminzoom);
+};
+
+
+}
+
+#endif
diff --git a/src/mbgl/geometry/vao.cpp b/src/mbgl/geometry/vao.cpp
new file mode 100644
index 0000000000..66822ba5ce
--- /dev/null
+++ b/src/mbgl/geometry/vao.cpp
@@ -0,0 +1,55 @@
+#include <mbgl/geometry/vao.hpp>
+#include <mbgl/platform/log.hpp>
+#include <mbgl/util/string.hpp>
+
+namespace mbgl {
+
+VertexArrayObject::~VertexArrayObject() {
+ if (!gl::DeleteVertexArrays) return;
+
+ if (vao) {
+ gl::DeleteVertexArrays(1, &vao);
+ }
+}
+
+void VertexArrayObject::bindVertexArrayObject() {
+ if (!gl::GenVertexArrays || !gl::BindVertexArray) {
+ static bool reported = false;
+ if (!reported) {
+ Log::Warning(Event::OpenGL, "Not using Vertex Array Objects");
+ reported = true;
+ }
+ return;
+ }
+
+ if (!vao) {
+ gl::GenVertexArrays(1, &vao);
+ }
+ gl::BindVertexArray(vao);
+}
+
+void VertexArrayObject::verifyBinding(Shader &shader, GLuint vertexBuffer, GLuint elementsBuffer,
+ char *offset) {
+ if (bound_shader != shader.getID()) {
+ throw std::runtime_error(std::string("trying to rebind VAO to another shader from " +
+ util::toString(bound_shader) + "(" + bound_shader_name + ") to " +
+ util::toString(shader.getID()) + "(" + shader.name + ")" ));
+ } else if (bound_offset != offset) {
+ throw std::runtime_error("trying to bind VAO to another offset");
+ } else if (bound_vertex_buffer != vertexBuffer) {
+ throw std::runtime_error("trying to bind VAO to another vertex buffer");
+ } else if (bound_elements_buffer != elementsBuffer) {
+ throw std::runtime_error("trying to bind VAO to another elements buffer");
+ }
+}
+
+void VertexArrayObject::storeBinding(Shader &shader, GLuint vertexBuffer, GLuint elementsBuffer,
+ char *offset) {
+ bound_shader = shader.getID();
+ bound_shader_name = shader.name;
+ bound_offset = offset;
+ bound_vertex_buffer = vertexBuffer;
+ bound_elements_buffer = elementsBuffer;
+}
+
+}
diff --git a/src/mbgl/geometry/vao.hpp b/src/mbgl/geometry/vao.hpp
new file mode 100644
index 0000000000..2ecba731f7
--- /dev/null
+++ b/src/mbgl/geometry/vao.hpp
@@ -0,0 +1,73 @@
+#ifndef MBGL_GEOMETRY_VAO
+#define MBGL_GEOMETRY_VAO
+
+#include <mbgl/shader/shader.hpp>
+#include <mbgl/platform/gl.hpp>
+#include <mbgl/util/noncopyable.hpp>
+
+#include <stdexcept>
+
+namespace mbgl {
+
+class VertexArrayObject : public util::noncopyable {
+public:
+ inline VertexArrayObject() {};
+
+ inline VertexArrayObject(VertexArrayObject &&rhs) noexcept
+ : vao(rhs.vao),
+ bound_shader(rhs.bound_shader),
+ bound_shader_name(rhs.bound_shader_name),
+ bound_vertex_buffer(rhs.bound_vertex_buffer),
+ bound_elements_buffer(rhs.bound_elements_buffer),
+ bound_offset(rhs.bound_offset) {};
+
+ template <typename Shader, typename VertexBuffer>
+ inline void bind(Shader& shader, VertexBuffer &vertexBuffer, char *offset) {
+ bindVertexArrayObject();
+ if (bound_shader == 0) {
+ vertexBuffer.bind();
+ shader.bind(offset);
+ if (vao) {
+ storeBinding(shader, vertexBuffer.getID(), 0, offset);
+ }
+ } else {
+ verifyBinding(shader, vertexBuffer.getID(), 0, offset);
+ }
+ }
+
+ template <typename Shader, typename VertexBuffer, typename ElementsBuffer>
+ inline void bind(Shader& shader, VertexBuffer &vertexBuffer, ElementsBuffer &elementsBuffer, char *offset) {
+ bindVertexArrayObject();
+ if (bound_shader == 0) {
+ vertexBuffer.bind();
+ elementsBuffer.bind();
+ shader.bind(offset);
+ if (vao) {
+ storeBinding(shader, vertexBuffer.getID(), elementsBuffer.getID(), offset);
+ }
+ } else {
+ verifyBinding(shader, vertexBuffer.getID(), elementsBuffer.getID(), offset);
+ }
+ }
+
+ ~VertexArrayObject();
+
+private:
+ void bindVertexArrayObject();
+ void storeBinding(Shader &shader, GLuint vertexBuffer, GLuint elementsBuffer, char *offset);
+ void verifyBinding(Shader &shader, GLuint vertexBuffer, GLuint elementsBuffer, char *offset);
+
+ GLuint vao = 0;
+
+ // For debug reasons, we're storing the bind information so that we can
+ // detect errors and report
+ GLuint bound_shader = 0;
+ const char *bound_shader_name = "";
+ GLuint bound_vertex_buffer = 0;
+ GLuint bound_elements_buffer = 0;
+ char *bound_offset = 0;
+};
+
+}
+
+#endif