diff options
Diffstat (limited to 'src/mbgl/geometry')
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 |