From 8f3fefd1e2910965771056c2fdc2b7bdcb9fe072 Mon Sep 17 00:00:00 2001 From: Chris Loer Date: Fri, 7 Jul 2017 13:31:53 -0700 Subject: [core] C++ port of TinySDF --- cmake/core-files.cmake | 2 + src/mbgl/util/tiny_sdf.cpp | 105 +++++++++++++++++++++++++++++++++++++++++++++ src/mbgl/util/tiny_sdf.hpp | 20 +++++++++ 3 files changed, 127 insertions(+) create mode 100644 src/mbgl/util/tiny_sdf.cpp create mode 100644 src/mbgl/util/tiny_sdf.hpp diff --git a/cmake/core-files.cmake b/cmake/core-files.cmake index d6c9493742..138fea1bcb 100644 --- a/cmake/core-files.cmake +++ b/cmake/core-files.cmake @@ -689,6 +689,8 @@ set(MBGL_CORE_FILES src/mbgl/util/tile_coordinate.hpp src/mbgl/util/tile_cover.cpp src/mbgl/util/tile_cover.hpp + src/mbgl/util/tiny_sdf.cpp + src/mbgl/util/tiny_sdf.hpp src/mbgl/util/token.hpp src/mbgl/util/url.cpp src/mbgl/util/url.hpp diff --git a/src/mbgl/util/tiny_sdf.cpp b/src/mbgl/util/tiny_sdf.cpp new file mode 100644 index 0000000000..60839357d5 --- /dev/null +++ b/src/mbgl/util/tiny_sdf.cpp @@ -0,0 +1,105 @@ +#include + +#include + +#include + +namespace mbgl { +namespace util { + +namespace tinysdf { + +static const double INF = 1e20; + +// 1D squared distance transform +void edt1d(std::vector& f, + std::vector& d, + std::vector& v, + std::vector& z, + uint32_t n) { + v[0] = 0; + z[0] = -INF; + z[1] = +INF; + + for (uint32_t q = 1, k = 0; q < n; q++) { + double s = ((f[q] + q * q) - (f[v[k]] + v[k] * v[k])) / (2 * q - 2 * v[k]); + while (s <= z[k]) { + k--; + s = ((f[q] + q * q) - (f[v[k]] + v[k] * v[k])) / (2 * q - 2 * v[k]); + } + k++; + v[k] = q; + z[k] = s; + z[k + 1] = +INF; + } + + for (uint32_t q = 0, k = 0; q < n; q++) { + while (z[k + 1] < q) k++; + d[q] = (q - v[k]) * (q - v[k]) + f[v[k]]; + } +} + + +// 2D Euclidean distance transform by Felzenszwalb & Huttenlocher https://cs.brown.edu/~pff/dt/ +void edt(std::vector& data, + uint32_t width, + uint32_t height, + std::vector& f, + std::vector& d, + std::vector& v, + std::vector& z) { + for (uint32_t x = 0; x < width; x++) { + for (uint32_t y = 0; y < height; y++) { + f[y] = data[y * width + x]; + } + edt1d(f, d, v, z, height); + for (uint32_t y = 0; y < height; y++) { + data[y * width + x] = d[y]; + } + } + for (uint32_t y = 0; y < height; y++) { + for (uint32_t x = 0; x < width; x++) { + f[x] = data[y * width + x]; + } + edt1d(f, d, v, z, width); + for (uint32_t x = 0; x < width; x++) { + data[y * width + x] = std::sqrt(d[x]); + } + } +} + +} // namespace tinysdf + +AlphaImage transformRasterToSDF(const AlphaImage& rasterInput, double radius, double cutoff) { + uint32_t size = rasterInput.size.width * rasterInput.size.height; + uint32_t maxDimension = std::max(rasterInput.size.width, rasterInput.size.height); + + AlphaImage sdf(rasterInput.size); + + // temporary arrays for the distance transform + std::vector gridOuter(size); + std::vector gridInner(size); + std::vector f(maxDimension); + std::vector d(maxDimension); + std::vector z(maxDimension + 1); + std::vector v(maxDimension); + + for (uint32_t i = 0; i < size; i++) { + double a = double(rasterInput.data[i]) / 255; // alpha value + gridOuter[i] = a == 1.0 ? 0.0 : a == 0.0 ? tinysdf::INF : std::pow(std::max(0.0, 0.5 - a), 2.0); + gridInner[i] = a == 1.0 ? tinysdf::INF : a == 0.0 ? 0.0 : std::pow(std::max(0.0, a - 0.5), 2.0); + } + + tinysdf::edt(gridOuter, rasterInput.size.width, rasterInput.size.height, f, d, v, z); + tinysdf::edt(gridInner, rasterInput.size.width, rasterInput.size.height, f, d, v, z); + + for (uint32_t i = 0; i < size; i++) { + double distance = gridOuter[i] - gridInner[i]; + sdf.data[i] = std::max(0l, std::min(255l, std::lround(255.0 - 255.0 * (distance / radius + cutoff)))); + } + + return sdf; +} + +} // namespace util +} // namespace mbgl diff --git a/src/mbgl/util/tiny_sdf.hpp b/src/mbgl/util/tiny_sdf.hpp new file mode 100644 index 0000000000..33c9280cbd --- /dev/null +++ b/src/mbgl/util/tiny_sdf.hpp @@ -0,0 +1,20 @@ +#pragma once + +#include + +namespace mbgl { +namespace util { + +/* + C++ port of https://github.com/mapbox/tiny-sdf, which is in turn based on the + Felzenszwalb/Huttenlocher distance transform paper (https://cs.brown.edu/~pff/papers/dt-final.pdf). + Note there exists an alternative C++ implementation from the paper’s authors at + https://cs.brown.edu/~pff/dt/, which this implementation is not based on. + + Takes an alpha channel raster input and transforms it into an alpha channel + Signed Distance Field (SDF) output of the same dimensions. +*/ +AlphaImage transformRasterToSDF(const AlphaImage& rasterInput, double radius, double cutoff); + +} // namespace util +} // namespace mbgl -- cgit v1.2.1