#include #include #include #include #include #include #include #include #include using namespace mbgl; LineAtlas::LineAtlas(GLsizei w, GLsizei h) : width(w), height(h), data(std::make_unique(w * h)), dirty(true) { } LineAtlas::~LineAtlas() { assert(util::ThreadContext::currentlyOn(util::ThreadType::Map)); } LinePatternPos LineAtlas::getDashPosition(const std::vector &dasharray, bool round) { assert(util::ThreadContext::currentlyOn(util::ThreadType::Map)); size_t key = round ? std::numeric_limits::min() : std::numeric_limits::max(); for (const float part : dasharray) { boost::hash_combine(key, part); } // Note: We're not handling hash collisions here. const auto it = positions.find(key); if (it == positions.end()) { auto inserted = positions.emplace(key, addDash(dasharray, round)); assert(inserted.second); return inserted.first->second; } else { return it->second; } } LinePatternPos LineAtlas::addDash(const std::vector &dasharray, bool round) { int n = round ? 7 : 0; int dashheight = 2 * n + 1; const uint8_t offset = 128; if (nextRow + dashheight > height) { Log::Warning(Event::OpenGL, "line atlas bitmap overflow"); return LinePatternPos(); } float length = 0; for (const float part : dasharray) { length += part; } float stretch = width / length; float halfWidth = stretch * 0.5; // If dasharray has an odd length, both the first and last parts // are dashes and should be joined seamlessly. bool oddLength = dasharray.size() % 2 == 1; for (int y = -n; y <= n; y++) { int row = nextRow + n + y; int index = width * row; float left = 0; float right = dasharray[0]; unsigned int partIndex = 1; if (oddLength) { left -= dasharray.back(); } for (int x = 0; x < width; x++) { while (right < x / stretch) { left = right; right = right + dasharray[partIndex]; if (oddLength && partIndex == dasharray.size() - 1) { right += dasharray.front(); } partIndex++; } float distLeft = fabs(x - left * stretch); float distRight = fabs(x - right * stretch); float dist = fmin(distLeft, distRight); bool inside = (partIndex % 2) == 1; int signedDistance; if (round) { float distMiddle = n ? (float)y / n * (halfWidth + 1) : 0; if (inside) { float distEdge = halfWidth - fabs(distMiddle); signedDistance = sqrt(dist * dist + distEdge * distEdge); } else { signedDistance = halfWidth - sqrt(dist * dist + distMiddle * distMiddle); } } else { signedDistance = int((inside ? 1 : -1) * dist); } data[index + x] = fmax(0, fmin(255, signedDistance + offset)); } } LinePatternPos position; position.y = (0.5 + nextRow + n) / height; position.height = (2.0 * n) / height; position.width = length; nextRow += dashheight; dirty = true; bind(); return position; }; void LineAtlas::upload() { if (dirty) { bind(); } } void LineAtlas::bind() { assert(util::ThreadContext::currentlyOn(util::ThreadType::Map)); bool first = false; if (!texture) { texture.create(); MBGL_CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, texture.getID())); MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT)); MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); first = true; } else { MBGL_CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, texture.getID())); } if (dirty) { if (first) { MBGL_CHECK_ERROR(glTexImage2D( GL_TEXTURE_2D, // GLenum target 0, // GLint level GL_ALPHA, // GLint internalformat width, // GLsizei width height, // GLsizei height 0, // GLint border GL_ALPHA, // GLenum format GL_UNSIGNED_BYTE, // GLenum type data.get() // const GLvoid * data )); } else { MBGL_CHECK_ERROR(glTexSubImage2D( GL_TEXTURE_2D, // GLenum target 0, // GLint level 0, // GLint xoffset 0, // GLint yoffset width, // GLsizei width height, // GLsizei height GL_ALPHA, // GLenum format GL_UNSIGNED_BYTE, // GLenum type data.get() // const GLvoid *pixels )); } dirty = false; } };