diff --git a/.gitignore b/.gitignore index d3bf790..03938c2 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ Debug Library/ Release Library/ x86/ x64/ +.vs/ *.exe *.user *.sdf diff --git a/Msdfgen.vcxproj b/Msdfgen.vcxproj index 8c508f1..423a69f 100644 --- a/Msdfgen.vcxproj +++ b/Msdfgen.vcxproj @@ -296,6 +296,7 @@ + @@ -307,7 +308,9 @@ - + + + @@ -316,6 +319,8 @@ + + @@ -333,7 +338,9 @@ - + + + diff --git a/Msdfgen.vcxproj.filters b/Msdfgen.vcxproj.filters index 5c1fd80..55220f7 100644 --- a/Msdfgen.vcxproj.filters +++ b/Msdfgen.vcxproj.filters @@ -102,7 +102,22 @@ Core - + + Core + + + Core + + + Core + + + Core + + + Core + + Core @@ -176,7 +191,13 @@ Core - + + Core + + + Core + + Core diff --git a/core/Scanline.cpp b/core/Scanline.cpp index 556ef0a..8e5352d 100644 --- a/core/Scanline.cpp +++ b/core/Scanline.cpp @@ -65,8 +65,8 @@ Scanline::Scanline() : lastIndex(0) { } void Scanline::preprocess() { lastIndex = 0; - if (!this->intersections.empty()) { - qsort(&this->intersections[0], this->intersections.size(), sizeof(Intersection), compareIntersections); + if (!intersections.empty()) { + qsort(&intersections[0], intersections.size(), sizeof(Intersection), compareIntersections); int totalDirection = 0; for (std::vector::iterator intersection = intersections.begin(); intersection != intersections.end(); ++intersection) { totalDirection += intersection->direction; diff --git a/core/Shape.cpp b/core/Shape.cpp index 6d1a0cc..a0df08e 100644 --- a/core/Shape.cpp +++ b/core/Shape.cpp @@ -94,7 +94,7 @@ void Shape::scanline(Scanline &line, double y) const { int Shape::edgeCount() const { int total = 0; for (std::vector::const_iterator contour = contours.begin(); contour != contours.end(); ++contour) - total += contour->edges.size(); + total += (int) contour->edges.size(); return total; } diff --git a/core/ShapeDistanceFinder.h b/core/ShapeDistanceFinder.h new file mode 100644 index 0000000..57df8d8 --- /dev/null +++ b/core/ShapeDistanceFinder.h @@ -0,0 +1,37 @@ + +#pragma once + +#include +#include "Vector2.h" +#include "edge-selectors.h" +#include "contour-combiners.h" + +namespace msdfgen { + +/// Finds the distance between a point and a Shape. ContourCombiner dictates the distance metric and its data type. +template +class ShapeDistanceFinder { + +public: + typedef typename ContourCombiner::DistanceType DistanceType; + + // Passed shape object must persist until the distance finder is destroyed! + explicit ShapeDistanceFinder(const Shape &shape); + /// Finds the distance from origin. Not thread-safe! Is fastest when subsequent queries are close together. + DistanceType distance(const Point2 &origin); + + /// Finds the distance between shape and origin. Does not allocate result cache used to optimize performance of multiple queries. + static DistanceType oneShotDistance(const Shape &shape, const Point2 &origin); + +private: + const Shape &shape; + ContourCombiner contourCombiner; + std::vector shapeEdgeCache; + +}; + +typedef ShapeDistanceFinder > SimpleTrueShapeDistanceFinder; + +} + +#include "ShapeDistanceFinder.hpp" diff --git a/core/ShapeDistanceFinder.hpp b/core/ShapeDistanceFinder.hpp new file mode 100644 index 0000000..028738e --- /dev/null +++ b/core/ShapeDistanceFinder.hpp @@ -0,0 +1,56 @@ + +#include "ShapeDistanceFinder.h" + +namespace msdfgen { + +template +ShapeDistanceFinder::ShapeDistanceFinder(const Shape &shape) : shape(shape), contourCombiner(shape), shapeEdgeCache(shape.edgeCount()) { } + +template +typename ShapeDistanceFinder::DistanceType ShapeDistanceFinder::distance(const Point2 &origin) { + contourCombiner.reset(origin); + typename ContourCombiner::EdgeSelectorType::EdgeCache *edgeCache = &shapeEdgeCache[0]; + + for (std::vector::const_iterator contour = shape.contours.begin(); contour != shape.contours.end(); ++contour) { + if (!contour->edges.empty()) { + typename ContourCombiner::EdgeSelectorType &edgeSelector = contourCombiner.edgeSelector(int(contour-shape.contours.begin())); + + const EdgeSegment *prevEdge = contour->edges.size() >= 2 ? *(contour->edges.end()-2) : *contour->edges.begin(); + const EdgeSegment *curEdge = contour->edges.back(); + for (std::vector::const_iterator edge = contour->edges.begin(); edge != contour->edges.end(); ++edge) { + const EdgeSegment *nextEdge = *edge; + edgeSelector.addEdge(*edgeCache++, prevEdge, curEdge, nextEdge); + prevEdge = curEdge; + curEdge = nextEdge; + } + } + } + + return contourCombiner.distance(); +} + +template +typename ShapeDistanceFinder::DistanceType ShapeDistanceFinder::oneShotDistance(const Shape &shape, const Point2 &origin) { + ContourCombiner contourCombiner(shape); + contourCombiner.reset(origin); + + for (std::vector::const_iterator contour = shape.contours.begin(); contour != shape.contours.end(); ++contour) { + if (!contour->edges.empty()) { + typename ContourCombiner::EdgeSelectorType &edgeSelector = contourCombiner.edgeSelector(int(contour-shape.contours.begin())); + + const EdgeSegment *prevEdge = contour->edges.size() >= 2 ? *(contour->edges.end()-2) : *contour->edges.begin(); + const EdgeSegment *curEdge = contour->edges.back(); + for (std::vector::const_iterator edge = contour->edges.begin(); edge != contour->edges.end(); ++edge) { + const EdgeSegment *nextEdge = *edge; + typename ContourCombiner::EdgeSelectorType::EdgeCache dummy; + edgeSelector.addEdge(dummy, prevEdge, curEdge, nextEdge); + prevEdge = curEdge; + curEdge = nextEdge; + } + } + } + + return contourCombiner.distance(); +} + +} diff --git a/core/bitmap-interpolation.hpp b/core/bitmap-interpolation.hpp new file mode 100644 index 0000000..a14b0fb --- /dev/null +++ b/core/bitmap-interpolation.hpp @@ -0,0 +1,25 @@ + +#pragma once + +#include "arithmetics.hpp" +#include "Vector2.h" +#include "BitmapRef.hpp" + +namespace msdfgen { + +template +static void interpolate(T *output, const BitmapConstRef &bitmap, Point2 pos) { + pos -= .5; + int l = (int) floor(pos.x); + int b = (int) floor(pos.y); + int r = l+1; + int t = b+1; + double lr = pos.x-l; + double bt = pos.y-b; + l = clamp(l, bitmap.width-1), r = clamp(r, bitmap.width-1); + b = clamp(b, bitmap.height-1), t = clamp(t, bitmap.height-1); + for (int i = 0; i < N; ++i) + output[i] = mix(mix(bitmap(l, b)[i], bitmap(r, b)[i], lr), mix(bitmap(l, t)[i], bitmap(r, t)[i], lr), bt); +} + +} diff --git a/core/msdf-edge-artifact-patcher.cpp b/core/msdf-edge-artifact-patcher.cpp new file mode 100644 index 0000000..a1d8c76 --- /dev/null +++ b/core/msdf-edge-artifact-patcher.cpp @@ -0,0 +1,173 @@ + +#include "msdf-edge-artifact-patcher.h" + +#include +#include +#include "arithmetics.hpp" +#include "equation-solver.h" +#include "bitmap-interpolation.hpp" +#include "edge-selectors.h" +#include "contour-combiners.h" +#include "ShapeDistanceFinder.h" + +namespace msdfgen { + +static bool isHotspot(float am, float bm, float xm) { + return (am > .5f && bm > .5f && xm < .5f) || (am < .5f && bm < .5f && xm > .5f); + // A much more aggressive version for the entire distance field (not just edges): return median(am, bm, xm) != xm; +} + +static int findLinearChannelHotspots(double t[1], const float *a, const float *b, float dA, float dB) { + int found = 0; + double x = (double) dA/(dA-dB); + if (x > 0 && x < 1) { + float am = median(a[0], a[1], a[2]); + float bm = median(b[0], b[1], b[2]); + float xm = median( + mix(a[0], b[0], x), + mix(a[1], b[1], x), + mix(a[2], b[2], x) + ); + if (isHotspot(am, bm, xm)) + t[found++] = x; + } + return found; +} + +static int findDiagonalChannelHotspots(double t[2], const float *a, const float *b, const float *c, const float *d, float dA, float dB, float dC, float dD) { + int found = 0; + double x[2]; + int solutions = solveQuadratic(x, (dD-dC)-(dB-dA), dC+dB-2*dA, dA); + for (int i = 0; i < solutions; ++i) + if (x[i] > 0 && x[i] < 1) { + float am = median(a[0], a[1], a[2]); + float bm = median(b[0], b[1], b[2]); + float xm = median( + mix(mix(a[0], b[0], x[i]), mix(c[0], d[0], x[i]), x[i]), + mix(mix(a[1], b[1], x[i]), mix(c[1], d[1], x[i]), x[i]), + mix(mix(a[2], b[2], x[i]), mix(c[2], d[2], x[i]), x[i]) + ); + if (isHotspot(am, bm, xm)) + t[found++] = x[i]; + } + return found; +} + +static int findLinearHotspots(double t[3], const float *a, const float *b) { + int found = 0; + found += findLinearChannelHotspots(t+found, a, b, a[1]-a[0], b[1]-b[0]); + found += findLinearChannelHotspots(t+found, a, b, a[2]-a[1], b[2]-b[1]); + found += findLinearChannelHotspots(t+found, a, b, a[0]-a[2], b[0]-b[2]); + return found; +} + +static int findDiagonalHotspots(double t[6], const float *a, const float *b, const float *c, const float *d) { + int found = 0; + found += findDiagonalChannelHotspots(t+found, a, b, c, d, a[1]-a[0], b[1]-b[0], c[1]-c[0], d[1]-d[0]); + found += findDiagonalChannelHotspots(t+found, a, b, c, d, a[2]-a[1], b[2]-b[1], c[2]-c[1], d[2]-d[1]); + found += findDiagonalChannelHotspots(t+found, a, b, c, d, a[0]-a[2], b[0]-b[2], c[0]-c[2], d[0]-d[2]); + return found; +} + +template +void findHotspots(std::vector &hotspots, const BitmapConstRef &sdf) { + // All hotspots intersect either the horizontal, vertical, or diagonal line that connects neighboring texels + // Horizontal: + for (int y = 0; y < sdf.height; ++y) { + const float *left = sdf(0, y); + const float *right = sdf(1, y); + for (int x = 0; x < sdf.width-1; ++x) { + double t[3]; + int found = findLinearHotspots(t, left, right); + for (int i = 0; i < found; ++i) + hotspots.push_back(Point2(x+.5+t[i], y+.5)); + left += N, right += N; + } + } + // Vertical: + for (int y = 0; y < sdf.height-1; ++y) { + const float *bottom = sdf(0, y); + const float *top = sdf(0, y+1); + for (int x = 0; x < sdf.width; ++x) { + double t[3]; + int found = findLinearHotspots(t, bottom, top); + for (int i = 0; i < found; ++i) + hotspots.push_back(Point2(x+.5, y+.5+t[i])); + bottom += N, top += N; + } + } + // Diagonal: + for (int y = 0; y < sdf.height-1; ++y) { + const float *lb = sdf(0, y); + const float *rb = sdf(1, y); + const float *lt = sdf(0, y+1); + const float *rt = sdf(1, y+1); + for (int x = 0; x < sdf.width-1; ++x) { + double t[6]; + int found = 0; + found = findDiagonalHotspots(t, lb, rb, lt, rt); + for (int i = 0; i < found; ++i) + hotspots.push_back(Point2(x+.5+t[i], y+.5+t[i])); + found = findDiagonalHotspots(t, lt, rt, lb, rb); + for (int i = 0; i < found; ++i) + hotspots.push_back(Point2(x+.5+t[i], y+1.5-t[i])); + lb += N, rb += N, lt += N, rt += N; + } + } +} + +template