diff options
Diffstat (limited to 'src/mbgl')
-rw-r--r-- | src/mbgl/geometry/sprite_atlas.cpp | 111 | ||||
-rw-r--r-- | src/mbgl/map/map.cpp | 2 | ||||
-rw-r--r-- | src/mbgl/map/sprite.cpp | 7 | ||||
-rw-r--r-- | src/mbgl/map/sprite.hpp | 2 | ||||
-rw-r--r-- | src/mbgl/util/compression.cpp | 96 | ||||
-rw-r--r-- | src/mbgl/util/compression.hpp | 15 | ||||
-rw-r--r-- | src/mbgl/util/scaling.cpp | 111 | ||||
-rw-r--r-- | src/mbgl/util/scaling.hpp | 23 |
8 files changed, 312 insertions, 55 deletions
diff --git a/src/mbgl/geometry/sprite_atlas.cpp b/src/mbgl/geometry/sprite_atlas.cpp index 077550ff74..dce772f2e4 100644 --- a/src/mbgl/geometry/sprite_atlas.cpp +++ b/src/mbgl/geometry/sprite_atlas.cpp @@ -5,6 +5,7 @@ #include <mbgl/util/math.hpp> #include <mbgl/util/std.hpp> #include <mbgl/util/constants.hpp> +#include <mbgl/util/scaling.hpp> #include <mbgl/map/sprite.hpp> @@ -66,34 +67,6 @@ bool SpriteAtlas::resize(const float newRatio) { 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_height, const int dst_x, const int dst_y, - const int width, const int height, const bool wrap) { - if (wrap) { - - for (int y = -1; y <= height; y++) { - int dst_y_wrapped = (y + dst_y + dst_height) % dst_height; - int src_y_wrapped = ((y + height) % height) + src_y; - int srcI = src_y_wrapped * src_stride + src_x; - int dstI = dst_y_wrapped * dst_stride; - for (int x = -1; x <= width; x++) { - int dst_x_wrapped = (x + dst_x + dst_stride) % dst_stride; - int src_x_wrapped = (x + width) % width; - dst[dstI + dst_x_wrapped] = src[srcI + src_x_wrapped]; - } - } - - } else { - dst += dst_y * dst_stride + dst_x; - src += src_y * src_stride + src_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(const size_t pixel_width, const size_t pixel_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 @@ -176,25 +149,53 @@ void SpriteAtlas::allocate() { void SpriteAtlas::copy(const Rect<dimension>& dst, const SpritePosition& src, const bool wrap) { if (!sprite->raster) return; - const uint32_t *src_img = reinterpret_cast<const uint32_t *>(sprite->raster->getData()); - if (!src_img) return; + + const uint32_t *srcData = reinterpret_cast<const uint32_t *>(sprite->raster->getData()); + if (!srcData) return; + const vec2<uint32_t> srcSize { sprite->raster->getWidth(), sprite->raster->getHeight() }; + const Rect<uint32_t> srcPos { src.x, src.y, src.width, src.height }; + 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 height */ height * pixelRatio, - /* dest x */ dst.x * pixelRatio, - /* dest y */ dst.y * pixelRatio, - /* icon dimension */ src.width, - /* icon dimension */ src.height, - /* wrap padding */ wrap - ); + uint32_t *dstData = reinterpret_cast<uint32_t *>(data); + const vec2<uint32_t> dstSize { static_cast<unsigned int>(width * pixelRatio), + static_cast<unsigned int>(height * pixelRatio) }; + const Rect<uint32_t> dstPos { static_cast<uint32_t>(dst.x * pixelRatio), + static_cast<uint32_t>(dst.y * pixelRatio), + static_cast<uint32_t>(dst.originalW * pixelRatio), + static_cast<uint32_t>(dst.originalH * pixelRatio) }; + + util::bilinearScale(srcData, srcSize, srcPos, dstData, dstSize, dstPos); + + // Add borders around the copied image if required. + if (wrap) { + // We're copying from the same image so we don't have to scale again. + const uint32_t border = 1; + // Left border + if (dstPos.x >= border) { + util::nearestNeighborScale( + dstData, dstSize, { dstPos.x + dstPos.w - border - 1, dstPos.y, border, dstPos.h }, + dstData, dstSize, { dstPos.x - border, dstPos.y, border, dstPos.h }); + } + // Right border + util::nearestNeighborScale(dstData, dstSize, { dstPos.x, dstPos.y, border, dstPos.h }, + dstData, dstSize, + { dstPos.x + dstPos.w, dstPos.y, border, dstPos.h }); + + // Top border + if (dstPos.y >= border) { + util::nearestNeighborScale( + dstData, dstSize, { dstPos.x - border, dstPos.y + dstPos.h - border - 1, + dstPos.w + 2 * border, border }, + dstData, dstSize, + { dstPos.x - border, dstPos.y - border, dstPos.w + 2 * border, border }); + } + + // Bottom border + util::nearestNeighborScale( + dstData, dstSize, { dstPos.x - border, dstPos.y, dstPos.w + 2 * border, border }, + dstData, dstSize, + { dstPos.x - border, dstPos.y + dstPos.h, dstPos.w + 2 * border, border }); + } dirty = true; } @@ -236,8 +237,10 @@ void SpriteAtlas::bind(bool linear) { #ifndef GL_ES_VERSION_2_0 MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0)); #endif - 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_REPEAT)); + // We are using clamp to edge here since OpenGL ES doesn't allow GL_REPEAT on NPOT textures. + // We use those when the pixelRatio isn't a power of two, e.g. on iPhone 6 Plus. + MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); + 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)); @@ -255,7 +258,7 @@ void SpriteAtlas::bind(bool linear) { allocate(); if (first) { - glTexImage2D( + MBGL_CHECK_ERROR(glTexImage2D( GL_TEXTURE_2D, // GLenum target 0, // GLint level GL_RGBA, // GLint internalformat @@ -265,9 +268,9 @@ void SpriteAtlas::bind(bool linear) { GL_RGBA, // GLenum format GL_UNSIGNED_BYTE, // GLenum type data // const GLvoid * data - ); + )); } else { - glTexSubImage2D( + MBGL_CHECK_ERROR(glTexSubImage2D( GL_TEXTURE_2D, // GLenum target 0, // GLint level 0, // GLint xoffset @@ -277,12 +280,14 @@ void SpriteAtlas::bind(bool linear) { GL_RGBA, // GLenum format GL_UNSIGNED_BYTE, // GLenum type data // const GLvoid *pixels - ); + )); } dirty = false; - // platform::show_color_debug_image("Sprite Atlas", reinterpret_cast<const char *>(data), width, height, width * pixelRatio, height * pixelRatio); +#ifndef GL_ES_VERSION_2_0 + // platform::showColorDebugImage("Sprite Atlas", reinterpret_cast<const char *>(data), width * pixelRatio, height * pixelRatio, width * pixelRatio, height * pixelRatio); +#endif } }; diff --git a/src/mbgl/map/map.cpp b/src/mbgl/map/map.cpp index 29521f9499..d12afc9143 100644 --- a/src/mbgl/map/map.cpp +++ b/src/mbgl/map/map.cpp @@ -374,7 +374,7 @@ std::string Map::getStyleJSON() const { util::ptr<Sprite> Map::getSprite() { const float pixelRatio = state.getPixelRatio(); const std::string &sprite_url = style->getSpriteURL(); - if (!sprite || sprite->pixelRatio != pixelRatio) { + if (!sprite || !sprite->hasPixelRatio(pixelRatio)) { sprite = Sprite::Create(sprite_url, pixelRatio, *env); } diff --git a/src/mbgl/map/sprite.cpp b/src/mbgl/map/sprite.cpp index 114e8f45f5..8883ee092d 100644 --- a/src/mbgl/map/sprite.cpp +++ b/src/mbgl/map/sprite.cpp @@ -32,7 +32,7 @@ util::ptr<Sprite> Sprite::Create(const std::string &base_url, float pixelRatio, Sprite::Sprite(const Key &, const std::string& base_url, float pixelRatio_) : valid(base_url.length() > 0), - pixelRatio(pixelRatio_), + pixelRatio(pixelRatio_ > 1 ? 2 : 1), spriteURL(base_url + (pixelRatio_ > 1 ? "@2x" : "") + ".png"), jsonURL(base_url + (pixelRatio_ > 1 ? "@2x" : "") + ".json"), raster(), @@ -41,6 +41,11 @@ Sprite::Sprite(const Key &, const std::string& base_url, float pixelRatio_) future(promise.get_future()) { } +bool Sprite::hasPixelRatio(float ratio) const { + return pixelRatio == (ratio > 1 ? 2 : 1); +} + + void Sprite::waitUntilLoaded() const { future.wait(); } diff --git a/src/mbgl/map/sprite.hpp b/src/mbgl/map/sprite.hpp index cb0c274dee..6c1b3ba8e3 100644 --- a/src/mbgl/map/sprite.hpp +++ b/src/mbgl/map/sprite.hpp @@ -43,6 +43,8 @@ public: const SpritePosition &getSpritePosition(const std::string& name) const; + bool hasPixelRatio(float ratio) const; + void waitUntilLoaded() const; bool isLoaded() const; diff --git a/src/mbgl/util/compression.cpp b/src/mbgl/util/compression.cpp new file mode 100644 index 0000000000..3a1658b8f6 --- /dev/null +++ b/src/mbgl/util/compression.cpp @@ -0,0 +1,96 @@ +#include "compression.hpp" + +#include <zlib.h> + +#include <cstring> +#include <stdexcept> + + +// Check zlib library version. +const static bool zlibVersionCheck = []() { + const char *const version = zlibVersion(); + if (version[0] != ZLIB_VERSION[0]) { + char message[96]; + snprintf(message, 96, "zlib version mismatch: headers report %s, but library reports %s", + ZLIB_VERSION, version); + throw std::runtime_error(message); + } + + return true; +}(); + + +namespace mbgl { +namespace util { + +std::string compress(const std::string &raw) { + z_stream deflate_stream; + memset(&deflate_stream, 0, sizeof(deflate_stream)); + + // TODO: reuse z_streams + if (deflateInit(&deflate_stream, Z_DEFAULT_COMPRESSION) != Z_OK) { + throw std::runtime_error("failed to initialize deflate"); + } + + deflate_stream.next_in = (Bytef *)raw.data(); + deflate_stream.avail_in = uInt(raw.size()); + + std::string result; + char out[16384]; + + int code; + do { + deflate_stream.next_out = reinterpret_cast<Bytef *>(out); + deflate_stream.avail_out = sizeof(out); + code = deflate(&deflate_stream, Z_FINISH); + if (result.size() < deflate_stream.total_out) { + // append the block to the output string + result.append(out, deflate_stream.total_out - result.size()); + } + } while (code == Z_OK); + + deflateEnd(&deflate_stream); + + if (code != Z_STREAM_END) { + throw std::runtime_error(deflate_stream.msg); + } + + return result; +} + +std::string decompress(const std::string &raw) { + z_stream inflate_stream; + memset(&inflate_stream, 0, sizeof(inflate_stream)); + + // TODO: reuse z_streams + if (inflateInit(&inflate_stream) != Z_OK) { + throw std::runtime_error("failed to initialize inflate"); + } + + inflate_stream.next_in = (Bytef *)raw.data(); + inflate_stream.avail_in = uInt(raw.size()); + + std::string result; + char out[15384]; + + int code; + do { + inflate_stream.next_out = reinterpret_cast<Bytef *>(out); + inflate_stream.avail_out = sizeof(out); + code = inflate(&inflate_stream, 0); + // result.append(out, sizeof(out) - inflate_stream.avail_out); + if (result.size() < inflate_stream.total_out) { + result.append(out, inflate_stream.total_out - result.size()); + } + } while (code == Z_OK); + + inflateEnd(&inflate_stream); + + if (code != Z_STREAM_END) { + throw std::runtime_error(inflate_stream.msg ? inflate_stream.msg : "decompression error"); + } + + return result; +} +} +} diff --git a/src/mbgl/util/compression.hpp b/src/mbgl/util/compression.hpp new file mode 100644 index 0000000000..a33b2476a7 --- /dev/null +++ b/src/mbgl/util/compression.hpp @@ -0,0 +1,15 @@ +#ifndef MBGL_UTIL_COMPRESSION +#define MBGL_UTIL_COMPRESSION + +#include <string> + +namespace mbgl { +namespace util { + +std::string compress(const std::string &raw); +std::string decompress(const std::string &raw); + +} +} + +#endif diff --git a/src/mbgl/util/scaling.cpp b/src/mbgl/util/scaling.cpp new file mode 100644 index 0000000000..a554b2e137 --- /dev/null +++ b/src/mbgl/util/scaling.cpp @@ -0,0 +1,111 @@ +#include "scaling.hpp" + +namespace { + +using namespace mbgl; + +inline uint8_t bilinearInterpolate(uint8_t tl, uint8_t tr, uint8_t bl, uint8_t br, double dx, double dy) { + const double t = dx * (tr - tl) + tl; + const double b = dx * (br - bl) + bl; + return t + dy * (b - t); +} + +template <size_t i> +inline const uint8_t& b(const uint32_t& w) { + return reinterpret_cast<const uint8_t*>(&w)[i]; +} + +template <size_t i> +inline uint8_t& b(uint32_t& w) { + return reinterpret_cast<uint8_t*>(&w)[i]; +} + +vec2<double> getFactor(const Rect<uint32_t>& srcPos, const Rect<uint32_t>& dstPos) { + return { + double(srcPos.w) / dstPos.w, + double(srcPos.h) / dstPos.h + }; +} + +vec2<uint32_t> getBounds(const vec2<uint32_t>& srcSize, const Rect<uint32_t>& srcPos, + const vec2<uint32_t>& dstSize, const Rect<uint32_t>& dstPos, + const vec2<double>& factor) { + if (srcPos.x > srcSize.x || srcPos.y > srcSize.y || + dstPos.x > dstSize.x || dstPos.y > dstSize.y) { + // Source or destination position is out of range. + return { 0, 0 }; + } + + // Make sure we don't read/write values out of range. + return { std::min(uint32_t(double(srcSize.x - srcPos.x) / factor.x), + std::min(dstSize.x - dstPos.x, dstPos.w)), + std::min(uint32_t(double(srcSize.y - srcPos.y) / factor.y), + std::min(dstSize.y - dstPos.y, dstPos.h)) }; +} +} + +namespace mbgl { +namespace util { + +void bilinearScale(const uint32_t* srcData, const vec2<uint32_t>& srcSize, + const Rect<uint32_t>& srcPos, uint32_t* dstData, const vec2<uint32_t>& dstSize, + const Rect<uint32_t>& dstPos) { + const auto factor = getFactor(srcPos, dstPos); + const auto bounds = getBounds(srcSize, srcPos, dstSize, dstPos, factor); + + double fractSrcY = srcPos.y; + double fractSrcX; + uint32_t x, y; + size_t i = dstSize.x * dstPos.y + dstPos.x; + for (y = 0; y < bounds.y; y++) { + fractSrcX = srcPos.x; + const uint32_t srcY0 = fractSrcY; + const uint32_t srcY1 = std::min(srcY0 + 1, srcSize.y - 1); + for (x = 0; x < bounds.x; x++) { + const uint32_t srcX0 = fractSrcX; + const uint32_t srcX1 = std::min(srcX0 + 1, srcSize.x - 1); + + const uint32_t tl = srcData[srcSize.x * srcY0 + srcX0]; + const uint32_t tr = srcData[srcSize.x * srcY0 + srcX1]; + const uint32_t bl = srcData[srcSize.x * srcY1 + srcX0]; + const uint32_t br = srcData[srcSize.x * srcY1 + srcX1]; + + const double dx = fractSrcX - srcX0; + const double dy = fractSrcY - srcY0; + uint32_t& dst = dstData[i + x]; + b<0>(dst) = bilinearInterpolate(b<0>(tl), b<0>(tr), b<0>(bl), b<0>(br), dx, dy); + b<1>(dst) = bilinearInterpolate(b<1>(tl), b<1>(tr), b<1>(bl), b<1>(br), dx, dy); + b<2>(dst) = bilinearInterpolate(b<2>(tl), b<2>(tr), b<2>(bl), b<2>(br), dx, dy); + b<3>(dst) = bilinearInterpolate(b<3>(tl), b<3>(tr), b<3>(bl), b<3>(br), dx, dy); + fractSrcX += factor.x; + } + i += dstSize.x; + fractSrcY += factor.y; + } +} + +void nearestNeighborScale(const uint32_t* srcData, const vec2<uint32_t>& srcSize, + const Rect<uint32_t>& srcPos, uint32_t* dstData, + const vec2<uint32_t>& dstSize, const Rect<uint32_t>& dstPos) { + const auto factor = getFactor(srcPos, dstPos); + const auto bounds = getBounds(srcSize, srcPos, dstSize, dstPos, factor); + + double fractSrcY = srcPos.y; + double fractSrcX; + size_t i = dstSize.x * dstPos.y + dstPos.x; + uint32_t srcY; + uint32_t x, y; + for (y = 0; y < bounds.y; y++) { + fractSrcX = srcPos.x; + srcY = srcSize.x * uint32_t(fractSrcY); + for (x = 0; x < bounds.x; x++) { + dstData[i + x] = srcData[srcY + uint32_t(fractSrcX)]; + fractSrcX += factor.x; + } + i += dstSize.x; + fractSrcY += factor.y; + } +} + +} +}
\ No newline at end of file diff --git a/src/mbgl/util/scaling.hpp b/src/mbgl/util/scaling.hpp new file mode 100644 index 0000000000..d2625e9219 --- /dev/null +++ b/src/mbgl/util/scaling.hpp @@ -0,0 +1,23 @@ +#ifndef MBGL_UTIL_SCALING +#define MBGL_UTIL_SCALING + + +#include <mbgl/util/vec.hpp> +#include <mbgl/util/rect.hpp> + +#include <cstdint> + +namespace mbgl { +namespace util { + +void bilinearScale(const uint32_t* srcData, const vec2<uint32_t>& srcSize, + const Rect<uint32_t>& srcPos, uint32_t* dstData, const vec2<uint32_t>& dstSize, + const Rect<uint32_t>& dstPos); + +void nearestNeighborScale(const uint32_t* srcData, const vec2<uint32_t>& srcSize, + const Rect<uint32_t>& srcPos, uint32_t* dstData, + const vec2<uint32_t>& dstSize, const Rect<uint32_t>& dstPos); +} +} + +#endif |