From 6c1e1d01eefe6b566b2ad89071317dd3b611daac Mon Sep 17 00:00:00 2001 From: Chlumsky Date: Wed, 10 Apr 2019 14:27:49 +0200 Subject: [PATCH] Performance optimization - using squared distance metric until the last moment --- CHANGELOG.md | 2 +- core/SignedDistance.cpp | 17 +++++--- core/SignedDistance.h | 7 ++-- core/Vector2.cpp | 4 ++ core/Vector2.h | 2 + core/contour-combiners.cpp | 84 +++++++++++++++++++------------------- core/contour-combiners.h | 8 ++-- core/edge-segments.cpp | 69 +++++++++++++++---------------- core/edge-selectors.cpp | 34 +++++++-------- core/edge-selectors.h | 8 ++-- core/msdfgen.cpp | 37 ++++++++--------- main.cpp | 2 +- 12 files changed, 142 insertions(+), 132 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a427adb..065fca4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,5 @@ -## Version 1.6 (2019-04-06) +## Version 1.6 (2019-04-08) - Core algorithm rewritten to split up advanced edge selection logic into modular template arguments. - Pseudo-distance evaluation reworked to eliminate discontinuities at the midpoint between edges. diff --git a/core/SignedDistance.cpp b/core/SignedDistance.cpp index 18c9d2c..941278b 100644 --- a/core/SignedDistance.cpp +++ b/core/SignedDistance.cpp @@ -2,29 +2,34 @@ #include "SignedDistance.h" #include +#include "arithmetics.hpp" namespace msdfgen { const SignedDistance SignedDistance::INFINITE(-1e240, 1); -SignedDistance::SignedDistance() : distance(-1e240), dot(1) { } +SignedDistance::SignedDistance() : sqDistance(-1e240), dot(1) { } -SignedDistance::SignedDistance(double dist, double d) : distance(dist), dot(d) { } +SignedDistance::SignedDistance(double sqDistance, double d) : sqDistance(sqDistance), dot(d) { } + +double SignedDistance::distance() const { + return sign(sqDistance)*sqrt(fabs(sqDistance)); +} bool operator<(SignedDistance a, SignedDistance b) { - return fabs(a.distance) < fabs(b.distance) || (fabs(a.distance) == fabs(b.distance) && a.dot < b.dot); + return fabs(a.sqDistance) < fabs(b.sqDistance) || (fabs(a.sqDistance) == fabs(b.sqDistance) && a.dot < b.dot); } bool operator>(SignedDistance a, SignedDistance b) { - return fabs(a.distance) > fabs(b.distance) || (fabs(a.distance) == fabs(b.distance) && a.dot > b.dot); + return fabs(a.sqDistance) > fabs(b.sqDistance) || (fabs(a.sqDistance) == fabs(b.sqDistance) && a.dot > b.dot); } bool operator<=(SignedDistance a, SignedDistance b) { - return fabs(a.distance) < fabs(b.distance) || (fabs(a.distance) == fabs(b.distance) && a.dot <= b.dot); + return fabs(a.sqDistance) < fabs(b.sqDistance) || (fabs(a.sqDistance) == fabs(b.sqDistance) && a.dot <= b.dot); } bool operator>=(SignedDistance a, SignedDistance b) { - return fabs(a.distance) > fabs(b.distance) || (fabs(a.distance) == fabs(b.distance) && a.dot >= b.dot); + return fabs(a.sqDistance) > fabs(b.sqDistance) || (fabs(a.sqDistance) == fabs(b.sqDistance) && a.dot >= b.dot); } } diff --git a/core/SignedDistance.h b/core/SignedDistance.h index 034210f..b943554 100644 --- a/core/SignedDistance.h +++ b/core/SignedDistance.h @@ -3,17 +3,18 @@ namespace msdfgen { -/// Represents a signed distance and alignment, which together can be compared to uniquely determine the closest edge segment. +/// Represents a signed squared distance and alignment, which together can be compared to uniquely determine the closest edge segment. class SignedDistance { public: static const SignedDistance INFINITE; - double distance; + double sqDistance; double dot; SignedDistance(); - SignedDistance(double dist, double d); + SignedDistance(double sqDistance, double d); + double distance() const; friend bool operator<(SignedDistance a, SignedDistance b); friend bool operator>(SignedDistance a, SignedDistance b); diff --git a/core/Vector2.cpp b/core/Vector2.cpp index 896963f..64b731f 100644 --- a/core/Vector2.cpp +++ b/core/Vector2.cpp @@ -19,6 +19,10 @@ double Vector2::length() const { return sqrt(x*x+y*y); } +double Vector2::squaredLength() const { + return x*x+y*y; +} + double Vector2::direction() const { return atan2(y, x); } diff --git a/core/Vector2.h b/core/Vector2.h index 47ca637..a470c18 100644 --- a/core/Vector2.h +++ b/core/Vector2.h @@ -23,6 +23,8 @@ struct Vector2 { void set(double x, double y); /// Returns the vector's length. double length() const; + /// Returns the vector's squared length. (less expensive operation than length) + double squaredLength() const; /// Returns the angle of the vector in radians (atan2). double direction() const; /// Returns the normalized vector - one that has the same direction but unit length. diff --git a/core/contour-combiners.cpp b/core/contour-combiners.cpp index 61b5dfb..bee6cf3 100644 --- a/core/contour-combiners.cpp +++ b/core/contour-combiners.cpp @@ -5,22 +5,22 @@ namespace msdfgen { -static void initDistance(double &distance) { - distance = SignedDistance::INFINITE.distance; +static void initDistance(double &sqDistance) { + sqDistance = SignedDistance::INFINITE.sqDistance; } -static void initDistance(MultiDistance &distance) { - distance.r = SignedDistance::INFINITE.distance; - distance.g = SignedDistance::INFINITE.distance; - distance.b = SignedDistance::INFINITE.distance; +static void initDistance(MultiDistance &sqDistance) { + sqDistance.r = SignedDistance::INFINITE.sqDistance; + sqDistance.g = SignedDistance::INFINITE.sqDistance; + sqDistance.b = SignedDistance::INFINITE.sqDistance; } -static double resolveDistance(double distance) { - return distance; +static double resolveDistance(double sqDistance) { + return sqDistance; } -static double resolveDistance(const MultiDistance &distance) { - return median(distance.r, distance.g, distance.b); +static double resolveDistance(const MultiDistance &sqDistance) { + return median(sqDistance.r, sqDistance.g, sqDistance.b); } template @@ -32,13 +32,13 @@ void SimpleContourCombiner::reset(const Point2 &p) { } template -void SimpleContourCombiner::setContourEdge(int i, const EdgeSelector &edgeSelector) { +void SimpleContourCombiner::setContourEdgeSelection(int i, const EdgeSelector &edgeSelector) { shapeEdgeSelector.merge(edgeSelector); } template -typename SimpleContourCombiner::DistanceType SimpleContourCombiner::distance() const { - return shapeEdgeSelector.distance(); +typename SimpleContourCombiner::DistanceType SimpleContourCombiner::squaredDistance() const { + return shapeEdgeSelector.squaredDistance(); } template class SimpleContourCombiner; @@ -61,58 +61,58 @@ void OverlappingContourCombiner::reset(const Point2 &p) { } template -void OverlappingContourCombiner::setContourEdge(int i, const EdgeSelector &edgeSelector) { - DistanceType edgeDistance = edgeSelector.distance(); +void OverlappingContourCombiner::setContourEdgeSelection(int i, const EdgeSelector &edgeSelector) { + DistanceType edgeSqDistance = edgeSelector.squaredDistance(); edgeSelectors[i] = edgeSelector; shapeEdgeSelector.merge(edgeSelector); - if (windings[i] > 0 && resolveDistance(edgeDistance) >= 0) + if (windings[i] > 0 && resolveDistance(edgeSqDistance) >= 0) innerEdgeSelector.merge(edgeSelector); - if (windings[i] < 0 && resolveDistance(edgeDistance) <= 0) + if (windings[i] < 0 && resolveDistance(edgeSqDistance) <= 0) outerEdgeSelector.merge(edgeSelector); } template -typename OverlappingContourCombiner::DistanceType OverlappingContourCombiner::distance() const { - DistanceType shapeDistance = shapeEdgeSelector.distance(); - DistanceType innerDistance = innerEdgeSelector.distance(); - DistanceType outerDistance = outerEdgeSelector.distance(); - double innerScalarDistance = resolveDistance(innerDistance); - double outerScalarDistance = resolveDistance(outerDistance); - DistanceType distance; - initDistance(distance); +typename OverlappingContourCombiner::DistanceType OverlappingContourCombiner::squaredDistance() const { + DistanceType shapeSqDistance = shapeEdgeSelector.squaredDistance(); + DistanceType innerSqDistance = innerEdgeSelector.squaredDistance(); + DistanceType outerSqDistance = outerEdgeSelector.squaredDistance(); + double innerScalarSqDistance = resolveDistance(innerSqDistance); + double outerScalarSqDistance = resolveDistance(outerSqDistance); + DistanceType sqDistance; + initDistance(sqDistance); int contourCount = (int) windings.size(); int winding = 0; - if (innerScalarDistance >= 0 && fabs(innerScalarDistance) <= fabs(outerScalarDistance)) { - distance = innerDistance; + if (innerScalarSqDistance >= 0 && fabs(innerScalarSqDistance) <= fabs(outerScalarSqDistance)) { + sqDistance = innerSqDistance; winding = 1; for (int i = 0; i < contourCount; ++i) if (windings[i] > 0) { - DistanceType contourDistance = edgeSelectors[i].distance(); - if (fabs(resolveDistance(contourDistance)) < fabs(outerScalarDistance) && resolveDistance(contourDistance) > resolveDistance(distance)) - distance = contourDistance; + DistanceType contourSqDistance = edgeSelectors[i].squaredDistance(); + if (fabs(resolveDistance(contourSqDistance)) < fabs(outerScalarSqDistance) && resolveDistance(contourSqDistance) > resolveDistance(sqDistance)) + sqDistance = contourSqDistance; } - } else if (outerScalarDistance <= 0 && fabs(outerScalarDistance) < fabs(innerScalarDistance)) { - distance = outerDistance; + } else if (outerScalarSqDistance <= 0 && fabs(outerScalarSqDistance) < fabs(innerScalarSqDistance)) { + sqDistance = outerSqDistance; winding = -1; for (int i = 0; i < contourCount; ++i) if (windings[i] < 0) { - DistanceType contourDistance = edgeSelectors[i].distance(); - if (fabs(resolveDistance(contourDistance)) < fabs(innerScalarDistance) && resolveDistance(contourDistance) < resolveDistance(distance)) - distance = contourDistance; + DistanceType contourSqDistance = edgeSelectors[i].squaredDistance(); + if (fabs(resolveDistance(contourSqDistance)) < fabs(innerScalarSqDistance) && resolveDistance(contourSqDistance) < resolveDistance(sqDistance)) + sqDistance = contourSqDistance; } } else - return shapeDistance; + return shapeSqDistance; for (int i = 0; i < contourCount; ++i) if (windings[i] != winding) { - DistanceType contourDistance = edgeSelectors[i].distance(); - if (resolveDistance(contourDistance)*resolveDistance(distance) >= 0 && fabs(resolveDistance(contourDistance)) < fabs(resolveDistance(distance))) - distance = contourDistance; + DistanceType contourSqDistance = edgeSelectors[i].squaredDistance(); + if (resolveDistance(contourSqDistance)*resolveDistance(sqDistance) >= 0 && fabs(resolveDistance(contourSqDistance)) < fabs(resolveDistance(sqDistance))) + sqDistance = contourSqDistance; } - if (resolveDistance(distance) == resolveDistance(shapeDistance)) - distance = shapeDistance; - return distance; + if (resolveDistance(sqDistance) == resolveDistance(shapeSqDistance)) + sqDistance = shapeSqDistance; + return sqDistance; } template class OverlappingContourCombiner; diff --git a/core/contour-combiners.h b/core/contour-combiners.h index c057968..fda4e5b 100644 --- a/core/contour-combiners.h +++ b/core/contour-combiners.h @@ -16,8 +16,8 @@ public: explicit SimpleContourCombiner(const Shape &shape); void reset(const Point2 &p); - void setContourEdge(int i, const EdgeSelector &edgeSelector); - DistanceType distance() const; + void setContourEdgeSelection(int i, const EdgeSelector &edgeSelector); + DistanceType squaredDistance() const; private: EdgeSelector shapeEdgeSelector; @@ -34,8 +34,8 @@ public: explicit OverlappingContourCombiner(const Shape &shape); void reset(const Point2 &p); - void setContourEdge(int i, const EdgeSelector &edgeSelector); - DistanceType distance() const; + void setContourEdgeSelection(int i, const EdgeSelector &edgeSelector); + DistanceType squaredDistance() const; private: std::vector windings; diff --git a/core/edge-segments.cpp b/core/edge-segments.cpp index 1f8bbef..5534449 100644 --- a/core/edge-segments.cpp +++ b/core/edge-segments.cpp @@ -12,9 +12,10 @@ void EdgeSegment::distanceToPseudoDistance(SignedDistance &distance, Point2 orig Vector2 aq = origin-point(0); double ts = dotProduct(aq, dir); if (ts < 0) { + // TODO can we get squared pseudo-distance without squaring it? double pseudoDistance = crossProduct(aq, dir); - if (fabs(pseudoDistance) <= fabs(distance.distance)) { - distance.distance = pseudoDistance; + if (pseudoDistance*pseudoDistance <= fabs(distance.sqDistance)) { + distance.sqDistance = pseudoDistance*fabs(pseudoDistance); distance.dot = 0; } } @@ -23,9 +24,10 @@ void EdgeSegment::distanceToPseudoDistance(SignedDistance &distance, Point2 orig Vector2 bq = origin-point(1); double ts = dotProduct(bq, dir); if (ts > 0) { + // TODO can we get squared pseudo-distance without squaring it? double pseudoDistance = crossProduct(bq, dir); - if (fabs(pseudoDistance) <= fabs(distance.distance)) { - distance.distance = pseudoDistance; + if (pseudoDistance*pseudoDistance <= fabs(distance.sqDistance)) { + distance.sqDistance = pseudoDistance*fabs(pseudoDistance); distance.dot = 0; } } @@ -98,14 +100,11 @@ SignedDistance LinearSegment::signedDistance(Point2 origin, double ¶m) const Vector2 aq = origin-p[0]; Vector2 ab = p[1]-p[0]; param = dotProduct(aq, ab)/dotProduct(ab, ab); - Vector2 eq = p[param > .5]-origin; - double endpointDistance = eq.length(); - if (param > 0 && param < 1) { - double orthoDistance = dotProduct(ab.getOrthonormal(false), aq); - if (fabs(orthoDistance) < endpointDistance) - return SignedDistance(orthoDistance, 0); - } - return SignedDistance(nonZeroSign(crossProduct(aq, ab))*endpointDistance, fabs(dotProduct(ab.normalize(), eq.normalize()))); + double distanceSign = nonZeroSign(crossProduct(aq, ab)); + if (param > 0 && param < 1) + return SignedDistance(distanceSign*(param*ab-aq).squaredLength(), 0); + Vector2 qe = p[param > .5]-origin; + return SignedDistance(distanceSign*qe.squaredLength(), fabs(dotProduct(ab.normalize(), qe.normalize()))); } SignedDistance QuadraticSegment::signedDistance(Point2 origin, double ¶m) const { @@ -119,32 +118,32 @@ SignedDistance QuadraticSegment::signedDistance(Point2 origin, double ¶m) co double t[3]; int solutions = solveCubic(t, a, b, c, d); - double minDistance = nonZeroSign(crossProduct(ab, qa))*qa.length(); // distance from A + double minSqDistance = nonZeroSign(crossProduct(ab, qa))*qa.squaredLength(); // distance from A param = -dotProduct(qa, ab)/dotProduct(ab, ab); { - double distance = nonZeroSign(crossProduct(p[2]-p[1], p[2]-origin))*(p[2]-origin).length(); // distance from B - if (fabs(distance) < fabs(minDistance)) { - minDistance = distance; + double sqDistance = nonZeroSign(crossProduct(p[2]-p[1], p[2]-origin))*(p[2]-origin).squaredLength(); // distance from B + if (fabs(sqDistance) < fabs(minSqDistance)) { + minSqDistance = sqDistance; param = dotProduct(origin-p[1], p[2]-p[1])/dotProduct(p[2]-p[1], p[2]-p[1]); } } for (int i = 0; i < solutions; ++i) { if (t[i] > 0 && t[i] < 1) { - Point2 endpoint = p[0]+2*t[i]*ab+t[i]*t[i]*br; - double distance = nonZeroSign(crossProduct(p[2]-p[0], endpoint-origin))*(endpoint-origin).length(); - if (fabs(distance) <= fabs(minDistance)) { - minDistance = distance; + Vector2 qe = qa+2*t[i]*ab+t[i]*t[i]*br; + double sqDistance = nonZeroSign(crossProduct(p[2]-p[0], qe))*qe.squaredLength(); + if (fabs(sqDistance) <= fabs(minSqDistance)) { + minSqDistance = sqDistance; param = t[i]; } } } if (param >= 0 && param <= 1) - return SignedDistance(minDistance, 0); + return SignedDistance(minSqDistance, 0); if (param < .5) - return SignedDistance(minDistance, fabs(dotProduct(ab.normalize(), qa.normalize()))); + return SignedDistance(minSqDistance, fabs(dotProduct(ab.normalize(), qa.normalize()))); else - return SignedDistance(minDistance, fabs(dotProduct((p[2]-p[1]).normalize(), (p[2]-origin).normalize()))); + return SignedDistance(minSqDistance, fabs(dotProduct((p[2]-p[1]).normalize(), (p[2]-origin).normalize()))); } SignedDistance CubicSegment::signedDistance(Point2 origin, double ¶m) const { @@ -154,13 +153,13 @@ SignedDistance CubicSegment::signedDistance(Point2 origin, double ¶m) const Vector2 as = (p[3]-p[2])-(p[2]-p[1])-br; Vector2 epDir = direction(0); - double minDistance = nonZeroSign(crossProduct(epDir, qa))*qa.length(); // distance from A + double minSqDistance = nonZeroSign(crossProduct(epDir, qa))*qa.squaredLength(); // distance from A param = -dotProduct(qa, epDir)/dotProduct(epDir, epDir); { epDir = direction(1); - double distance = nonZeroSign(crossProduct(epDir, p[3]-origin))*(p[3]-origin).length(); // distance from B - if (fabs(distance) < fabs(minDistance)) { - minDistance = distance; + double sqDistance = nonZeroSign(crossProduct(epDir, p[3]-origin))*(p[3]-origin).squaredLength(); // distance from B + if (fabs(sqDistance) < fabs(minSqDistance)) { + minSqDistance = sqDistance; param = dotProduct(origin+epDir-p[3], epDir)/dotProduct(epDir, epDir); } } @@ -168,10 +167,10 @@ SignedDistance CubicSegment::signedDistance(Point2 origin, double ¶m) const for (int i = 0; i <= MSDFGEN_CUBIC_SEARCH_STARTS; ++i) { double t = (double) i/MSDFGEN_CUBIC_SEARCH_STARTS; for (int step = 0;; ++step) { - Vector2 qpt = point(t)-origin; - double distance = nonZeroSign(crossProduct(direction(t), qpt))*qpt.length(); - if (fabs(distance) < fabs(minDistance)) { - minDistance = distance; + Vector2 qe = qa+3*t*ab+3*t*t*br+t*t*t*as; + double sqDistance = nonZeroSign(crossProduct(direction(t), qe))*qe.squaredLength(); + if (fabs(sqDistance) < fabs(minSqDistance)) { + minSqDistance = sqDistance; param = t; } if (step == MSDFGEN_CUBIC_SEARCH_STEPS) @@ -179,18 +178,18 @@ SignedDistance CubicSegment::signedDistance(Point2 origin, double ¶m) const // Improve t Vector2 d1 = 3*as*t*t+6*br*t+3*ab; Vector2 d2 = 6*as*t+6*br; - t -= dotProduct(qpt, d1)/(dotProduct(d1, d1)+dotProduct(qpt, d2)); + t -= dotProduct(qe, d1)/(dotProduct(d1, d1)+dotProduct(qe, d2)); if (t < 0 || t > 1) break; } } if (param >= 0 && param <= 1) - return SignedDistance(minDistance, 0); + return SignedDistance(minSqDistance, 0); if (param < .5) - return SignedDistance(minDistance, fabs(dotProduct(direction(0).normalize(), qa.normalize()))); + return SignedDistance(minSqDistance, fabs(dotProduct(ab.normalize(), qa.normalize()))); else - return SignedDistance(minDistance, fabs(dotProduct(direction(1).normalize(), (p[3]-origin).normalize()))); + return SignedDistance(minSqDistance, fabs(dotProduct((p[3]-p[2]).normalize(), (p[3]-origin).normalize()))); } int LinearSegment::scanlineIntersections(double x[3], int dy[3], double y) const { diff --git a/core/edge-selectors.cpp b/core/edge-selectors.cpp index c1679dd..ab046b4 100644 --- a/core/edge-selectors.cpp +++ b/core/edge-selectors.cpp @@ -17,8 +17,8 @@ void TrueDistanceSelector::merge(const TrueDistanceSelector &other) { minDistance = other.minDistance; } -TrueDistanceSelector::DistanceType TrueDistanceSelector::distance() const { - return minDistance.distance; +TrueDistanceSelector::DistanceType TrueDistanceSelector::squaredDistance() const { + return minDistance.sqDistance; } bool PseudoDistanceSelectorBase::pointFacingEdge(const EdgeSegment *prevEdge, const EdgeSegment *edge, const EdgeSegment *nextEdge, const Point2 &p, double param) { @@ -48,7 +48,7 @@ void PseudoDistanceSelectorBase::addEdgeTrueDistance(const EdgeSegment *edge, co } void PseudoDistanceSelectorBase::addEdgePseudoDistance(const SignedDistance &distance) { - SignedDistance &minPseudoDistance = distance.distance < 0 ? minNegativePseudoDistance : minPositivePseudoDistance; + SignedDistance &minPseudoDistance = distance.sqDistance < 0 ? minNegativePseudoDistance : minPositivePseudoDistance; if (distance < minPseudoDistance) minPseudoDistance = distance; } @@ -65,15 +65,15 @@ void PseudoDistanceSelectorBase::merge(const PseudoDistanceSelectorBase &other) minPositivePseudoDistance = other.minPositivePseudoDistance; } -double PseudoDistanceSelectorBase::computeDistance(const Point2 &p) const { - double minDistance = minTrueDistance.distance < 0 ? minNegativePseudoDistance.distance : minPositivePseudoDistance.distance; +double PseudoDistanceSelectorBase::computeSquaredDistance(const Point2 &p) const { + double sqDistance = minTrueDistance.sqDistance < 0 ? minNegativePseudoDistance.sqDistance : minPositivePseudoDistance.sqDistance; if (nearEdge) { SignedDistance distance = minTrueDistance; nearEdge->distanceToPseudoDistance(distance, p, nearEdgeParam); - if (fabs(distance.distance) < fabs(minDistance)) - minDistance = distance.distance; + if (fabs(distance.sqDistance) < fabs(sqDistance)) + sqDistance = distance.sqDistance; } - return minDistance; + return sqDistance; } PseudoDistanceSelector::PseudoDistanceSelector(const Point2 &p) : p(p) { } @@ -88,8 +88,8 @@ void PseudoDistanceSelector::addEdge(const EdgeSegment *prevEdge, const EdgeSegm } } -PseudoDistanceSelector::DistanceType PseudoDistanceSelector::distance() const { - return computeDistance(p); +PseudoDistanceSelector::DistanceType PseudoDistanceSelector::squaredDistance() const { + return computeSquaredDistance(p); } MultiDistanceSelector::MultiDistanceSelector(const Point2 &p) : p(p) { } @@ -103,7 +103,7 @@ void MultiDistanceSelector::addEdge(const EdgeSegment *prevEdge, const EdgeSegme g.addEdgeTrueDistance(edge, distance, param); if (edge->color&BLUE) b.addEdgeTrueDistance(edge, distance, param); - if (PseudoDistanceSelector::pointFacingEdge(prevEdge, edge, nextEdge, p, param)) { + if (PseudoDistanceSelectorBase::pointFacingEdge(prevEdge, edge, nextEdge, p, param)) { edge->distanceToPseudoDistance(distance, p, param); if (edge->color&RED) r.addEdgePseudoDistance(distance); @@ -120,12 +120,12 @@ void MultiDistanceSelector::merge(const MultiDistanceSelector &other) { b.merge(other.b); } -MultiDistanceSelector::DistanceType MultiDistanceSelector::distance() const { - MultiDistance multiDistance; - multiDistance.r = r.computeDistance(p); - multiDistance.g = g.computeDistance(p); - multiDistance.b = b.computeDistance(p); - return multiDistance; +MultiDistanceSelector::DistanceType MultiDistanceSelector::squaredDistance() const { + MultiDistance sqDistance; + sqDistance.r = r.computeSquaredDistance(p); + sqDistance.g = g.computeSquaredDistance(p); + sqDistance.b = b.computeSquaredDistance(p); + return sqDistance; } } diff --git a/core/edge-selectors.h b/core/edge-selectors.h index b474941..13a3c49 100644 --- a/core/edge-selectors.h +++ b/core/edge-selectors.h @@ -20,7 +20,7 @@ public: explicit TrueDistanceSelector(const Point2 &p = Point2()); void addEdge(const EdgeSegment *prevEdge, const EdgeSegment *edge, const EdgeSegment *nextEdge); void merge(const TrueDistanceSelector &other); - DistanceType distance() const; + DistanceType squaredDistance() const; private: Point2 p; @@ -37,7 +37,7 @@ public: void addEdgeTrueDistance(const EdgeSegment *edge, const SignedDistance &distance, double param); void addEdgePseudoDistance(const SignedDistance &distance); void merge(const PseudoDistanceSelectorBase &other); - double computeDistance(const Point2 &p) const; + double computeSquaredDistance(const Point2 &p) const; private: SignedDistance minTrueDistance; @@ -56,7 +56,7 @@ public: explicit PseudoDistanceSelector(const Point2 &p = Point2()); void addEdge(const EdgeSegment *prevEdge, const EdgeSegment *edge, const EdgeSegment *nextEdge); - DistanceType distance() const; + DistanceType squaredDistance() const; private: Point2 p; @@ -72,7 +72,7 @@ public: explicit MultiDistanceSelector(const Point2 &p = Point2()); void addEdge(const EdgeSegment *prevEdge, const EdgeSegment *edge, const EdgeSegment *nextEdge); void merge(const MultiDistanceSelector &other); - DistanceType distance() const; + DistanceType squaredDistance() const; private: Point2 p; diff --git a/core/msdfgen.cpp b/core/msdfgen.cpp index fcc46c3..2b23dae 100644 --- a/core/msdfgen.cpp +++ b/core/msdfgen.cpp @@ -1,39 +1,38 @@ #include "../msdfgen.h" -#include #include "edge-selectors.h" #include "contour-combiners.h" namespace msdfgen { template -class DistancePixelConversion; +class SquaredDistancePixelConversion; template <> -class DistancePixelConversion { +class SquaredDistancePixelConversion { public: typedef float PixelType; - inline static PixelType convert(double distance, double range) { - return PixelType(distance/range+.5); + inline static PixelType convert(double sqDistance, double range) { + return PixelType(sign(sqDistance)*sqrt(fabs(sqDistance))/range+.5); } }; template <> -class DistancePixelConversion { +class SquaredDistancePixelConversion { public: typedef FloatRGB PixelType; - inline static PixelType convert(const MultiDistance &distance, double range) { + inline static PixelType convert(const MultiDistance &sqDistance, double range) { PixelType pixel; - pixel.r = float(distance.r/range+.5); - pixel.g = float(distance.g/range+.5); - pixel.b = float(distance.b/range+.5); + pixel.r = float(sign(sqDistance.r)*sqrt(fabs(sqDistance.r))/range+.5); + pixel.g = float(sign(sqDistance.g)*sqrt(fabs(sqDistance.g))/range+.5); + pixel.b = float(sign(sqDistance.b)*sqrt(fabs(sqDistance.b))/range+.5); return pixel; } }; template -void generateDistanceField(Bitmap::PixelType> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate) { +void generateDistanceField(Bitmap::PixelType> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate) { int w = output.width(), h = output.height(); #ifdef MSDFGEN_USE_OPENMP @@ -66,12 +65,12 @@ void generateDistanceField(Bitmap::convert(distance, range); + ContourCombiner::DistanceType sqDistance = contourCombiner.squaredDistance(); + output(x, row) = SquaredDistancePixelConversion::convert(sqDistance, range); } } } @@ -179,7 +178,7 @@ void generateSDF_legacy(Bitmap &output, const Shape &shape, double range, if (distance < minDistance) minDistance = distance; } - output(x, row) = float(minDistance.distance/range+.5); + output(x, row) = float(minDistance.distance()/range+.5); } } } @@ -208,7 +207,7 @@ void generatePseudoSDF_legacy(Bitmap &output, const Shape &shape, double } if (nearEdge) (*nearEdge)->distanceToPseudoDistance(minDistance, p, nearParam); - output(x, row) = float(minDistance.distance/range+.5); + output(x, row) = float(minDistance.distance()/range+.5); } } } @@ -258,9 +257,9 @@ void generateMSDF_legacy(Bitmap &output, const Shape &shape, double ra (*g.nearEdge)->distanceToPseudoDistance(g.minDistance, p, g.nearParam); if (b.nearEdge) (*b.nearEdge)->distanceToPseudoDistance(b.minDistance, p, b.nearParam); - output(x, row).r = float(r.minDistance.distance/range+.5); - output(x, row).g = float(g.minDistance.distance/range+.5); - output(x, row).b = float(b.minDistance.distance/range+.5); + output(x, row).r = float(r.minDistance.distance()/range+.5); + output(x, row).g = float(g.minDistance.distance()/range+.5); + output(x, row).b = float(b.minDistance.distance()/range+.5); } } diff --git a/main.cpp b/main.cpp index 7c750dd..60506e9 100644 --- a/main.cpp +++ b/main.cpp @@ -816,7 +816,7 @@ int main(int argc, const char * const *argv) { if (distance < minDistance) minDistance = distance; } - orientation = minDistance.distance <= 0 ? KEEP : REVERSE; + orientation = minDistance.sqDistance <= 0 ? KEEP : REVERSE; } if (orientation == REVERSE) { switch (mode) {