Sfoglia il codice sorgente

Replaced double by configurable real type

Chlumsky 1 anno fa
parent
commit
901b099386

+ 1 - 2
core/BitmapRef.hpp

@@ -2,11 +2,10 @@
 #pragma once
 
 #include <cstddef>
+#include "types.h"
 
 namespace msdfgen {
 
-typedef unsigned char byte;
-
 /// Reference to a 2D image bitmap or a buffer acting as one. Pixel storage not owned or managed by the object.
 template <typename T, int N = 1>
 struct BitmapRef {

+ 10 - 10
core/Contour.cpp

@@ -5,7 +5,7 @@
 
 namespace msdfgen {
 
-static double shoelace(const Point2 &a, const Point2 &b) {
+static real shoelace(const Point2 &a, const Point2 &b) {
     return (b.x-a.x)*(a.y+b.y);
 }
 
@@ -24,29 +24,29 @@ EdgeHolder &Contour::addEdge() {
     return edges.back();
 }
 
-static void boundPoint(double &l, double &b, double &r, double &t, Point2 p) {
+static void boundPoint(real &l, real &b, real &r, real &t, Point2 p) {
     if (p.x < l) l = p.x;
     if (p.y < b) b = p.y;
     if (p.x > r) r = p.x;
     if (p.y > t) t = p.y;
 }
 
-void Contour::bound(double &l, double &b, double &r, double &t) const {
+void Contour::bound(real &l, real &b, real &r, real &t) const {
     for (std::vector<EdgeHolder>::const_iterator edge = edges.begin(); edge != edges.end(); ++edge)
         (*edge)->bound(l, b, r, t);
 }
 
-void Contour::boundMiters(double &l, double &b, double &r, double &t, double border, double miterLimit, int polarity) const {
+void Contour::boundMiters(real &l, real &b, real &r, real &t, real border, real miterLimit, int polarity) const {
     if (edges.empty())
         return;
     Vector2 prevDir = edges.back()->direction(1).normalize(true);
     for (std::vector<EdgeHolder>::const_iterator edge = edges.begin(); edge != edges.end(); ++edge) {
         Vector2 dir = -(*edge)->direction(0).normalize(true);
         if (polarity*crossProduct(prevDir, dir) >= 0) {
-            double miterLength = miterLimit;
-            double q = .5*(1-dotProduct(prevDir, dir));
-            if (q > 0)
-                miterLength = min(1/sqrt(q), miterLimit);
+            real miterLength = miterLimit;
+            real q = real(.5)*(real(1)-dotProduct(prevDir, dir));
+            if (q > real(0))
+                miterLength = min(real(1)/real(sqrt(q)), miterLimit);
             Point2 miter = (*edge)->point(0)+border*miterLength*(prevDir+dir).normalize(true);
             boundPoint(l, b, r, t, miter);
         }
@@ -59,12 +59,12 @@ int Contour::winding() const {
         return 0;
     double total = 0;
     if (edges.size() == 1) {
-        Point2 a = edges[0]->point(0), b = edges[0]->point(1/3.), c = edges[0]->point(2/3.);
+        Point2 a = edges[0]->point(0), b = edges[0]->point(real(1)/real(3)), c = edges[0]->point(real(2)/real(3));
         total += shoelace(a, b);
         total += shoelace(b, c);
         total += shoelace(c, a);
     } else if (edges.size() == 2) {
-        Point2 a = edges[0]->point(0), b = edges[0]->point(.5), c = edges[1]->point(0), d = edges[1]->point(.5);
+        Point2 a = edges[0]->point(0), b = edges[0]->point(real(.5)), c = edges[1]->point(0), d = edges[1]->point(real(.5));
         total += shoelace(a, b);
         total += shoelace(b, c);
         total += shoelace(c, d);

+ 3 - 2
core/Contour.h

@@ -2,6 +2,7 @@
 #pragma once
 
 #include <vector>
+#include "types.h"
 #include "EdgeHolder.h"
 
 namespace msdfgen {
@@ -21,9 +22,9 @@ public:
     /// Creates a new edge in the contour and returns its reference.
     EdgeHolder &addEdge();
     /// Adjusts the bounding box to fit the contour.
-    void bound(double &l, double &b, double &r, double &t) const;
+    void bound(real &l, real &b, real &r, real &t) const;
     /// Adjusts the bounding box to fit the contour border's mitered corners.
-    void boundMiters(double &l, double &b, double &r, double &t, double border, double miterLimit, int polarity) const;
+    void boundMiters(real &l, real &b, real &r, real &t, real border, real miterLimit, int polarity) const;
     /// Computes the winding of the contour. Returns 1 if positive, -1 if negative.
     int winding() const;
     /// Reverses the sequence of edges on the contour.

+ 49 - 49
core/MSDFErrorCorrection.cpp

@@ -13,24 +13,24 @@
 
 namespace msdfgen {
 
-#define ARTIFACT_T_EPSILON .01
-#define PROTECTION_RADIUS_TOLERANCE 1.001
+#define ARTIFACT_T_EPSILON ::msdfgen::real(.01)
+#define PROTECTION_RADIUS_TOLERANCE ::msdfgen::real(1.001)
 
 #define CLASSIFIER_FLAG_CANDIDATE 0x01
 #define CLASSIFIER_FLAG_ARTIFACT 0x02
 
-MSDFGEN_PUBLIC const double ErrorCorrectionConfig::defaultMinDeviationRatio = 1.11111111111111111;
-MSDFGEN_PUBLIC const double ErrorCorrectionConfig::defaultMinImproveRatio = 1.11111111111111111;
+MSDFGEN_PUBLIC const real ErrorCorrectionConfig::defaultMinDeviationRatio = 1.11111111111111111;
+MSDFGEN_PUBLIC const real ErrorCorrectionConfig::defaultMinImproveRatio = 1.11111111111111111;
 
 /// The base artifact classifier recognizes artifacts based on the contents of the SDF alone.
 class BaseArtifactClassifier {
 public:
-    inline BaseArtifactClassifier(double span, bool protectedFlag) : span(span), protectedFlag(protectedFlag) { }
+    inline BaseArtifactClassifier(real span, bool protectedFlag) : span(span), protectedFlag(protectedFlag) { }
     /// Evaluates if the median value xm interpolated at xt in the range between am at at and bm at bt indicates an artifact.
-    inline int rangeTest(double at, double bt, double xt, float am, float bm, float xm) const {
+    inline int rangeTest(real at, real bt, real xt, float am, float bm, float xm) const {
         // For protected texels, only consider inversion artifacts (interpolated median has different sign than boundaries). For the rest, it is sufficient that the interpolated median is outside its boundaries.
         if ((am > .5f && bm > .5f && xm <= .5f) || (am < .5f && bm < .5f && xm >= .5f) || (!protectedFlag && median(am, bm, xm) != xm)) {
-            double axSpan = (xt-at)*span, bxSpan = (bt-xt)*span;
+            real axSpan = (xt-at)*span, bxSpan = (bt-xt)*span;
             // Check if the interpolated median's value is in the expected range based on its distance (span) from boundaries a, b.
             if (!(xm >= am-axSpan && xm <= am+axSpan && xm >= bm-bxSpan && xm <= bm+bxSpan))
                 return CLASSIFIER_FLAG_CANDIDATE|CLASSIFIER_FLAG_ARTIFACT;
@@ -39,11 +39,11 @@ public:
         return 0;
     }
     /// Returns true if the combined results of the tests performed on the median value m interpolated at t indicate an artifact.
-    inline bool evaluate(double t, float m, int flags) const {
-        return (flags&2) != 0;
+    inline bool evaluate(real t, float m, int flags) const {
+        return (flags&CLASSIFIER_FLAG_ARTIFACT) != 0;
     }
 private:
-    double span;
+    real span;
     bool protectedFlag;
 };
 
@@ -53,9 +53,9 @@ class ShapeDistanceChecker {
 public:
     class ArtifactClassifier : public BaseArtifactClassifier {
     public:
-        inline ArtifactClassifier(ShapeDistanceChecker *parent, const Vector2 &direction, double span) : BaseArtifactClassifier(span, parent->protectedFlag), parent(parent), direction(direction) { }
+        inline ArtifactClassifier(ShapeDistanceChecker *parent, const Vector2 &direction, real span) : BaseArtifactClassifier(span, parent->protectedFlag), parent(parent), direction(direction) { }
         /// Returns true if the combined results of the tests performed on the median value m interpolated at t indicate an artifact.
-        inline bool evaluate(double t, float m, int flags) const {
+        inline bool evaluate(real t, float m, int flags) const {
             if (flags&CLASSIFIER_FLAG_CANDIDATE) {
                 // Skip expensive distance evaluation if the point has already been classified as an artifact by the base classifier.
                 if (flags&CLASSIFIER_FLAG_ARTIFACT)
@@ -66,7 +66,7 @@ public:
                 Point2 sdfCoord = parent->sdfCoord+tVector;
                 interpolate(oldMSD, parent->sdf, sdfCoord);
                 // Compute the color that would be interpolated at the artifact candidate's position if error correction was applied on the current texel.
-                double aWeight = (1-fabs(tVector.x))*(1-fabs(tVector.y));
+                real aWeight = (real(1)-fabs(tVector.x))*(real(1)-fabs(tVector.y));
                 float aPSD = median(parent->msd[0], parent->msd[1], parent->msd[2]);
                 newMSD[0] = float(oldMSD[0]+aWeight*(aPSD-parent->msd[0]));
                 newMSD[1] = float(oldMSD[1]+aWeight*(aPSD-parent->msd[1]));
@@ -74,9 +74,9 @@ public:
                 // Compute the evaluated distance (interpolated median) before and after error correction, as well as the exact shape distance.
                 float oldPSD = median(oldMSD[0], oldMSD[1], oldMSD[2]);
                 float newPSD = median(newMSD[0], newMSD[1], newMSD[2]);
-                float refPSD = float(parent->invRange*parent->distanceFinder.distance(parent->shapeCoord+tVector*parent->texelSize)+.5);
+                float refPSD = float(parent->invRange*parent->distanceFinder.distance(parent->shapeCoord+tVector*parent->texelSize)+real(.5));
                 // Compare the differences of the exact distance and the before and after distances.
-                return parent->minImproveRatio*fabsf(newPSD-refPSD) < double(fabsf(oldPSD-refPSD));
+                return parent->minImproveRatio*fabsf(newPSD-refPSD) < real(fabsf(oldPSD-refPSD));
             }
             return false;
         }
@@ -87,34 +87,34 @@ public:
     Point2 shapeCoord, sdfCoord;
     const float *msd;
     bool protectedFlag;
-    inline ShapeDistanceChecker(const BitmapConstRef<float, N> &sdf, const Shape &shape, const Projection &projection, double invRange, double minImproveRatio) : distanceFinder(shape), sdf(sdf), invRange(invRange), minImproveRatio(minImproveRatio) {
+    inline ShapeDistanceChecker(const BitmapConstRef<float, N> &sdf, const Shape &shape, const Projection &projection, real invRange, real minImproveRatio) : distanceFinder(shape), sdf(sdf), invRange(invRange), minImproveRatio(minImproveRatio) {
         texelSize = projection.unprojectVector(Vector2(1));
     }
-    inline ArtifactClassifier classifier(const Vector2 &direction, double span) {
+    inline ArtifactClassifier classifier(const Vector2 &direction, real span) {
         return ArtifactClassifier(this, direction, span);
     }
 private:
     ShapeDistanceFinder<ContourCombiner<PseudoDistanceSelector> > distanceFinder;
     BitmapConstRef<float, N> sdf;
-    double invRange;
+    real invRange;
     Vector2 texelSize;
-    double minImproveRatio;
+    real minImproveRatio;
 };
 
 MSDFErrorCorrection::MSDFErrorCorrection() { }
 
-MSDFErrorCorrection::MSDFErrorCorrection(const BitmapRef<byte, 1> &stencil, const Projection &projection, double range) : stencil(stencil), projection(projection) {
-    invRange = 1/range;
+MSDFErrorCorrection::MSDFErrorCorrection(const BitmapRef<byte, 1> &stencil, const Projection &projection, real range) : stencil(stencil), projection(projection) {
+    invRange = real(1)/range;
     minDeviationRatio = ErrorCorrectionConfig::defaultMinDeviationRatio;
     minImproveRatio = ErrorCorrectionConfig::defaultMinImproveRatio;
     memset(stencil.pixels, 0, sizeof(byte)*stencil.width*stencil.height);
 }
 
-void MSDFErrorCorrection::setMinDeviationRatio(double minDeviationRatio) {
+void MSDFErrorCorrection::setMinDeviationRatio(real minDeviationRatio) {
     this->minDeviationRatio = minDeviationRatio;
 }
 
-void MSDFErrorCorrection::setMinImproveRatio(double minImproveRatio) {
+void MSDFErrorCorrection::setMinImproveRatio(real minImproveRatio) {
     this->minImproveRatio = minImproveRatio;
 }
 
@@ -130,8 +130,8 @@ void MSDFErrorCorrection::protectCorners(const Shape &shape) {
                     Point2 p = projection.project((*edge)->point(0));
                     if (shape.inverseYAxis)
                         p.y = stencil.height-p.y;
-                    int l = (int) floor(p.x-.5);
-                    int b = (int) floor(p.y-.5);
+                    int l = (int) floor(p.x-real(.5));
+                    int b = (int) floor(p.y-real(.5));
                     int r = l+1;
                     int t = b+1;
                     // Check that the positions are within bounds.
@@ -154,7 +154,7 @@ void MSDFErrorCorrection::protectCorners(const Shape &shape) {
 /// Determines if the channel contributes to an edge between the two texels a, b.
 static bool edgeBetweenTexelsChannel(const float *a, const float *b, int channel) {
     // Find interpolation ratio t (0 < t < 1) where an edge is expected (mix(a[channel], b[channel], t) == 0.5).
-    double t = (a[channel]-.5)/(a[channel]-b[channel]);
+    real t = (a[channel]-real(.5))/(a[channel]-b[channel]);
     if (t > 0 && t < 1) {
         // Interpolate channel values at t.
         float c[3] = {
@@ -256,7 +256,7 @@ void MSDFErrorCorrection::protectAll() {
 }
 
 /// Returns the median of the linear interpolation of texels a, b at t.
-static float interpolatedMedian(const float *a, const float *b, double t) {
+static float interpolatedMedian(const float *a, const float *b, real t) {
     return median(
         mix(a[0], b[0], t),
         mix(a[1], b[1], t),
@@ -264,7 +264,7 @@ static float interpolatedMedian(const float *a, const float *b, double t) {
     );
 }
 /// Returns the median of the bilinear interpolation with the given constant, linear, and quadratic terms at t.
-static float interpolatedMedian(const float *a, const float *l, const float *q, double t) {
+static float interpolatedMedian(const float *a, const float *l, const float *q, real t) {
     return float(median(
         t*(t*q[0]+l[0])+a[0],
         t*(t*q[1]+l[1])+a[1],
@@ -273,7 +273,7 @@ static float interpolatedMedian(const float *a, const float *l, const float *q,
 }
 
 /// Determines if the interpolated median xm is an artifact.
-static bool isArtifact(bool isProtected, double axSpan, double bxSpan, float am, float bm, float xm) {
+static bool isArtifact(bool isProtected, real axSpan, real bxSpan, float am, float bm, float xm) {
     return (
         // For protected texels, only report an artifact if it would cause fill inversion (change between positive and negative distance).
         (!isProtected || (am > .5f && bm > .5f && xm <= .5f) || (am < .5f && bm < .5f && xm >= .5f)) &&
@@ -286,8 +286,8 @@ static bool isArtifact(bool isProtected, double axSpan, double bxSpan, float am,
 template <class ArtifactClassifier>
 static bool hasLinearArtifactInner(const ArtifactClassifier &artifactClassifier, float am, float bm, const float *a, const float *b, float dA, float dB) {
     // Find interpolation ratio t (0 < t < 1) where two color channels are equal (mix(dA, dB, t) == 0).
-    double t = (double) dA/(dA-dB);
-    if (t > ARTIFACT_T_EPSILON && t < 1-ARTIFACT_T_EPSILON) {
+    real t = (real) dA/(dA-dB);
+    if (t > ARTIFACT_T_EPSILON && t < real(1)-ARTIFACT_T_EPSILON) {
         // Interpolate median at t and let the classifier decide if its value indicates an artifact.
         float xm = interpolatedMedian(a, b, t);
         return artifactClassifier.evaluate(t, xm, artifactClassifier.rangeTest(0, 1, t, am, bm, xm));
@@ -297,22 +297,22 @@ static bool hasLinearArtifactInner(const ArtifactClassifier &artifactClassifier,
 
 /// Checks if a bilinear interpolation artifact will occur at a point where two specific color channels are equal - such points have extreme median values.
 template <class ArtifactClassifier>
-static bool hasDiagonalArtifactInner(const ArtifactClassifier &artifactClassifier, float am, float dm, const float *a, const float *l, const float *q, float dA, float dBC, float dD, double tEx0, double tEx1) {
+static bool hasDiagonalArtifactInner(const ArtifactClassifier &artifactClassifier, float am, float dm, const float *a, const float *l, const float *q, float dA, float dBC, float dD, real tEx0, real tEx1) {
     // Find interpolation ratios t (0 < t[i] < 1) where two color channels are equal.
-    double t[2];
+    real t[2];
     int solutions = solveQuadratic(t, dD-dBC+dA, dBC-dA-dA, dA);
     for (int i = 0; i < solutions; ++i) {
         // Solutions t[i] == 0 and t[i] == 1 are singularities and occur very often because two channels are usually equal at texels.
-        if (t[i] > ARTIFACT_T_EPSILON && t[i] < 1-ARTIFACT_T_EPSILON) {
+        if (t[i] > ARTIFACT_T_EPSILON && t[i] < real(1)-ARTIFACT_T_EPSILON) {
             // Interpolate median xm at t.
             float xm = interpolatedMedian(a, l, q, t[i]);
             // Determine if xm deviates too much from medians of a, d.
             int rangeFlags = artifactClassifier.rangeTest(0, 1, t[i], am, dm, xm);
             // Additionally, check xm against the interpolated medians at the local extremes tEx0, tEx1.
-            double tEnd[2];
+            real tEnd[2];
             float em[2];
             // tEx0
-            if (tEx0 > 0 && tEx0 < 1) {
+            if (tEx0 > real(0) && tEx0 < real(1)) {
                 tEnd[0] = 0, tEnd[1] = 1;
                 em[0] = am, em[1] = dm;
                 tEnd[tEx0 > t[i]] = tEx0;
@@ -320,7 +320,7 @@ static bool hasDiagonalArtifactInner(const ArtifactClassifier &artifactClassifie
                 rangeFlags |= artifactClassifier.rangeTest(tEnd[0], tEnd[1], t[i], em[0], em[1], xm);
             }
             // tEx1
-            if (tEx1 > 0 && tEx1 < 1) {
+            if (tEx1 > real(0) && tEx1 < real(1)) {
                 tEnd[0] = 0, tEnd[1] = 1;
                 em[0] = am, em[1] = dm;
                 tEnd[tEx1 > t[i]] = tEx1;
@@ -373,10 +373,10 @@ static bool hasDiagonalArtifact(const ArtifactClassifier &artifactClassifier, fl
             d[2]+abc[2]
         };
         // Compute interpolation ratios tEx (0 < tEx[i] < 1) for the local extremes of each color channel (the derivative 2*q[i]*tEx[i]+l[i] == 0).
-        double tEx[3] = {
-            -.5*l[0]/q[0],
-            -.5*l[1]/q[1],
-            -.5*l[2]/q[2]
+        real tEx[3] = {
+            real(-.5)*l[0]/q[0],
+            real(-.5)*l[1]/q[1],
+            real(-.5)*l[2]/q[2]
         };
         // Check points where each pair of color channels meets.
         return (
@@ -391,9 +391,9 @@ static bool hasDiagonalArtifact(const ArtifactClassifier &artifactClassifier, fl
 template <int N>
 void MSDFErrorCorrection::findErrors(const BitmapConstRef<float, N> &sdf) {
     // Compute the expected deltas between values of horizontally, vertically, and diagonally adjacent texels.
-    double hSpan = minDeviationRatio*projection.unprojectVector(Vector2(invRange, 0)).length();
-    double vSpan = minDeviationRatio*projection.unprojectVector(Vector2(0, invRange)).length();
-    double dSpan = minDeviationRatio*projection.unprojectVector(Vector2(invRange)).length();
+    real hSpan = minDeviationRatio*projection.unprojectVector(Vector2(invRange, 0)).length();
+    real vSpan = minDeviationRatio*projection.unprojectVector(Vector2(0, invRange)).length();
+    real dSpan = minDeviationRatio*projection.unprojectVector(Vector2(invRange)).length();
     // Inspect all texels.
     for (int y = 0; y < sdf.height; ++y) {
         for (int x = 0; x < sdf.width; ++x) {
@@ -419,9 +419,9 @@ void MSDFErrorCorrection::findErrors(const BitmapConstRef<float, N> &sdf) {
 template <template <typename> class ContourCombiner, int N>
 void MSDFErrorCorrection::findErrors(const BitmapConstRef<float, N> &sdf, const Shape &shape) {
     // Compute the expected deltas between values of horizontally, vertically, and diagonally adjacent texels.
-    double hSpan = minDeviationRatio*projection.unprojectVector(Vector2(invRange, 0)).length();
-    double vSpan = minDeviationRatio*projection.unprojectVector(Vector2(0, invRange)).length();
-    double dSpan = minDeviationRatio*projection.unprojectVector(Vector2(invRange)).length();
+    real hSpan = minDeviationRatio*projection.unprojectVector(Vector2(invRange, 0)).length();
+    real vSpan = minDeviationRatio*projection.unprojectVector(Vector2(0, invRange)).length();
+    real dSpan = minDeviationRatio*projection.unprojectVector(Vector2(invRange)).length();
 #ifdef MSDFGEN_USE_OPENMP
     #pragma omp parallel
 #endif
@@ -439,8 +439,8 @@ void MSDFErrorCorrection::findErrors(const BitmapConstRef<float, N> &sdf, const
                 if ((*stencil(x, row)&ERROR))
                     continue;
                 const float *c = sdf(x, row);
-                shapeDistanceChecker.shapeCoord = projection.unproject(Point2(x+.5, y+.5));
-                shapeDistanceChecker.sdfCoord = Point2(x+.5, row+.5);
+                shapeDistanceChecker.shapeCoord = projection.unproject(Point2(real(x)+real(.5), real(y)+real(.5)));
+                shapeDistanceChecker.sdfCoord = Point2(real(x)+real(.5), real(row)+real(.5));
                 shapeDistanceChecker.msd = c;
                 shapeDistanceChecker.protectedFlag = (*stencil(x, row)&PROTECTED) != 0;
                 float cm = median(c[0], c[1], c[2]);

+ 7 - 6
core/MSDFErrorCorrection.h

@@ -1,6 +1,7 @@
 
 #pragma once
 
+#include "types.h"
 #include "Projection.h"
 #include "Shape.h"
 #include "BitmapRef.hpp"
@@ -20,11 +21,11 @@ public:
     };
 
     MSDFErrorCorrection();
-    explicit MSDFErrorCorrection(const BitmapRef<byte, 1> &stencil, const Projection &projection, double range);
+    explicit MSDFErrorCorrection(const BitmapRef<byte, 1> &stencil, const Projection &projection, real range);
     /// Sets the minimum ratio between the actual and maximum expected distance delta to be considered an error.
-    void setMinDeviationRatio(double minDeviationRatio);
+    void setMinDeviationRatio(real minDeviationRatio);
     /// Sets the minimum ratio between the pre-correction distance error and the post-correction distance error.
-    void setMinImproveRatio(double minImproveRatio);
+    void setMinImproveRatio(real minImproveRatio);
     /// Flags all texels that are interpolated at corners as protected.
     void protectCorners(const Shape &shape);
     /// Flags all texels that contribute to edges as protected.
@@ -47,9 +48,9 @@ public:
 private:
     BitmapRef<byte, 1> stencil;
     Projection projection;
-    double invRange;
-    double minDeviationRatio;
-    double minImproveRatio;
+    real invRange;
+    real minDeviationRatio;
+    real minImproveRatio;
 
 };
 

+ 4 - 4
core/Projection.cpp

@@ -23,19 +23,19 @@ Vector2 Projection::unprojectVector(const Vector2 &vector) const {
     return vector/scale;
 }
 
-double Projection::projectX(double x) const {
+real Projection::projectX(real x) const {
     return scale.x*(x+translate.x);
 }
 
-double Projection::projectY(double y) const {
+real Projection::projectY(real y) const {
     return scale.y*(y+translate.y);
 }
 
-double Projection::unprojectX(double x) const {
+real Projection::unprojectX(real x) const {
     return x/scale.x-translate.x;
 }
 
-double Projection::unprojectY(double y) const {
+real Projection::unprojectY(real y) const {
     return y/scale.y-translate.y;
 }
 

+ 5 - 4
core/Projection.h

@@ -1,6 +1,7 @@
 
 #pragma once
 
+#include "types.h"
 #include "Vector2.hpp"
 
 namespace msdfgen {
@@ -20,13 +21,13 @@ public:
     /// Converts the vector from pixel coordinate space.
     Vector2 unprojectVector(const Vector2 &vector) const;
     /// Converts the X-coordinate from shape to pixel coordinate space.
-    double projectX(double x) const;
+    real projectX(real x) const;
     /// Converts the Y-coordinate from shape to pixel coordinate space.
-    double projectY(double y) const;
+    real projectY(real y) const;
     /// Converts the X-coordinate from pixel to shape coordinate space.
-    double unprojectX(double x) const;
+    real unprojectX(real x) const;
     /// Converts the Y-coordinate from pixel to shape coordinate space.
-    double unprojectY(double y) const;
+    real unprojectY(real y) const;
 
 private:
     Vector2 scale;

+ 11 - 11
core/Scanline.cpp

@@ -24,14 +24,14 @@ bool interpretFillRule(int intersections, FillRule fillRule) {
     return false;
 }
 
-double Scanline::overlap(const Scanline &a, const Scanline &b, double xFrom, double xTo, FillRule fillRule) {
-    double total = 0;
+real Scanline::overlap(const Scanline &a, const Scanline &b, real xFrom, real xTo, FillRule fillRule) {
+    real total = 0;
     bool aInside = false, bInside = false;
     int ai = 0, bi = 0;
-    double ax = !a.intersections.empty() ? a.intersections[ai].x : xTo;
-    double bx = !b.intersections.empty() ? b.intersections[bi].x : xTo;
+    real ax = !a.intersections.empty() ? a.intersections[ai].x : xTo;
+    real bx = !b.intersections.empty() ? b.intersections[bi].x : xTo;
     while (ax < xFrom || bx < xFrom) {
-        double xNext = min(ax, bx);
+        real xNext = min(ax, bx);
         if (ax == xNext && ai < (int) a.intersections.size()) {
             aInside = interpretFillRule(a.intersections[ai].direction, fillRule);
             ax = ++ai < (int) a.intersections.size() ? a.intersections[ai].x : xTo;
@@ -41,9 +41,9 @@ double Scanline::overlap(const Scanline &a, const Scanline &b, double xFrom, dou
             bx = ++bi < (int) b.intersections.size() ? b.intersections[bi].x : xTo;
         }
     }
-    double x = xFrom;
+    real x = xFrom;
     while (ax < xTo || bx < xTo) {
-        double xNext = min(ax, bx);
+        real xNext = min(ax, bx);
         if (aInside == bInside)
             total += xNext-x;
         if (ax == xNext && ai < (int) a.intersections.size()) {
@@ -87,7 +87,7 @@ void Scanline::setIntersections(std::vector<Intersection> &&intersections) {
 }
 #endif
 
-int Scanline::moveTo(double x) const {
+int Scanline::moveTo(real x) const {
     if (intersections.empty())
         return -1;
     int index = lastIndex;
@@ -107,18 +107,18 @@ int Scanline::moveTo(double x) const {
     return index;
 }
 
-int Scanline::countIntersections(double x) const {
+int Scanline::countIntersections(real x) const {
     return moveTo(x)+1;
 }
 
-int Scanline::sumIntersections(double x) const {
+int Scanline::sumIntersections(real x) const {
     int index = moveTo(x);
     if (index >= 0)
         return intersections[index].direction;
     return 0;
 }
 
-bool Scanline::filled(double x, FillRule fillRule) const {
+bool Scanline::filled(real x, FillRule fillRule) const {
     return interpretFillRule(sumIntersections(x), fillRule);
 }
 

+ 7 - 6
core/Scanline.h

@@ -2,6 +2,7 @@
 #pragma once
 
 #include <vector>
+#include "types.h"
 
 namespace msdfgen {
 
@@ -23,12 +24,12 @@ public:
     /// An intersection with the scanline.
     struct Intersection {
         /// X coordinate.
-        double x;
+        real x;
         /// Normalized Y direction of the oriented edge at the point of intersection.
         int direction;
     };
 
-    static double overlap(const Scanline &a, const Scanline &b, double xFrom, double xTo, FillRule fillRule);
+    static real overlap(const Scanline &a, const Scanline &b, real xFrom, real xTo, FillRule fillRule);
 
     Scanline();
     /// Populates the intersection list.
@@ -37,18 +38,18 @@ public:
     void setIntersections(std::vector<Intersection> &&intersections);
 #endif
     /// Returns the number of intersections left of x.
-    int countIntersections(double x) const;
+    int countIntersections(real x) const;
     /// Returns the total sign of intersections left of x.
-    int sumIntersections(double x) const;
+    int sumIntersections(real x) const;
     /// Decides whether the scanline is filled at x based on fill rule.
-    bool filled(double x, FillRule fillRule) const;
+    bool filled(real x, FillRule fillRule) const;
 
 private:
     std::vector<Intersection> intersections;
     mutable int lastIndex;
 
     void preprocess();
-    int moveTo(double x) const;
+    int moveTo(real x) const;
 
 };
 

+ 16 - 16
core/Shape.cpp

@@ -2,6 +2,7 @@
 #include "Shape.h"
 
 #include <cstdlib>
+#include <cfloat>
 #include "arithmetics.hpp"
 
 namespace msdfgen {
@@ -63,7 +64,7 @@ void Shape::normalize() {
             for (std::vector<EdgeHolder>::iterator edge = contour->edges.begin(); edge != contour->edges.end(); ++edge) {
                 Vector2 prevDir = (*prevEdge)->direction(1).normalize();
                 Vector2 curDir = (*edge)->direction(0).normalize();
-                if (dotProduct(prevDir, curDir) < MSDFGEN_CORNER_DOT_EPSILON-1) {
+                if (dotProduct(prevDir, curDir) < MSDFGEN_CORNER_DOT_EPSILON-real(1)) {
                     deconvergeEdge(*prevEdge, 1);
                     deconvergeEdge(*edge, 0);
                 }
@@ -73,32 +74,31 @@ void Shape::normalize() {
     }
 }
 
-void Shape::bound(double &l, double &b, double &r, double &t) const {
+void Shape::bound(real &l, real &b, real &r, real &t) const {
     for (std::vector<Contour>::const_iterator contour = contours.begin(); contour != contours.end(); ++contour)
         contour->bound(l, b, r, t);
 }
 
-void Shape::boundMiters(double &l, double &b, double &r, double &t, double border, double miterLimit, int polarity) const {
+void Shape::boundMiters(real &l, real &b, real &r, real &t, real border, real miterLimit, int polarity) const {
     for (std::vector<Contour>::const_iterator contour = contours.begin(); contour != contours.end(); ++contour)
         contour->boundMiters(l, b, r, t, border, miterLimit, polarity);
 }
 
-Shape::Bounds Shape::getBounds(double border, double miterLimit, int polarity) const {
-    static const double LARGE_VALUE = 1e240;
-    Shape::Bounds bounds = { +LARGE_VALUE, +LARGE_VALUE, -LARGE_VALUE, -LARGE_VALUE };
+Shape::Bounds Shape::getBounds(real border, real miterLimit, int polarity) const {
+    Shape::Bounds bounds = { +FLT_MAX, +FLT_MAX, -FLT_MAX, -FLT_MAX };
     bound(bounds.l, bounds.b, bounds.r, bounds.t);
-    if (border > 0) {
+    if (border > real(0)) {
         bounds.l -= border, bounds.b -= border;
         bounds.r += border, bounds.t += border;
-        if (miterLimit > 0)
+        if (miterLimit > real(0))
             boundMiters(bounds.l, bounds.b, bounds.r, bounds.t, border, miterLimit, polarity);
     }
     return bounds;
 }
 
-void Shape::scanline(Scanline &line, double y) const {
+void Shape::scanline(Scanline &line, real y) const {
     std::vector<Scanline::Intersection> intersections;
-    double x[3];
+    real x[3];
     int dy[3];
     for (std::vector<Contour>::const_iterator contour = contours.begin(); contour != contours.end(); ++contour) {
         for (std::vector<EdgeHolder>::const_iterator edge = contour->edges.begin(); edge != contour->edges.end(); ++edge) {
@@ -125,7 +125,7 @@ int Shape::edgeCount() const {
 
 void Shape::orientContours() {
     struct Intersection {
-        double x;
+        real x;
         int direction;
         int contourIndex;
 
@@ -134,21 +134,21 @@ void Shape::orientContours() {
         }
     };
 
-    const double ratio = .5*(sqrt(5)-1); // an irrational number to minimize chance of intersecting a corner or other point of interest
+    const real ratio = real(.5)*(sqrt(5)-real(1)); // an irrational number to minimize chance of intersecting a corner or other point of interest
     std::vector<int> orientations(contours.size());
     std::vector<Intersection> intersections;
     for (int i = 0; i < (int) contours.size(); ++i) {
         if (!orientations[i] && !contours[i].edges.empty()) {
             // Find an Y that crosses the contour
-            double y0 = contours[i].edges.front()->point(0).y;
-            double y1 = y0;
+            real y0 = contours[i].edges.front()->point(0).y;
+            real y1 = y0;
             for (std::vector<EdgeHolder>::const_iterator edge = contours[i].edges.begin(); edge != contours[i].edges.end() && y0 == y1; ++edge)
                 y1 = (*edge)->point(1).y;
             for (std::vector<EdgeHolder>::const_iterator edge = contours[i].edges.begin(); edge != contours[i].edges.end() && y0 == y1; ++edge)
                 y1 = (*edge)->point(ratio).y; // in case all endpoints are in a horizontal line
-            double y = mix(y0, y1, ratio);
+            real y = mix(y0, y1, ratio);
             // Scanline through whole shape at Y
-            double x[3];
+            real x[3];
             int dy[3];
             for (int j = 0; j < (int) contours.size(); ++j) {
                 for (std::vector<EdgeHolder>::const_iterator edge = contours[j].edges.begin(); edge != contours[j].edges.end(); ++edge) {

+ 8 - 7
core/Shape.h

@@ -2,22 +2,23 @@
 #pragma once
 
 #include <vector>
+#include "types.h"
 #include "Contour.h"
 #include "Scanline.h"
 
 namespace msdfgen {
 
 // Threshold of the dot product of adjacent edge directions to be considered convergent.
-#define MSDFGEN_CORNER_DOT_EPSILON .000001
+#define MSDFGEN_CORNER_DOT_EPSILON ::msdfgen::real(.000001)
 // The proportional amount by which a curve's control point will be adjusted to eliminate convergent corners.
-#define MSDFGEN_DECONVERGENCE_FACTOR .000001
+#define MSDFGEN_DECONVERGENCE_FACTOR ::msdfgen::real(.000001)
 
 /// Vector shape representation.
 class Shape {
 
 public:
     struct Bounds {
-        double l, b, r, t;
+        real l, b, r, t;
     };
 
     /// The list of contours the shape consists of.
@@ -38,13 +39,13 @@ public:
     /// Performs basic checks to determine if the object represents a valid shape.
     bool validate() const;
     /// Adjusts the bounding box to fit the shape.
-    void bound(double &l, double &b, double &r, double &t) const;
+    void bound(real &l, real &b, real &r, real &t) const;
     /// Adjusts the bounding box to fit the shape border's mitered corners.
-    void boundMiters(double &l, double &b, double &r, double &t, double border, double miterLimit, int polarity) const;
+    void boundMiters(real &l, real &b, real &r, real &t, real border, real miterLimit, int polarity) const;
     /// Computes the minimum bounding box that fits the shape, optionally with a (mitered) border.
-    Bounds getBounds(double border = 0, double miterLimit = 0, int polarity = 0) const;
+    Bounds getBounds(real border = 0, real miterLimit = 0, int polarity = 0) const;
     /// Outputs the scanline that intersects the shape at y.
-    void scanline(Scanline &line, double y) const;
+    void scanline(Scanline &line, real y) const;
     /// Returns the total number of edge segments
     int edgeCount() const;
     /// Assumes its contours are unoriented (even-odd fill rule). Attempts to orient them to conform to the non-zero winding rule.

+ 4 - 4
core/SignedDistance.hpp

@@ -10,11 +10,11 @@ namespace msdfgen {
 class SignedDistance {
 
 public:
-    double distance;
-    double dot;
+    real distance;
+    real dot;
 
-    inline SignedDistance() : distance(-DBL_MAX), dot(0) { }
-    inline SignedDistance(double dist, double d) : distance(dist), dot(d) { }
+    inline SignedDistance() : distance(-FLT_MAX), dot(0) { }
+    inline SignedDistance(real dist, real d) : distance(dist), dot(d) { }
 
 };
 

+ 17 - 16
core/Vector2.hpp

@@ -3,6 +3,7 @@
 
 #include <cstddef>
 #include <cmath>
+#include "types.h"
 
 namespace msdfgen {
 
@@ -12,11 +13,11 @@ namespace msdfgen {
  */
 struct Vector2 {
 
-    double x, y;
+    real x, y;
 
-    inline Vector2(double val = 0) : x(val), y(val) { }
+    inline Vector2(real val = 0) : x(val), y(val) { }
 
-    inline Vector2(double x, double y) : x(x), y(y) { }
+    inline Vector2(real x, real y) : x(x), y(y) { }
 
     /// Sets the vector to zero.
     inline void reset() {
@@ -24,23 +25,23 @@ struct Vector2 {
     }
 
     /// Sets individual elements of the vector.
-    inline void set(double x, double y) {
+    inline void set(real x, real y) {
         this->x = x, this->y = y;
     }
 
     /// Returns the vector's squared length.
-    inline double squaredLength() const {
+    inline real squaredLength() const {
         return x*x+y*y;
     }
 
     /// Returns the vector's length.
-    inline double length() const {
+    inline real length() const {
         return sqrt(x*x+y*y);
     }
 
     /// Returns the normalized vector - one that has the same direction but unit length.
     inline Vector2 normalize(bool allowZero = false) const {
-        if (double len = length())
+        if (real len = length())
             return Vector2(x/len, y/len);
         return Vector2(0, !allowZero);
     }
@@ -52,7 +53,7 @@ struct Vector2 {
 
     /// Returns a vector with unit length that is orthogonal to this one.
     inline Vector2 getOrthonormal(bool polarity = true, bool allowZero = false) const {
-        if (double len = length())
+        if (real len = length())
             return polarity ? Vector2(-y/len, x/len) : Vector2(y/len, -x/len);
         return polarity ? Vector2(0, !allowZero) : Vector2(0, -!allowZero);
     }
@@ -87,12 +88,12 @@ struct Vector2 {
         return *this;
     }
 
-    inline Vector2 &operator*=(double value) {
+    inline Vector2 &operator*=(real value) {
         x *= value, y *= value;
         return *this;
     }
 
-    inline Vector2 &operator/=(double value) {
+    inline Vector2 &operator/=(real value) {
         x /= value, y /= value;
         return *this;
     }
@@ -103,12 +104,12 @@ struct Vector2 {
 typedef Vector2 Point2;
 
 /// Dot product of two vectors.
-inline double dotProduct(const Vector2 a, const Vector2 b) {
+inline real dotProduct(const Vector2 a, const Vector2 b) {
     return a.x*b.x+a.y*b.y;
 }
 
 /// A special version of the cross product for 2D vectors (returns scalar value).
-inline double crossProduct(const Vector2 a, const Vector2 b) {
+inline real crossProduct(const Vector2 a, const Vector2 b) {
     return a.x*b.y-a.y*b.x;
 }
 
@@ -148,19 +149,19 @@ inline Vector2 operator/(const Vector2 a, const Vector2 b) {
     return Vector2(a.x/b.x, a.y/b.y);
 }
 
-inline Vector2 operator*(double a, const Vector2 b) {
+inline Vector2 operator*(real a, const Vector2 b) {
     return Vector2(a*b.x, a*b.y);
 }
 
-inline Vector2 operator/(double a, const Vector2 b) {
+inline Vector2 operator/(real a, const Vector2 b) {
     return Vector2(a/b.x, a/b.y);
 }
 
-inline Vector2 operator*(const Vector2 a, double b) {
+inline Vector2 operator*(const Vector2 a, real b) {
     return Vector2(a.x*b, a.y*b);
 }
 
-inline Vector2 operator/(const Vector2 a, double b) {
+inline Vector2 operator/(const Vector2 a, real b) {
     return Vector2(a.x/b, a.y/b);
 }
 

+ 34 - 33
core/bezier-solver.hpp

@@ -2,16 +2,17 @@
 #pragma once
 
 #include <cmath>
+#include "types.h"
 #include "Vector2.hpp"
 
 // Parameters for iterative search of closest point on a cubic Bezier curve. Increase for higher precision.
 #define MSDFGEN_CUBIC_SEARCH_STARTS 4
 #define MSDFGEN_CUBIC_SEARCH_STEPS 4
 
-#define MSDFGEN_QUADRATIC_RATIO_LIMIT 1e8
+#define MSDFGEN_QUADRATIC_RATIO_LIMIT ::msdfgen::real(1e8)
 
 #ifndef MSDFGEN_CUBE_ROOT
-#define MSDFGEN_CUBE_ROOT(x) pow((x), 1/3.)
+#define MSDFGEN_CUBE_ROOT(x) pow((x), ::msdfgen::real(1)/::msdfgen::real(3))
 #endif
 
 namespace msdfgen {
@@ -22,37 +23,37 @@ namespace msdfgen {
  * q = 2*P1-2*P0
  * r = P2-2*P1+P0
  */
-inline double quadraticNearPoint(const Vector2 p, const Vector2 q, const Vector2 r) {
-    double qq = q.squaredLength();
-    double rr = r.squaredLength();
+inline real quadraticNearPoint(const Vector2 p, const Vector2 q, const Vector2 r) {
+    real qq = q.squaredLength();
+    real rr = r.squaredLength();
     if (qq >= MSDFGEN_QUADRATIC_RATIO_LIMIT*rr)
         return dotProduct(p, q)/qq;
-    double norm = .5/rr;
-    double a = 3*norm*dotProduct(q, r);
-    double b = norm*(qq-2*dotProduct(p, r));
-    double c = norm*dotProduct(p, q);
-    double aa = a*a;
-    double g = 1/9.*(aa-3*b);
-    double h = 1/54.*(a*(aa+aa-9*b)-27*c);
-    double hh = h*h;
-    double ggg = g*g*g;
-    a *= 1/3.;
+    real norm = real(.5)/rr;
+    real a = real(3)*norm*dotProduct(q, r);
+    real b = norm*(qq-real(2)*dotProduct(p, r));
+    real c = norm*dotProduct(p, q);
+    real aa = a*a;
+    real g = real(1)/real(9)*(aa-real(3)*b);
+    real h = real(1)/real(54)*(a*(aa+aa-real(9)*b)-real(27)*c);
+    real hh = h*h;
+    real ggg = g*g*g;
+    a *= real(1)/real(3);
     if (hh < ggg) {
-        double u = 1/3.*acos(h/sqrt(ggg));
-        g = -2*sqrt(g);
-        if (h >= 0) {
-            double t = g*cos(u)-a;
-            if (t >= 0)
+        real u = real(1)/real(3)*acos(h/sqrt(ggg));
+        g = real(-2)*sqrt(g);
+        if (h >= real(0)) {
+            real t = g*cos(u)-a;
+            if (t >= real(0))
                 return t;
-            return g*cos(u+2.0943951023931954923)-a; // 2.094 = PI*2/3
+            return g*cos(u+real(2.0943951023931954923))-a; // 2.094 = PI*2/3
         } else {
-            double t = g*cos(u+2.0943951023931954923)-a;
-            if (t <= 1)
+            real t = g*cos(u+real(2.0943951023931954923))-a;
+            if (t <= real(1))
                 return t;
             return g*cos(u)-a;
         }
     }
-    double s = (h < 0 ? 1. : -1.)*MSDFGEN_CUBE_ROOT(fabs(h)+sqrt(hh-ggg));
+    real s = (h < real(0) ? real(1) : real(-1))*MSDFGEN_CUBE_ROOT(fabs(h)+sqrt(hh-ggg));
     return s+g/s-a;
 }
 
@@ -63,20 +64,20 @@ inline double quadraticNearPoint(const Vector2 p, const Vector2 q, const Vector2
  * r = 3*P2-6*P1+3*P0
  * s = P3-3*P2+3*P1-P0
  */
-inline double cubicNearPoint(const Vector2 p, const Vector2 q, const Vector2 r, const Vector2 s, double &squaredDistance) {
+inline real cubicNearPoint(const Vector2 p, const Vector2 q, const Vector2 r, const Vector2 s, real &squaredDistance) {
     squaredDistance = p.squaredLength();
-    double bestT = 0;
+    real bestT = 0;
     for (int i = 0; i <= MSDFGEN_CUBIC_SEARCH_STARTS; ++i) {
-        double t = 1./MSDFGEN_CUBIC_SEARCH_STARTS*i;
+        real t = real(1)/real(MSDFGEN_CUBIC_SEARCH_STARTS)*real(i);
         Vector2 curP = p-(q+(r+s*t)*t)*t;
         for (int step = 0; step < MSDFGEN_CUBIC_SEARCH_STEPS; ++step) {
-            Vector2 d0 = q+(r+r+3*s*t)*t;
-            Vector2 d1 = r+r+6*s*t;
+            Vector2 d0 = q+(r+r+real(3)*s*t)*t;
+            Vector2 d1 = r+r+real(6)*s*t;
             t += dotProduct(curP, d0)/(d0.squaredLength()-dotProduct(curP, d1));
-            if (t <= 0 || t >= 1)
+            if (t <= real(0) || t >= real(1))
                 break;
             curP = p-(q+(r+s*t)*t)*t;
-            double curSquaredDistance = curP.squaredLength();
+            real curSquaredDistance = curP.squaredLength();
             if (curSquaredDistance < squaredDistance) {
                 squaredDistance = curSquaredDistance;
                 bestT = t;
@@ -86,8 +87,8 @@ inline double cubicNearPoint(const Vector2 p, const Vector2 q, const Vector2 r,
     return bestT;
 }
 
-inline double cubicNearPoint(const Vector2 p, const Vector2 q, const Vector2 r, const Vector2 s) {
-    double squaredDistance;
+inline real cubicNearPoint(const Vector2 p, const Vector2 q, const Vector2 r, const Vector2 s) {
+    real squaredDistance;
     return cubicNearPoint(p, q, r, s, squaredDistance);
 }
 

+ 4 - 3
core/bitmap-interpolation.hpp

@@ -1,6 +1,7 @@
 
 #pragma once
 
+#include "types.h"
 #include "arithmetics.hpp"
 #include "Vector2.hpp"
 #include "BitmapRef.hpp"
@@ -9,13 +10,13 @@ namespace msdfgen {
 
 template <typename T, int N>
 static void interpolate(T *output, const BitmapConstRef<T, N> &bitmap, Point2 pos) {
-    pos -= .5;
+    pos -= real(.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;
+    real lr = pos.x-real(l);
+    real bt = pos.y-real(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)

+ 14 - 14
core/contour-combiners.cpp

@@ -6,21 +6,21 @@
 
 namespace msdfgen {
 
-static void initDistance(double &distance) {
-    distance = -DBL_MAX;
+static void initDistance(real &distance) {
+    distance = -FLT_MAX;
 }
 
 static void initDistance(MultiDistance &distance) {
-    distance.r = -DBL_MAX;
-    distance.g = -DBL_MAX;
-    distance.b = -DBL_MAX;
+    distance.r = -FLT_MAX;
+    distance.g = -FLT_MAX;
+    distance.b = -FLT_MAX;
 }
 
-static double resolveDistance(double distance) {
+static real resolveDistance(real distance) {
     return distance;
 }
 
-static double resolveDistance(const MultiDistance &distance) {
+static real resolveDistance(const MultiDistance &distance) {
     return median(distance.r, distance.g, distance.b);
 }
 
@@ -79,22 +79,22 @@ typename OverlappingContourCombiner<EdgeSelector>::DistanceType OverlappingConto
     for (int i = 0; i < contourCount; ++i) {
         DistanceType edgeDistance = edgeSelectors[i].distance();
         shapeEdgeSelector.merge(edgeSelectors[i]);
-        if (windings[i] > 0 && resolveDistance(edgeDistance) >= 0)
+        if (windings[i] > 0 && resolveDistance(edgeDistance) >= real(0))
             innerEdgeSelector.merge(edgeSelectors[i]);
-        if (windings[i] < 0 && resolveDistance(edgeDistance) <= 0)
+        if (windings[i] < 0 && resolveDistance(edgeDistance) <= real(0))
             outerEdgeSelector.merge(edgeSelectors[i]);
     }
 
     DistanceType shapeDistance = shapeEdgeSelector.distance();
     DistanceType innerDistance = innerEdgeSelector.distance();
     DistanceType outerDistance = outerEdgeSelector.distance();
-    double innerScalarDistance = resolveDistance(innerDistance);
-    double outerScalarDistance = resolveDistance(outerDistance);
+    real innerScalarDistance = resolveDistance(innerDistance);
+    real outerScalarDistance = resolveDistance(outerDistance);
     DistanceType distance;
     initDistance(distance);
 
     int winding = 0;
-    if (innerScalarDistance >= 0 && fabs(innerScalarDistance) <= fabs(outerScalarDistance)) {
+    if (innerScalarDistance >= real(0) && fabs(innerScalarDistance) <= fabs(outerScalarDistance)) {
         distance = innerDistance;
         winding = 1;
         for (int i = 0; i < contourCount; ++i)
@@ -103,7 +103,7 @@ typename OverlappingContourCombiner<EdgeSelector>::DistanceType OverlappingConto
                 if (fabs(resolveDistance(contourDistance)) < fabs(outerScalarDistance) && resolveDistance(contourDistance) > resolveDistance(distance))
                     distance = contourDistance;
             }
-    } else if (outerScalarDistance <= 0 && fabs(outerScalarDistance) < fabs(innerScalarDistance)) {
+    } else if (outerScalarDistance <= real(0) && fabs(outerScalarDistance) < fabs(innerScalarDistance)) {
         distance = outerDistance;
         winding = -1;
         for (int i = 0; i < contourCount; ++i)
@@ -118,7 +118,7 @@ typename OverlappingContourCombiner<EdgeSelector>::DistanceType OverlappingConto
     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)))
+            if (resolveDistance(contourDistance)*resolveDistance(distance) >= real(0) && fabs(resolveDistance(contourDistance)) < fabs(resolveDistance(distance)))
                 distance = contourDistance;
         }
     if (resolveDistance(distance) == resolveDistance(shapeDistance))

+ 30 - 30
core/edge-coloring.cpp

@@ -11,15 +11,15 @@
 
 namespace msdfgen {
 
-static bool isCorner(const Vector2 &aDir, const Vector2 &bDir, double crossThreshold) {
+static bool isCorner(const Vector2 &aDir, const Vector2 &bDir, real crossThreshold) {
     return dotProduct(aDir, bDir) <= 0 || fabs(crossProduct(aDir, bDir)) > crossThreshold;
 }
 
-static double estimateEdgeLength(const EdgeSegment *edge) {
-    double len = 0;
+static real estimateEdgeLength(const EdgeSegment *edge) {
+    real len = 0;
     Point2 prev = edge->point(0);
     for (int i = 1; i <= MSDFGEN_EDGE_LENGTH_PRECISION; ++i) {
-        Point2 cur = edge->point(1./MSDFGEN_EDGE_LENGTH_PRECISION*i);
+        Point2 cur = edge->point(real(1)/real(MSDFGEN_EDGE_LENGTH_PRECISION)*real(i));
         len += (cur-prev).length();
         prev = cur;
     }
@@ -43,8 +43,8 @@ static void switchColor(EdgeColor &color, unsigned long long &seed, EdgeColor ba
     seed >>= 1;
 }
 
-void edgeColoringSimple(Shape &shape, double angleThreshold, unsigned long long seed) {
-    double crossThreshold = sin(angleThreshold);
+void edgeColoringSimple(Shape &shape, real angleThreshold, unsigned long long seed) {
+    real crossThreshold = sin(angleThreshold);
     std::vector<int> corners;
     for (std::vector<Contour>::iterator contour = shape.contours.begin(); contour != shape.contours.end(); ++contour) {
         // Identify corners
@@ -115,18 +115,18 @@ void edgeColoringSimple(Shape &shape, double angleThreshold, unsigned long long
 
 struct EdgeColoringInkTrapCorner {
     int index;
-    double prevEdgeLengthEstimate;
+    real prevEdgeLengthEstimate;
     bool minor;
     EdgeColor color;
 };
 
-void edgeColoringInkTrap(Shape &shape, double angleThreshold, unsigned long long seed) {
+void edgeColoringInkTrap(Shape &shape, real angleThreshold, unsigned long long seed) {
     typedef EdgeColoringInkTrapCorner Corner;
-    double crossThreshold = sin(angleThreshold);
+    real crossThreshold = sin(angleThreshold);
     std::vector<Corner> corners;
     for (std::vector<Contour>::iterator contour = shape.contours.begin(); contour != shape.contours.end(); ++contour) {
         // Identify corners
-        double splineLength = 0;
+        real splineLength = 0;
         corners.clear();
         if (!contour->edges.empty()) {
             Vector2 prevDirection = contour->edges.back()->direction(1);
@@ -227,29 +227,29 @@ void edgeColoringInkTrap(Shape &shape, double angleThreshold, unsigned long long
 #define MAX_RECOLOR_STEPS 16
 #define EDGE_DISTANCE_PRECISION 16
 
-static double edgeToEdgeDistance(const EdgeSegment &a, const EdgeSegment &b, int precision) {
+static real edgeToEdgeDistance(const EdgeSegment &a, const EdgeSegment &b, int precision) {
     if (a.point(0) == b.point(0) || a.point(0) == b.point(1) || a.point(1) == b.point(0) || a.point(1) == b.point(1))
         return 0;
-    double iFac = 1./precision;
-    double minDistance = (b.point(0)-a.point(0)).length();
+    real iFac = real(1)/real(precision);
+    real minDistance = (b.point(0)-a.point(0)).length();
     for (int i = 0; i <= precision; ++i) {
-        double t = iFac*i;
-        double d = fabs(a.signedDistance(b.point(t), t).distance);
+        real t = iFac*i;
+        real d = fabs(a.signedDistance(b.point(t), t).distance);
         minDistance = min(minDistance, d);
     }
     for (int i = 0; i <= precision; ++i) {
-        double t = iFac*i;
-        double d = fabs(b.signedDistance(a.point(t), t).distance);
+        real t = iFac*i;
+        real d = fabs(b.signedDistance(a.point(t), t).distance);
         minDistance = min(minDistance, d);
     }
     return minDistance;
 }
 
-static double splineToSplineDistance(EdgeSegment *const *edgeSegments, int aStart, int aEnd, int bStart, int bEnd, int precision) {
-    double minDistance = DBL_MAX;
+static real splineToSplineDistance(EdgeSegment *const *edgeSegments, int aStart, int aEnd, int bStart, int bEnd, int precision) {
+    real minDistance = FLT_MAX;
     for (int ai = aStart; ai < aEnd; ++ai)
         for (int bi = bStart; bi < bEnd && minDistance; ++bi) {
-            double d = edgeToEdgeDistance(*edgeSegments[ai], *edgeSegments[bi], precision);
+            real d = edgeToEdgeDistance(*edgeSegments[ai], *edgeSegments[bi], precision);
             minDistance = min(minDistance, d);
         }
     return minDistance;
@@ -358,16 +358,16 @@ static bool tryAddEdge(int *coloring, int *const *edgeMatrix, int vertexCount, i
     return true;
 }
 
-static int cmpDoublePtr(const void *a, const void *b) {
-    return sign(**reinterpret_cast<const double *const *>(a)-**reinterpret_cast<const double *const *>(b));
+static int cmpRealPtr(const void *a, const void *b) {
+    return sign(**reinterpret_cast<const real *const *>(a)-**reinterpret_cast<const real *const *>(b));
 }
 
-void edgeColoringByDistance(Shape &shape, double angleThreshold, unsigned long long seed) {
+void edgeColoringByDistance(Shape &shape, real angleThreshold, unsigned long long seed) {
 
     std::vector<EdgeSegment *> edgeSegments;
     std::vector<int> splineStarts;
 
-    double crossThreshold = sin(angleThreshold);
+    real crossThreshold = sin(angleThreshold);
     std::vector<int> corners;
     for (std::vector<Contour>::iterator contour = shape.contours.begin(); contour != shape.contours.end(); ++contour)
         if (!contour->edges.empty()) {
@@ -445,29 +445,29 @@ void edgeColoringByDistance(Shape &shape, double angleThreshold, unsigned long l
     if (!splineCount)
         return;
 
-    std::vector<double> distanceMatrixStorage(splineCount*splineCount);
-    std::vector<double *> distanceMatrix(splineCount);
+    std::vector<real> distanceMatrixStorage(splineCount*splineCount);
+    std::vector<real *> distanceMatrix(splineCount);
     for (int i = 0; i < splineCount; ++i)
         distanceMatrix[i] = &distanceMatrixStorage[i*splineCount];
-    const double *distanceMatrixBase = &distanceMatrixStorage[0];
+    const real *distanceMatrixBase = &distanceMatrixStorage[0];
 
     for (int i = 0; i < splineCount; ++i) {
         distanceMatrix[i][i] = -1;
         for (int j = i+1; j < splineCount; ++j) {
-            double dist = splineToSplineDistance(&edgeSegments[0], splineStarts[i], splineStarts[i+1], splineStarts[j], splineStarts[j+1], EDGE_DISTANCE_PRECISION);
+            real dist = splineToSplineDistance(&edgeSegments[0], splineStarts[i], splineStarts[i+1], splineStarts[j], splineStarts[j+1], EDGE_DISTANCE_PRECISION);
             distanceMatrix[i][j] = dist;
             distanceMatrix[j][i] = dist;
         }
     }
 
-    std::vector<const double *> graphEdgeDistances;
+    std::vector<const real *> graphEdgeDistances;
     graphEdgeDistances.reserve(splineCount*(splineCount-1)/2);
     for (int i = 0; i < splineCount; ++i)
         for (int j = i+1; j < splineCount; ++j)
             graphEdgeDistances.push_back(&distanceMatrix[i][j]);
     int graphEdgeCount = (int) graphEdgeDistances.size();
     if (!graphEdgeDistances.empty())
-        qsort(&graphEdgeDistances[0], graphEdgeDistances.size(), sizeof(const double *), &cmpDoublePtr);
+        qsort(&graphEdgeDistances[0], graphEdgeDistances.size(), sizeof(const real *), &cmpRealPtr);
 
     std::vector<int> edgeMatrixStorage(splineCount*splineCount);
     std::vector<int *> edgeMatrix(splineCount);

+ 4 - 3
core/edge-coloring.h

@@ -1,6 +1,7 @@
 
 #pragma once
 
+#include "types.h"
 #include "Shape.h"
 
 #define MSDFGEN_EDGE_LENGTH_PRECISION 4
@@ -12,18 +13,18 @@ namespace msdfgen {
  *  angleThreshold specifies the maximum angle (in radians) to be considered a corner, for example 3 (~172 degrees).
  *  Values below 1/2 PI will be treated as the external angle.
  */
-void edgeColoringSimple(Shape &shape, double angleThreshold, unsigned long long seed = 0);
+void edgeColoringSimple(Shape &shape, real angleThreshold, unsigned long long seed = 0);
 
 /** The alternative "ink trap" coloring strategy is designed for better results with typefaces
  *  that use ink traps as a design feature. It guarantees that even if all edges that are shorter than
  *  both their neighboring edges are removed, the coloring remains consistent with the established rules.
  */
-void edgeColoringInkTrap(Shape &shape, double angleThreshold, unsigned long long seed = 0);
+void edgeColoringInkTrap(Shape &shape, real angleThreshold, unsigned long long seed = 0);
 
 /** The alternative coloring by distance tries to use different colors for edges that are close together.
  *  This should theoretically be the best strategy on average. However, since it needs to compute the distance
  *  between all pairs of edges, and perform a graph optimization task, it is much slower than the rest.
  */
-void edgeColoringByDistance(Shape &shape, double angleThreshold, unsigned long long seed = 0);
+void edgeColoringByDistance(Shape &shape, real angleThreshold, unsigned long long seed = 0);
 
 }

+ 133 - 133
core/edge-segments.cpp

@@ -9,24 +9,24 @@
 
 namespace msdfgen {
 
-void EdgeSegment::distanceToPseudoDistance(SignedDistance &distance, Point2 origin, double param) const {
-    if (param < 0) {
+void EdgeSegment::distanceToPseudoDistance(SignedDistance &distance, Point2 origin, real param) const {
+    if (param < real(0)) {
         Vector2 dir = direction(0).normalize();
         Vector2 aq = origin-point(0);
-        double ts = dotProduct(aq, dir);
-        if (ts < 0) {
-            double pseudoDistance = crossProduct(aq, dir);
+        real ts = dotProduct(aq, dir);
+        if (ts < real(0)) {
+            real pseudoDistance = crossProduct(aq, dir);
             if (fabs(pseudoDistance) <= fabs(distance.distance)) {
                 distance.distance = pseudoDistance;
                 distance.dot = 0;
             }
         }
-    } else if (param > 1) {
+    } else if (param > real(1)) {
         Vector2 dir = direction(1).normalize();
         Vector2 bq = origin-point(1);
-        double ts = dotProduct(bq, dir);
-        if (ts > 0) {
-            double pseudoDistance = crossProduct(bq, dir);
+        real ts = dotProduct(bq, dir);
+        if (ts > real(0)) {
+            real pseudoDistance = crossProduct(bq, dir);
             if (fabs(pseudoDistance) <= fabs(distance.distance)) {
                 distance.distance = pseudoDistance;
                 distance.dot = 0;
@@ -42,7 +42,7 @@ LinearSegment::LinearSegment(Point2 p0, Point2 p1, EdgeColor edgeColor) : EdgeSe
 
 QuadraticSegment::QuadraticSegment(Point2 p0, Point2 p1, Point2 p2, EdgeColor edgeColor) : EdgeSegment(edgeColor) {
     if (p1 == p0 || p1 == p2)
-        p1 = 0.5*(p0+p2);
+        p1 = real(.5)*(p0+p2);
     p[0] = p0;
     p[1] = p1;
     p[2] = p2;
@@ -50,8 +50,8 @@ QuadraticSegment::QuadraticSegment(Point2 p0, Point2 p1, Point2 p2, EdgeColor ed
 
 CubicSegment::CubicSegment(Point2 p0, Point2 p1, Point2 p2, Point2 p3, EdgeColor edgeColor) : EdgeSegment(edgeColor) {
     if ((p1 == p0 || p1 == p3) && (p2 == p0 || p2 == p3)) {
-        p1 = mix(p0, p3, 1/3.);
-        p2 = mix(p0, p3, 2/3.);
+        p1 = mix(p0, p3, real(1)/real(3));
+        p2 = mix(p0, p3, real(2)/real(3));
     }
     p[0] = p0;
     p[1] = p1;
@@ -95,31 +95,31 @@ const Point2 *CubicSegment::controlPoints() const {
     return p;
 }
 
-Point2 LinearSegment::point(double param) const {
+Point2 LinearSegment::point(real param) const {
     return mix(p[0], p[1], param);
 }
 
-Point2 QuadraticSegment::point(double param) const {
+Point2 QuadraticSegment::point(real param) const {
     return mix(mix(p[0], p[1], param), mix(p[1], p[2], param), param);
 }
 
-Point2 CubicSegment::point(double param) const {
+Point2 CubicSegment::point(real param) const {
     Vector2 p12 = mix(p[1], p[2], param);
     return mix(mix(mix(p[0], p[1], param), p12, param), mix(p12, mix(p[2], p[3], param), param), param);
 }
 
-Vector2 LinearSegment::direction(double param) const {
+Vector2 LinearSegment::direction(real param) const {
     return p[1]-p[0];
 }
 
-Vector2 QuadraticSegment::direction(double param) const {
+Vector2 QuadraticSegment::direction(real param) const {
     Vector2 tangent = mix(p[1]-p[0], p[2]-p[1], param);
     if (!tangent)
         return p[2]-p[0];
     return tangent;
 }
 
-Vector2 CubicSegment::direction(double param) const {
+Vector2 CubicSegment::direction(real param) const {
     Vector2 tangent = mix(mix(p[1]-p[0], p[2]-p[1], param), mix(p[2]-p[1], p[3]-p[2], param), param);
     if (!tangent) {
         if (param == 0) return p[2]-p[0];
@@ -128,195 +128,195 @@ Vector2 CubicSegment::direction(double param) const {
     return tangent;
 }
 
-Vector2 LinearSegment::directionChange(double param) const {
+Vector2 LinearSegment::directionChange(real param) const {
     return Vector2();
 }
 
-Vector2 QuadraticSegment::directionChange(double param) const {
+Vector2 QuadraticSegment::directionChange(real param) const {
     return (p[2]-p[1])-(p[1]-p[0]);
 }
 
-Vector2 CubicSegment::directionChange(double param) const {
+Vector2 CubicSegment::directionChange(real param) const {
     return mix((p[2]-p[1])-(p[1]-p[0]), (p[3]-p[2])-(p[2]-p[1]), param);
 }
 
-double LinearSegment::length() const {
+real LinearSegment::length() const {
     return (p[1]-p[0]).length();
 }
 
-double QuadraticSegment::length() const {
+real QuadraticSegment::length() const {
     Vector2 ab = p[1]-p[0];
     Vector2 br = p[2]-p[1]-ab;
-    double abab = dotProduct(ab, ab);
-    double abbr = dotProduct(ab, br);
-    double brbr = dotProduct(br, br);
-    double abLen = sqrt(abab);
-    double brLen = sqrt(brbr);
-    double crs = crossProduct(ab, br);
-    double h = sqrt(abab+abbr+abbr+brbr);
+    real abab = dotProduct(ab, ab);
+    real abbr = dotProduct(ab, br);
+    real brbr = dotProduct(br, br);
+    real abLen = sqrt(abab);
+    real brLen = sqrt(brbr);
+    real crs = crossProduct(ab, br);
+    real h = sqrt(abab+abbr+abbr+brbr);
     return (
         brLen*((abbr+brbr)*h-abbr*abLen)+
         crs*crs*log((brLen*h+abbr+brbr)/(brLen*abLen+abbr))
     )/(brbr*brLen);
 }
 
-SignedDistance LinearSegment::signedDistance(Point2 origin, double &param) const {
+SignedDistance LinearSegment::signedDistance(Point2 origin, real &param) 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);
+    Vector2 eq = p[param > real(.5)]-origin;
+    real endpointDistance = eq.length();
+    if (param > real(0) && param < real(1)) {
+        real 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())));
+    return SignedDistance(real(nonZeroSign(crossProduct(aq, ab)))*endpointDistance, fabs(dotProduct(ab.normalize(), eq.normalize())));
 }
 
 #ifdef MSDFGEN_USE_BEZIER_SOLVER
 
-SignedDistance QuadraticSegment::signedDistance(Point2 origin, double &param) const {
+SignedDistance QuadraticSegment::signedDistance(Point2 origin, real &param) const {
     Vector2 ap = origin-p[0];
     Vector2 bp = origin-p[2];
-    Vector2 q = 2*(p[1]-p[0]);
+    Vector2 q = real(2)*(p[1]-p[0]);
     Vector2 r = p[2]-2*p[1]+p[0];
-    double aSqD = ap.squaredLength();
-    double bSqD = bp.squaredLength();
-    double t = quadraticNearPoint(ap, q, r);
-    if (t > 0 && t < 1) {
+    real aSqD = ap.squaredLength();
+    real bSqD = bp.squaredLength();
+    real t = quadraticNearPoint(ap, q, r);
+    if (t > real(0) && t < real(1)) {
         Vector2 tp = ap-(q+r*t)*t;
-        double tSqD = tp.squaredLength();
+        real tSqD = tp.squaredLength();
         if (tSqD < aSqD && tSqD < bSqD) {
             param = t;
-            return SignedDistance(nonZeroSign(crossProduct(tp, q+2*r*t))*sqrt(tSqD), 0);
+            return SignedDistance(real(nonZeroSign(crossProduct(tp, q+real(2)*r*t)))*sqrt(tSqD), 0);
         }
     }
     if (bSqD < aSqD) {
         Vector2 d = q+r+r;
         if (!d)
             d = p[2]-p[0];
-        param = dotProduct(bp, d)/d.squaredLength()+1;
-        return SignedDistance(nonZeroSign(crossProduct(bp, d))*sqrt(bSqD), dotProduct(bp.normalize(), d.normalize()));
+        param = dotProduct(bp, d)/d.squaredLength()+real(1);
+        return SignedDistance(real(nonZeroSign(crossProduct(bp, d)))*sqrt(bSqD), dotProduct(bp.normalize(), d.normalize()));
     }
     if (!q)
         q = p[2]-p[0];
     param = dotProduct(ap, q)/q.squaredLength();
-    return SignedDistance(nonZeroSign(crossProduct(ap, q))*sqrt(aSqD), -dotProduct(ap.normalize(), q.normalize()));
+    return SignedDistance(real(nonZeroSign(crossProduct(ap, q)))*sqrt(aSqD), -dotProduct(ap.normalize(), q.normalize()));
 }
 
-SignedDistance CubicSegment::signedDistance(Point2 origin, double &param) const {
+SignedDistance CubicSegment::signedDistance(Point2 origin, real &param) const {
     Vector2 ap = origin-p[0];
     Vector2 bp = origin-p[3];
-    Vector2 q = 3*(p[1]-p[0]);
-    Vector2 r = 3*(p[2]-p[1])-q;
-    Vector2 s = p[3]-3*(p[2]-p[1])-p[0];
-    double aSqD = ap.squaredLength();
-    double bSqD = bp.squaredLength();
-    double tSqD;
-    double t = cubicNearPoint(ap, q, r, s, tSqD);
-    if (t > 0 && t < 1) {
+    Vector2 q = real(3)*(p[1]-p[0]);
+    Vector2 r = real(3)*(p[2]-p[1])-q;
+    Vector2 s = p[3]-real(3)*(p[2]-p[1])-p[0];
+    real aSqD = ap.squaredLength();
+    real bSqD = bp.squaredLength();
+    real tSqD;
+    real t = cubicNearPoint(ap, q, r, s, tSqD);
+    if (t > real(0) && t < real(1)) {
         if (tSqD < aSqD && tSqD < bSqD) {
             param = t;
-            return SignedDistance(nonZeroSign(crossProduct(ap-(q+(r+s*t)*t)*t, q+(r+r+3*s*t)*t))*sqrt(tSqD), 0);
+            return SignedDistance(real(nonZeroSign(crossProduct(ap-(q+(r+s*t)*t)*t, q+(r+r+real(3)*s*t)*t)))*sqrt(tSqD), 0);
         }
     }
     if (bSqD < aSqD) {
-        Vector2 d = q+r+r+3*s;
+        Vector2 d = q+r+r+real(3)*s;
         if (!d)
             d = p[3]-p[1];
-        param = dotProduct(bp, d)/d.squaredLength()+1;
-        return SignedDistance(nonZeroSign(crossProduct(bp, d))*sqrt(bSqD), dotProduct(bp.normalize(), d.normalize()));
+        param = dotProduct(bp, d)/d.squaredLength()+real(1);
+        return SignedDistance(real(nonZeroSign(crossProduct(bp, d)))*sqrt(bSqD), dotProduct(bp.normalize(), d.normalize()));
     }
     if (!q)
         q = p[2]-p[0];
     param = dotProduct(ap, q)/q.squaredLength();
-    return SignedDistance(nonZeroSign(crossProduct(ap, q))*sqrt(aSqD), -dotProduct(ap.normalize(), q.normalize()));
+    return SignedDistance(real(nonZeroSign(crossProduct(ap, q)))*sqrt(aSqD), -dotProduct(ap.normalize(), q.normalize()));
 }
 
 #else
 
-SignedDistance QuadraticSegment::signedDistance(Point2 origin, double &param) const {
+SignedDistance QuadraticSegment::signedDistance(Point2 origin, real &param) const {
     Vector2 qa = p[0]-origin;
     Vector2 ab = p[1]-p[0];
     Vector2 br = p[2]-p[1]-ab;
-    double a = dotProduct(br, br);
-    double b = 3*dotProduct(ab, br);
-    double c = 2*dotProduct(ab, ab)+dotProduct(qa, br);
-    double d = dotProduct(qa, ab);
-    double t[3];
+    real a = dotProduct(br, br);
+    real b = real(3)*dotProduct(ab, br);
+    real c = real(2)*dotProduct(ab, ab)+dotProduct(qa, br);
+    real d = dotProduct(qa, ab);
+    real t[3];
     int solutions = solveCubic(t, a, b, c, d);
 
     Vector2 epDir = direction(0);
-    double minDistance = nonZeroSign(crossProduct(epDir, qa))*qa.length(); // distance from A
+    real minDistance = real(nonZeroSign(crossProduct(epDir, qa)))*qa.length(); // distance from A
     param = -dotProduct(qa, epDir)/dotProduct(epDir, epDir);
     {
         epDir = direction(1);
-        double distance = (p[2]-origin).length(); // distance from B
+        real distance = (p[2]-origin).length(); // distance from B
         if (distance < fabs(minDistance)) {
-            minDistance = nonZeroSign(crossProduct(epDir, p[2]-origin))*distance;
+            minDistance = real(nonZeroSign(crossProduct(epDir, p[2]-origin)))*distance;
             param = dotProduct(origin-p[1], epDir)/dotProduct(epDir, epDir);
         }
     }
     for (int i = 0; i < solutions; ++i) {
-        if (t[i] > 0 && t[i] < 1) {
-            Point2 qe = qa+2*t[i]*ab+t[i]*t[i]*br;
-            double distance = qe.length();
+        if (t[i] > real(0) && t[i] < real(1)) {
+            Point2 qe = qa+real(2)*t[i]*ab+t[i]*t[i]*br;
+            real distance = qe.length();
             if (distance <= fabs(minDistance)) {
-                minDistance = nonZeroSign(crossProduct(ab+t[i]*br, qe))*distance;
+                minDistance = real(nonZeroSign(crossProduct(ab+t[i]*br, qe)))*distance;
                 param = t[i];
             }
         }
     }
 
-    if (param >= 0 && param <= 1)
+    if (param >= real(0) && param <= real(1))
         return SignedDistance(minDistance, 0);
-    if (param < .5)
+    if (param < real(.5))
         return SignedDistance(minDistance, fabs(dotProduct(direction(0).normalize(), qa.normalize())));
     else
         return SignedDistance(minDistance, fabs(dotProduct(direction(1).normalize(), (p[2]-origin).normalize())));
 }
 
-SignedDistance CubicSegment::signedDistance(Point2 origin, double &param) const {
+SignedDistance CubicSegment::signedDistance(Point2 origin, real &param) const {
     Vector2 qa = p[0]-origin;
     Vector2 ab = p[1]-p[0];
     Vector2 br = p[2]-p[1]-ab;
     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
+    real minDistance = real(nonZeroSign(crossProduct(epDir, qa)))*qa.length(); // distance from A
     param = -dotProduct(qa, epDir)/dotProduct(epDir, epDir);
     {
         epDir = direction(1);
-        double distance = (p[3]-origin).length(); // distance from B
+        real distance = (p[3]-origin).length(); // distance from B
         if (distance < fabs(minDistance)) {
-            minDistance = nonZeroSign(crossProduct(epDir, p[3]-origin))*distance;
+            minDistance = real(nonZeroSign(crossProduct(epDir, p[3]-origin)))*distance;
             param = dotProduct(epDir-(p[3]-origin), epDir)/dotProduct(epDir, epDir);
         }
     }
     // Iterative minimum distance search
     for (int i = 0; i <= MSDFGEN_CUBIC_SEARCH_STARTS; ++i) {
-        double t = (double) i/MSDFGEN_CUBIC_SEARCH_STARTS;
-        Vector2 qe = qa+3*t*ab+3*t*t*br+t*t*t*as;
+        real t = real(1)/real(MSDFGEN_CUBIC_SEARCH_STARTS)*real(i);
+        Vector2 qe = qa+real(3)*t*ab+real(3)*t*t*br+t*t*t*as;
         for (int step = 0; step < MSDFGEN_CUBIC_SEARCH_STEPS; ++step) {
             // Improve t
-            Vector2 d1 = 3*ab+6*t*br+3*t*t*as;
-            Vector2 d2 = 6*br+6*t*as;
+            Vector2 d1 = real(3)*ab+real(6)*t*br+real(3)*t*t*as;
+            Vector2 d2 = real(6)*br+real(6)*t*as;
             t -= dotProduct(qe, d1)/(dotProduct(d1, d1)+dotProduct(qe, d2));
-            if (t <= 0 || t >= 1)
+            if (t <= real(0) || t >= real(1))
                 break;
-            qe = qa+3*t*ab+3*t*t*br+t*t*t*as;
-            double distance = qe.length();
+            qe = qa+real(3)*t*ab+real(3)*t*t*br+t*t*t*as;
+            real distance = qe.length();
             if (distance < fabs(minDistance)) {
-                minDistance = nonZeroSign(crossProduct(d1, qe))*distance;
+                minDistance = real(nonZeroSign(crossProduct(d1, qe)))*distance;
                 param = t;
             }
         }
     }
 
-    if (param >= 0 && param <= 1)
+    if (param >= real(0) && param <= real(1))
         return SignedDistance(minDistance, 0);
-    if (param < .5)
+    if (param < real(.5))
         return SignedDistance(minDistance, fabs(dotProduct(direction(0).normalize(), qa.normalize())));
     else
         return SignedDistance(minDistance, fabs(dotProduct(direction(1).normalize(), (p[3]-origin).normalize())));
@@ -324,9 +324,9 @@ SignedDistance CubicSegment::signedDistance(Point2 origin, double &param) const
 
 #endif
 
-int LinearSegment::scanlineIntersections(double x[3], int dy[3], double y) const {
+int LinearSegment::scanlineIntersections(real x[3], int dy[3], real y) const {
     if ((y >= p[0].y && y < p[1].y) || (y >= p[1].y && y < p[0].y)) {
-        double param = (y-p[0].y)/(p[1].y-p[0].y);
+        real param = (y-p[0].y)/(p[1].y-p[0].y);
         x[0] = mix(p[0].x, p[1].x, param);
         dy[0] = sign(p[1].y-p[0].y);
         return 1;
@@ -334,7 +334,7 @@ int LinearSegment::scanlineIntersections(double x[3], int dy[3], double y) const
     return 0;
 }
 
-int QuadraticSegment::scanlineIntersections(double x[3], int dy[3], double y) const {
+int QuadraticSegment::scanlineIntersections(real x[3], int dy[3], real y) const {
     int total = 0;
     int nextDY = y > p[0].y ? 1 : -1;
     x[total] = p[0].x;
@@ -347,16 +347,16 @@ int QuadraticSegment::scanlineIntersections(double x[3], int dy[3], double y) co
     {
         Vector2 ab = p[1]-p[0];
         Vector2 br = p[2]-p[1]-ab;
-        double t[2];
+        real t[2];
         int solutions = solveQuadratic(t, br.y, 2*ab.y, p[0].y-y);
         // Sort solutions
-        double tmp;
+        real tmp;
         if (solutions >= 2 && t[0] > t[1])
             tmp = t[0], t[0] = t[1], t[1] = tmp;
         for (int i = 0; i < solutions && total < 2; ++i) {
             if (t[i] >= 0 && t[i] <= 1) {
                 x[total] = p[0].x+2*t[i]*ab.x+t[i]*t[i]*br.x;
-                if (nextDY*(ab.y+t[i]*br.y) >= 0) {
+                if (real(nextDY)*(ab.y+t[i]*br.y) >= real(0)) {
                     dy[total++] = nextDY;
                     nextDY = -nextDY;
                 }
@@ -388,7 +388,7 @@ int QuadraticSegment::scanlineIntersections(double x[3], int dy[3], double y) co
     return total;
 }
 
-int CubicSegment::scanlineIntersections(double x[3], int dy[3], double y) const {
+int CubicSegment::scanlineIntersections(real x[3], int dy[3], real y) const {
     int total = 0;
     int nextDY = y > p[0].y ? 1 : -1;
     x[total] = p[0].x;
@@ -402,10 +402,10 @@ int CubicSegment::scanlineIntersections(double x[3], int dy[3], double y) const
         Vector2 ab = p[1]-p[0];
         Vector2 br = p[2]-p[1]-ab;
         Vector2 as = (p[3]-p[2])-(p[2]-p[1])-br;
-        double t[3];
+        real t[3];
         int solutions = solveCubic(t, as.y, 3*br.y, 3*ab.y, p[0].y-y);
         // Sort solutions
-        double tmp;
+        real tmp;
         if (solutions >= 2) {
             if (t[0] > t[1])
                 tmp = t[0], t[0] = t[1], t[1] = tmp;
@@ -417,8 +417,8 @@ int CubicSegment::scanlineIntersections(double x[3], int dy[3], double y) const
         }
         for (int i = 0; i < solutions && total < 3; ++i) {
             if (t[i] >= 0 && t[i] <= 1) {
-                x[total] = p[0].x+3*t[i]*ab.x+3*t[i]*t[i]*br.x+t[i]*t[i]*t[i]*as.x;
-                if (nextDY*(ab.y+2*t[i]*br.y+t[i]*t[i]*as.y) >= 0) {
+                x[total] = p[0].x+real(3)*t[i]*ab.x+real(3)*t[i]*t[i]*br.x+t[i]*t[i]*t[i]*as.x;
+                if (real(nextDY)*(ab.y+real(2)*t[i]*br.y+t[i]*t[i]*as.y) >= real(0)) {
                     dy[total++] = nextDY;
                     nextDY = -nextDY;
                 }
@@ -450,49 +450,49 @@ int CubicSegment::scanlineIntersections(double x[3], int dy[3], double y) const
     return total;
 }
 
-static void pointBounds(Point2 p, double &l, double &b, double &r, double &t) {
+static void pointBounds(Point2 p, real &l, real &b, real &r, real &t) {
     if (p.x < l) l = p.x;
     if (p.y < b) b = p.y;
     if (p.x > r) r = p.x;
     if (p.y > t) t = p.y;
 }
 
-void LinearSegment::bound(double &l, double &b, double &r, double &t) const {
+void LinearSegment::bound(real &l, real &b, real &r, real &t) const {
     pointBounds(p[0], l, b, r, t);
     pointBounds(p[1], l, b, r, t);
 }
 
-void QuadraticSegment::bound(double &l, double &b, double &r, double &t) const {
+void QuadraticSegment::bound(real &l, real &b, real &r, real &t) const {
     pointBounds(p[0], l, b, r, t);
     pointBounds(p[2], l, b, r, t);
     Vector2 bot = (p[1]-p[0])-(p[2]-p[1]);
     if (bot.x) {
-        double param = (p[1].x-p[0].x)/bot.x;
-        if (param > 0 && param < 1)
+        real param = (p[1].x-p[0].x)/bot.x;
+        if (param > real(0) && param < real(1))
             pointBounds(point(param), l, b, r, t);
     }
     if (bot.y) {
-        double param = (p[1].y-p[0].y)/bot.y;
-        if (param > 0 && param < 1)
+        real param = (p[1].y-p[0].y)/bot.y;
+        if (param > real(0) && param < real(1))
             pointBounds(point(param), l, b, r, t);
     }
 }
 
-void CubicSegment::bound(double &l, double &b, double &r, double &t) const {
+void CubicSegment::bound(real &l, real &b, real &r, real &t) const {
     pointBounds(p[0], l, b, r, t);
     pointBounds(p[3], l, b, r, t);
     Vector2 a0 = p[1]-p[0];
-    Vector2 a1 = 2*(p[2]-p[1]-a0);
-    Vector2 a2 = p[3]-3*p[2]+3*p[1]-p[0];
-    double params[2];
+    Vector2 a1 = real(2)*(p[2]-p[1]-a0);
+    Vector2 a2 = p[3]-real(3)*p[2]+real(3)*p[1]-p[0];
+    real params[2];
     int solutions;
     solutions = solveQuadratic(params, a2.x, a1.x, a0.x);
     for (int i = 0; i < solutions; ++i)
-        if (params[i] > 0 && params[i] < 1)
+        if (params[i] > real(0) && params[i] < real(1))
             pointBounds(point(params[i]), l, b, r, t);
     solutions = solveQuadratic(params, a2.y, a1.y, a0.y);
     for (int i = 0; i < solutions; ++i)
-        if (params[i] > 0 && params[i] < 1)
+        if (params[i] > real(0) && params[i] < real(1))
             pointBounds(point(params[i]), l, b, r, t);
 }
 
@@ -526,7 +526,7 @@ void QuadraticSegment::moveStartPoint(Point2 to) {
     Point2 origP1 = p[1];
     p[1] += crossProduct(p[0]-p[1], to-p[0])/crossProduct(p[0]-p[1], p[2]-p[1])*(p[2]-p[1]);
     p[0] = to;
-    if (dotProduct(origSDir, p[0]-p[1]) < 0)
+    if (dotProduct(origSDir, p[0]-p[1]) < real(0))
         p[1] = origP1;
 }
 
@@ -544,7 +544,7 @@ void QuadraticSegment::moveEndPoint(Point2 to) {
     Point2 origP1 = p[1];
     p[1] += crossProduct(p[2]-p[1], to-p[2])/crossProduct(p[2]-p[1], p[0]-p[1])*(p[0]-p[1]);
     p[2] = to;
-    if (dotProduct(origEDir, p[2]-p[1]) < 0)
+    if (dotProduct(origEDir, p[2]-p[1]) < real(0))
         p[1] = origP1;
 }
 
@@ -554,40 +554,40 @@ void CubicSegment::moveEndPoint(Point2 to) {
 }
 
 void LinearSegment::splitInThirds(EdgeSegment *&part1, EdgeSegment *&part2, EdgeSegment *&part3) const {
-    part1 = new LinearSegment(p[0], point(1/3.), color);
-    part2 = new LinearSegment(point(1/3.), point(2/3.), color);
-    part3 = new LinearSegment(point(2/3.), p[1], color);
+    part1 = new LinearSegment(p[0], point(real(1)/real(3)), color);
+    part2 = new LinearSegment(point(real(1)/real(3)), point(real(2)/real(3)), color);
+    part3 = new LinearSegment(point(real(2)/real(3)), p[1], color);
 }
 
 void QuadraticSegment::splitInThirds(EdgeSegment *&part1, EdgeSegment *&part2, EdgeSegment *&part3) const {
-    part1 = new QuadraticSegment(p[0], mix(p[0], p[1], 1/3.), point(1/3.), color);
-    part2 = new QuadraticSegment(point(1/3.), mix(mix(p[0], p[1], 5/9.), mix(p[1], p[2], 4/9.), .5), point(2/3.), color);
-    part3 = new QuadraticSegment(point(2/3.), mix(p[1], p[2], 2/3.), p[2], color);
+    part1 = new QuadraticSegment(p[0], mix(p[0], p[1], real(1)/real(3)), point(real(1)/real(3)), color);
+    part2 = new QuadraticSegment(point(real(1)/real(3)), mix(mix(p[0], p[1], real(5)/real(9)), mix(p[1], p[2], real(4)/real(9)), real(.5)), point(real(2)/real(3)), color);
+    part3 = new QuadraticSegment(point(real(2)/real(3)), mix(p[1], p[2], real(2)/real(3)), p[2], color);
 }
 
 void CubicSegment::splitInThirds(EdgeSegment *&part1, EdgeSegment *&part2, EdgeSegment *&part3) const {
-    part1 = new CubicSegment(p[0], p[0] == p[1] ? p[0] : mix(p[0], p[1], 1/3.), mix(mix(p[0], p[1], 1/3.), mix(p[1], p[2], 1/3.), 1/3.), point(1/3.), color);
-    part2 = new CubicSegment(point(1/3.),
-        mix(mix(mix(p[0], p[1], 1/3.), mix(p[1], p[2], 1/3.), 1/3.), mix(mix(p[1], p[2], 1/3.), mix(p[2], p[3], 1/3.), 1/3.), 2/3.),
-        mix(mix(mix(p[0], p[1], 2/3.), mix(p[1], p[2], 2/3.), 2/3.), mix(mix(p[1], p[2], 2/3.), mix(p[2], p[3], 2/3.), 2/3.), 1/3.),
-        point(2/3.), color);
-    part3 = new CubicSegment(point(2/3.), mix(mix(p[1], p[2], 2/3.), mix(p[2], p[3], 2/3.), 2/3.), p[2] == p[3] ? p[3] : mix(p[2], p[3], 2/3.), p[3], color);
+    part1 = new CubicSegment(p[0], p[0] == p[1] ? p[0] : mix(p[0], p[1], real(1)/real(3)), mix(mix(p[0], p[1], real(1)/real(3)), mix(p[1], p[2], real(1)/real(3)), real(1)/real(3)), point(real(1)/real(3)), color);
+    part2 = new CubicSegment(point(real(1)/real(3)),
+        mix(mix(mix(p[0], p[1], real(1)/real(3)), mix(p[1], p[2], real(1)/real(3)), real(1)/real(3)), mix(mix(p[1], p[2], real(1)/real(3)), mix(p[2], p[3], real(1)/real(3)), real(1)/real(3)), real(2)/real(3)),
+        mix(mix(mix(p[0], p[1], real(2)/real(3)), mix(p[1], p[2], real(2)/real(3)), real(2)/real(3)), mix(mix(p[1], p[2], real(2)/real(3)), mix(p[2], p[3], real(2)/real(3)), real(2)/real(3)), real(1)/real(3)),
+        point(real(2)/real(3)), color);
+    part3 = new CubicSegment(point(real(2)/real(3)), mix(mix(p[1], p[2], real(2)/real(3)), mix(p[2], p[3], real(2)/real(3)), real(2)/real(3)), p[2] == p[3] ? p[3] : mix(p[2], p[3], real(2)/real(3)), p[3], color);
 }
 
 EdgeSegment *QuadraticSegment::convertToCubic() const {
-    return new CubicSegment(p[0], mix(p[0], p[1], 2/3.), mix(p[1], p[2], 1/3.), p[2], color);
+    return new CubicSegment(p[0], mix(p[0], p[1], real(2)/real(3)), mix(p[1], p[2], real(1)/real(3)), p[2], color);
 }
 
-void CubicSegment::deconverge(int param, double amount) {
+void CubicSegment::deconverge(int param, real amount) {
     Vector2 dir = direction(param);
     Vector2 normal = dir.getOrthonormal();
-    double h = dotProduct(directionChange(param)-dir, normal);
+    real h = dotProduct(directionChange(param)-dir, normal);
     switch (param) {
         case 0:
-            p[1] += amount*(dir+sign(h)*sqrt(fabs(h))*normal);
+            p[1] += amount*(dir+real(sign(h))*sqrt(fabs(h))*normal);
             break;
         case 1:
-            p[2] -= amount*(dir-sign(h)*sqrt(fabs(h))*normal);
+            p[2] -= amount*(dir-real(sign(h))*sqrt(fabs(h))*normal);
             break;
     }
 }

+ 29 - 28
core/edge-segments.h

@@ -1,6 +1,7 @@
 
 #pragma once
 
+#include "types.h"
 #include "Vector2.hpp"
 #include "SignedDistance.hpp"
 #include "EdgeColor.h"
@@ -22,19 +23,19 @@ public:
     /// Returns the array of control points.
     virtual const Point2 *controlPoints() const = 0;
     /// Returns the point on the edge specified by the parameter (between 0 and 1).
-    virtual Point2 point(double param) const = 0;
+    virtual Point2 point(real param) const = 0;
     /// Returns the direction the edge has at the point specified by the parameter.
-    virtual Vector2 direction(double param) const = 0;
+    virtual Vector2 direction(real param) const = 0;
     /// Returns the change of direction (second derivative) at the point specified by the parameter.
-    virtual Vector2 directionChange(double param) const = 0;
+    virtual Vector2 directionChange(real param) const = 0;
     /// Returns the minimum signed distance between origin and the edge.
-    virtual SignedDistance signedDistance(Point2 origin, double &param) const = 0;
+    virtual SignedDistance signedDistance(Point2 origin, real &param) const = 0;
     /// Converts a previously retrieved signed distance from origin to pseudo-distance.
-    virtual void distanceToPseudoDistance(SignedDistance &distance, Point2 origin, double param) const;
+    virtual void distanceToPseudoDistance(SignedDistance &distance, Point2 origin, real param) const;
     /// Outputs a list of (at most three) intersections (their X coordinates) with an infinite horizontal scanline at y and returns how many there are.
-    virtual int scanlineIntersections(double x[3], int dy[3], double y) const = 0;
+    virtual int scanlineIntersections(real x[3], int dy[3], real y) const = 0;
     /// Adjusts the bounding box to fit the edge segment.
-    virtual void bound(double &l, double &b, double &r, double &t) const = 0;
+    virtual void bound(real &l, real &b, real &r, real &t) const = 0;
 
     /// Reverses the edge (swaps its start point and end point).
     virtual void reverse() = 0;
@@ -61,13 +62,13 @@ public:
     LinearSegment *clone() const;
     int type() const;
     const Point2 *controlPoints() const;
-    Point2 point(double param) const;
-    Vector2 direction(double param) const;
-    Vector2 directionChange(double param) const;
-    double length() const;
-    SignedDistance signedDistance(Point2 origin, double &param) const;
-    int scanlineIntersections(double x[3], int dy[3], double y) const;
-    void bound(double &l, double &b, double &r, double &t) const;
+    Point2 point(real param) const;
+    Vector2 direction(real param) const;
+    Vector2 directionChange(real param) const;
+    real length() const;
+    SignedDistance signedDistance(Point2 origin, real &param) const;
+    int scanlineIntersections(real x[3], int dy[3], real y) const;
+    void bound(real &l, real &b, real &r, real &t) const;
 
     void reverse();
     void moveStartPoint(Point2 to);
@@ -90,13 +91,13 @@ public:
     QuadraticSegment *clone() const;
     int type() const;
     const Point2 *controlPoints() const;
-    Point2 point(double param) const;
-    Vector2 direction(double param) const;
-    Vector2 directionChange(double param) const;
-    double length() const;
-    SignedDistance signedDistance(Point2 origin, double &param) const;
-    int scanlineIntersections(double x[3], int dy[3], double y) const;
-    void bound(double &l, double &b, double &r, double &t) const;
+    Point2 point(real param) const;
+    Vector2 direction(real param) const;
+    Vector2 directionChange(real param) const;
+    real length() const;
+    SignedDistance signedDistance(Point2 origin, real &param) const;
+    int scanlineIntersections(real x[3], int dy[3], real y) const;
+    void bound(real &l, real &b, real &r, real &t) const;
 
     void reverse();
     void moveStartPoint(Point2 to);
@@ -121,19 +122,19 @@ public:
     CubicSegment *clone() const;
     int type() const;
     const Point2 *controlPoints() const;
-    Point2 point(double param) const;
-    Vector2 direction(double param) const;
-    Vector2 directionChange(double param) const;
-    SignedDistance signedDistance(Point2 origin, double &param) const;
-    int scanlineIntersections(double x[3], int dy[3], double y) const;
-    void bound(double &l, double &b, double &r, double &t) const;
+    Point2 point(real param) const;
+    Vector2 direction(real param) const;
+    Vector2 directionChange(real param) const;
+    SignedDistance signedDistance(Point2 origin, real &param) const;
+    int scanlineIntersections(real x[3], int dy[3], real y) const;
+    void bound(real &l, real &b, real &r, real &t) const;
 
     void reverse();
     void moveStartPoint(Point2 to);
     void moveEndPoint(Point2 to);
     void splitInThirds(EdgeSegment *&part1, EdgeSegment *&part2, EdgeSegment *&part3) const;
 
-    void deconverge(int param, double amount);
+    void deconverge(int param, real amount);
 
 };
 

+ 36 - 36
core/edge-selectors.cpp

@@ -6,20 +6,20 @@
 
 namespace msdfgen {
 
-#define DISTANCE_DELTA_FACTOR 1.001
+#define DISTANCE_DELTA_FACTOR ::msdfgen::real(1.001)
 
 TrueDistanceSelector::EdgeCache::EdgeCache() : absDistance(0) { }
 
 void TrueDistanceSelector::reset(const Point2 &p) {
-    double delta = DISTANCE_DELTA_FACTOR*(p-this->p).length();
-    minDistance.distance += nonZeroSign(minDistance.distance)*delta;
+    real delta = DISTANCE_DELTA_FACTOR*(p-this->p).length();
+    minDistance.distance += real(nonZeroSign(minDistance.distance))*delta;
     this->p = p;
 }
 
 void TrueDistanceSelector::addEdge(EdgeCache &cache, const EdgeSegment *prevEdge, const EdgeSegment *edge, const EdgeSegment *nextEdge) {
-    double delta = DISTANCE_DELTA_FACTOR*(p-cache.point).length();
+    real delta = DISTANCE_DELTA_FACTOR*(p-cache.point).length();
     if (cache.absDistance-delta <= fabs(minDistance.distance)) {
-        double dummy;
+        real dummy;
         SignedDistance distance = edge->signedDistance(p, dummy);
         if (distance < minDistance)
             minDistance = distance;
@@ -39,10 +39,10 @@ TrueDistanceSelector::DistanceType TrueDistanceSelector::distance() const {
 
 PseudoDistanceSelectorBase::EdgeCache::EdgeCache() : absDistance(0), aDomainDistance(0), bDomainDistance(0), aPseudoDistance(0), bPseudoDistance(0) { }
 
-bool PseudoDistanceSelectorBase::getPseudoDistance(double &distance, const Vector2 &ep, const Vector2 &edgeDir) {
-    double ts = dotProduct(ep, edgeDir);
-    if (ts > 0) {
-        double pseudoDistance = crossProduct(ep, edgeDir);
+bool PseudoDistanceSelectorBase::getPseudoDistance(real &distance, const Vector2 &ep, const Vector2 &edgeDir) {
+    real ts = dotProduct(ep, edgeDir);
+    if (ts > real(0)) {
+        real pseudoDistance = crossProduct(ep, edgeDir);
         if (fabs(pseudoDistance) < fabs(distance)) {
             distance = pseudoDistance;
             return true;
@@ -53,8 +53,8 @@ bool PseudoDistanceSelectorBase::getPseudoDistance(double &distance, const Vecto
 
 PseudoDistanceSelectorBase::PseudoDistanceSelectorBase() : minNegativePseudoDistance(-fabs(minTrueDistance.distance)), minPositivePseudoDistance(fabs(minTrueDistance.distance)), nearEdge(NULL), nearEdgeParam(0) { }
 
-void PseudoDistanceSelectorBase::reset(double delta) {
-    minTrueDistance.distance += nonZeroSign(minTrueDistance.distance)*delta;
+void PseudoDistanceSelectorBase::reset(real delta) {
+    minTrueDistance.distance += real(nonZeroSign(minTrueDistance.distance))*delta;
     minNegativePseudoDistance = -fabs(minTrueDistance.distance);
     minPositivePseudoDistance = fabs(minTrueDistance.distance);
     nearEdge = NULL;
@@ -62,23 +62,23 @@ void PseudoDistanceSelectorBase::reset(double delta) {
 }
 
 bool PseudoDistanceSelectorBase::isEdgeRelevant(const EdgeCache &cache, const EdgeSegment *edge, const Point2 &p) const {
-    double delta = DISTANCE_DELTA_FACTOR*(p-cache.point).length();
+    real delta = DISTANCE_DELTA_FACTOR*(p-cache.point).length();
     return (
         cache.absDistance-delta <= fabs(minTrueDistance.distance) ||
         fabs(cache.aDomainDistance) < delta ||
         fabs(cache.bDomainDistance) < delta ||
-        (cache.aDomainDistance > 0 && (cache.aPseudoDistance < 0 ?
+        (cache.aDomainDistance > real(0) && (cache.aPseudoDistance < real(0) ?
             cache.aPseudoDistance+delta >= minNegativePseudoDistance :
             cache.aPseudoDistance-delta <= minPositivePseudoDistance
         )) ||
-        (cache.bDomainDistance > 0 && (cache.bPseudoDistance < 0 ?
+        (cache.bDomainDistance > real(0) && (cache.bPseudoDistance < real(0) ?
             cache.bPseudoDistance+delta >= minNegativePseudoDistance :
             cache.bPseudoDistance-delta <= minPositivePseudoDistance
         ))
     );
 }
 
-void PseudoDistanceSelectorBase::addEdgeTrueDistance(const EdgeSegment *edge, const SignedDistance &distance, double param) {
+void PseudoDistanceSelectorBase::addEdgeTrueDistance(const EdgeSegment *edge, const SignedDistance &distance, real param) {
     if (distance < minTrueDistance) {
         minTrueDistance = distance;
         nearEdge = edge;
@@ -86,10 +86,10 @@ void PseudoDistanceSelectorBase::addEdgeTrueDistance(const EdgeSegment *edge, co
     }
 }
 
-void PseudoDistanceSelectorBase::addEdgePseudoDistance(double distance) {
-    if (distance <= 0 && distance > minNegativePseudoDistance)
+void PseudoDistanceSelectorBase::addEdgePseudoDistance(real distance) {
+    if (distance <= real(0) && distance > minNegativePseudoDistance)
         minNegativePseudoDistance = distance;
-    if (distance >= 0 && distance < minPositivePseudoDistance)
+    if (distance >= real(0) && distance < minPositivePseudoDistance)
         minPositivePseudoDistance = distance;
 }
 
@@ -105,8 +105,8 @@ void PseudoDistanceSelectorBase::merge(const PseudoDistanceSelectorBase &other)
         minPositivePseudoDistance = other.minPositivePseudoDistance;
 }
 
-double PseudoDistanceSelectorBase::computeDistance(const Point2 &p) const {
-    double minDistance = minTrueDistance.distance < 0 ? minNegativePseudoDistance : minPositivePseudoDistance;
+real PseudoDistanceSelectorBase::computeDistance(const Point2 &p) const {
+    real minDistance = minTrueDistance.distance < real(0) ? minNegativePseudoDistance : minPositivePseudoDistance;
     if (nearEdge) {
         SignedDistance distance = minTrueDistance;
         nearEdge->distanceToPseudoDistance(distance, p, nearEdgeParam);
@@ -121,14 +121,14 @@ SignedDistance PseudoDistanceSelectorBase::trueDistance() const {
 }
 
 void PseudoDistanceSelector::reset(const Point2 &p) {
-    double delta = DISTANCE_DELTA_FACTOR*(p-this->p).length();
+    real delta = DISTANCE_DELTA_FACTOR*(p-this->p).length();
     PseudoDistanceSelectorBase::reset(delta);
     this->p = p;
 }
 
 void PseudoDistanceSelector::addEdge(EdgeCache &cache, const EdgeSegment *prevEdge, const EdgeSegment *edge, const EdgeSegment *nextEdge) {
     if (isEdgeRelevant(cache, edge, p)) {
-        double param;
+        real param;
         SignedDistance distance = edge->signedDistance(p, param);
         addEdgeTrueDistance(edge, distance, param);
         cache.point = p;
@@ -140,16 +140,16 @@ void PseudoDistanceSelector::addEdge(EdgeCache &cache, const EdgeSegment *prevEd
         Vector2 bDir = edge->direction(1).normalize(true);
         Vector2 prevDir = prevEdge->direction(1).normalize(true);
         Vector2 nextDir = nextEdge->direction(0).normalize(true);
-        double add = dotProduct(ap, (prevDir+aDir).normalize(true));
-        double bdd = -dotProduct(bp, (bDir+nextDir).normalize(true));
-        if (add > 0) {
-            double pd = distance.distance;
+        real add = dotProduct(ap, (prevDir+aDir).normalize(true));
+        real bdd = -dotProduct(bp, (bDir+nextDir).normalize(true));
+        if (add > real(0)) {
+            real pd = distance.distance;
             if (getPseudoDistance(pd, ap, -aDir))
                 addEdgePseudoDistance(pd = -pd);
             cache.aPseudoDistance = pd;
         }
-        if (bdd > 0) {
-            double pd = distance.distance;
+        if (bdd > real(0)) {
+            real pd = distance.distance;
             if (getPseudoDistance(pd, bp, bDir))
                 addEdgePseudoDistance(pd);
             cache.bPseudoDistance = pd;
@@ -164,7 +164,7 @@ PseudoDistanceSelector::DistanceType PseudoDistanceSelector::distance() const {
 }
 
 void MultiDistanceSelector::reset(const Point2 &p) {
-    double delta = DISTANCE_DELTA_FACTOR*(p-this->p).length();
+    real delta = DISTANCE_DELTA_FACTOR*(p-this->p).length();
     r.reset(delta);
     g.reset(delta);
     b.reset(delta);
@@ -177,7 +177,7 @@ void MultiDistanceSelector::addEdge(EdgeCache &cache, const EdgeSegment *prevEdg
         (edge->color&GREEN && g.isEdgeRelevant(cache, edge, p)) ||
         (edge->color&BLUE && b.isEdgeRelevant(cache, edge, p))
     ) {
-        double param;
+        real param;
         SignedDistance distance = edge->signedDistance(p, param);
         if (edge->color&RED)
             r.addEdgeTrueDistance(edge, distance, param);
@@ -194,10 +194,10 @@ void MultiDistanceSelector::addEdge(EdgeCache &cache, const EdgeSegment *prevEdg
         Vector2 bDir = edge->direction(1).normalize(true);
         Vector2 prevDir = prevEdge->direction(1).normalize(true);
         Vector2 nextDir = nextEdge->direction(0).normalize(true);
-        double add = dotProduct(ap, (prevDir+aDir).normalize(true));
-        double bdd = -dotProduct(bp, (bDir+nextDir).normalize(true));
-        if (add > 0) {
-            double pd = distance.distance;
+        real add = dotProduct(ap, (prevDir+aDir).normalize(true));
+        real bdd = -dotProduct(bp, (bDir+nextDir).normalize(true));
+        if (add > real(0)) {
+            real pd = distance.distance;
             if (PseudoDistanceSelectorBase::getPseudoDistance(pd, ap, -aDir)) {
                 pd = -pd;
                 if (edge->color&RED)
@@ -209,8 +209,8 @@ void MultiDistanceSelector::addEdge(EdgeCache &cache, const EdgeSegment *prevEdg
             }
             cache.aPseudoDistance = pd;
         }
-        if (bdd > 0) {
-            double pd = distance.distance;
+        if (bdd > real(0)) {
+            real pd = distance.distance;
             if (PseudoDistanceSelectorBase::getPseudoDistance(pd, bp, bDir)) {
                 if (edge->color&RED)
                     r.addEdgePseudoDistance(pd);

+ 17 - 16
core/edge-selectors.h

@@ -1,6 +1,7 @@
 
 #pragma once
 
+#include "types.h"
 #include "Vector2.hpp"
 #include "SignedDistance.hpp"
 #include "edge-segments.h"
@@ -8,21 +9,21 @@
 namespace msdfgen {
 
 struct MultiDistance {
-    double r, g, b;
+    real r, g, b;
 };
 struct MultiAndTrueDistance : MultiDistance {
-    double a;
+    real a;
 };
 
 /// Selects the nearest edge by its true distance.
 class TrueDistanceSelector {
 
 public:
-    typedef double DistanceType;
+    typedef real DistanceType;
 
     struct EdgeCache {
         Point2 point;
-        double absDistance;
+        real absDistance;
 
         EdgeCache();
     };
@@ -43,30 +44,30 @@ class PseudoDistanceSelectorBase {
 public:
     struct EdgeCache {
         Point2 point;
-        double absDistance;
-        double aDomainDistance, bDomainDistance;
-        double aPseudoDistance, bPseudoDistance;
+        real absDistance;
+        real aDomainDistance, bDomainDistance;
+        real aPseudoDistance, bPseudoDistance;
 
         EdgeCache();
     };
 
-    static bool getPseudoDistance(double &distance, const Vector2 &ep, const Vector2 &edgeDir);
+    static bool getPseudoDistance(real &distance, const Vector2 &ep, const Vector2 &edgeDir);
 
     PseudoDistanceSelectorBase();
-    void reset(double delta);
+    void reset(real delta);
     bool isEdgeRelevant(const EdgeCache &cache, const EdgeSegment *edge, const Point2 &p) const;
-    void addEdgeTrueDistance(const EdgeSegment *edge, const SignedDistance &distance, double param);
-    void addEdgePseudoDistance(double distance);
+    void addEdgeTrueDistance(const EdgeSegment *edge, const SignedDistance &distance, real param);
+    void addEdgePseudoDistance(real distance);
     void merge(const PseudoDistanceSelectorBase &other);
-    double computeDistance(const Point2 &p) const;
+    real computeDistance(const Point2 &p) const;
     SignedDistance trueDistance() const;
 
 private:
     SignedDistance minTrueDistance;
-    double minNegativePseudoDistance;
-    double minPositivePseudoDistance;
+    real minNegativePseudoDistance;
+    real minPositivePseudoDistance;
     const EdgeSegment *nearEdge;
-    double nearEdgeParam;
+    real nearEdgeParam;
 
 };
 
@@ -74,7 +75,7 @@ private:
 class PseudoDistanceSelector : public PseudoDistanceSelectorBase {
 
 public:
-    typedef double DistanceType;
+    typedef real DistanceType;
 
     void reset(const Point2 &p);
     void addEdge(EdgeCache &cache, const EdgeSegment *prevEdge, const EdgeSegment *edge, const EdgeSegment *nextEdge);

+ 34 - 32
core/equation-solver.cpp

@@ -4,70 +4,72 @@
 #define _USE_MATH_DEFINES
 #include <cmath>
 
+#define LARGE_RATIO ::msdfgen::real(1e10)
+
 #ifndef MSDFGEN_CUBE_ROOT
-#define MSDFGEN_CUBE_ROOT(x) pow((x), 1/3.)
+#define MSDFGEN_CUBE_ROOT(x) pow((x), ::msdfgen::real(1)/::msdfgen::real(3))
 #endif
 
 namespace msdfgen {
 
-int solveQuadratic(double x[2], double a, double b, double c) {
+int solveQuadratic(real x[2], real a, real b, real c) {
     // a == 0 -> linear equation
-    if (a == 0 || fabs(b) > 1e12*fabs(a)) {
+    if (a == real(0) || fabs(b) > LARGE_RATIO*fabs(a)) {
         // a == 0, b == 0 -> no solution
-        if (b == 0) {
-            if (c == 0)
+        if (b == real(0)) {
+            if (c == real(0))
                 return -1; // 0 == 0
             return 0;
         }
         x[0] = -c/b;
         return 1;
     }
-    double dscr = b*b-4*a*c;
-    if (dscr > 0) {
+    real dscr = b*b-real(4)*a*c;
+    if (dscr > real(0)) {
         dscr = sqrt(dscr);
-        x[0] = (-b+dscr)/(2*a);
-        x[1] = (-b-dscr)/(2*a);
+        x[0] = (-b+dscr)/(a+a);
+        x[1] = (-b-dscr)/(a+a);
         return 2;
     } else if (dscr == 0) {
-        x[0] = -b/(2*a);
+        x[0] = -b/(a+a);
         return 1;
     } else
         return 0;
 }
 
-static int solveCubicNormed(double x[3], double a, double b, double c) {
-    double a2 = a*a;
-    double q = 1/9.*(a2-3*b);
-    double r = 1/54.*(a*(2*a2-9*b)+27*c);
-    double r2 = r*r;
-    double q3 = q*q*q;
-    a *= 1/3.;
+static int solveCubicNormed(real x[3], real a, real b, real c) {
+    real a2 = a*a;
+    real q = real(1)/real(9)*(a2-real(3)*b);
+    real r = real(1)/real(54)*(a*(real(2)*a2-real(9)*b)+real(27)*c);
+    real r2 = r*r;
+    real q3 = q*q*q;
+    a *= real(1)/real(3);
     if (r2 < q3) {
-        double t = r/sqrt(q3);
-        if (t < -1) t = -1;
-        if (t > 1) t = 1;
-        t = acos(t);
-        q = -2*sqrt(q);
-        x[0] = q*cos(1/3.*t)-a;
-        x[1] = q*cos(1/3.*(t+2*M_PI))-a;
-        x[2] = q*cos(1/3.*(t-2*M_PI))-a;
+        real t = r/sqrt(q3);
+        if (t < real(-1)) t = -1;
+        if (t > real(1)) t = 1;
+        t = real(1)/real(3)*acos(t);
+        q = real(-2)*sqrt(q);
+        x[0] = q*cos(t)-a;
+        x[1] = q*cos(t+real(2)/real(3)*real(M_PI))-a;
+        x[2] = q*cos(t-real(2)/real(3)*real(M_PI))-a;
         return 3;
     } else {
-        double u = (r < 0 ? 1 : -1)*MSDFGEN_CUBE_ROOT(fabs(r)+sqrt(r2-q3));
-        double v = u == 0 ? 0 : q/u;
+        real u = (r < real(0) ? real(1) : real(-1))*MSDFGEN_CUBE_ROOT(fabs(r)+sqrt(r2-q3));
+        real v = u == real(0) ? real(0) : q/u;
         x[0] = (u+v)-a;
-        if (u == v || fabs(u-v) < 1e-12*fabs(u+v)) {
-            x[1] = -.5*(u+v)-a;
+        if (u == v || LARGE_RATIO*fabs(u-v) < fabs(u+v)) {
+            x[1] = real(-.5)*(u+v)-a;
             return 2;
         }
         return 1;
     }
 }
 
-int solveCubic(double x[3], double a, double b, double c, double d) {
+int solveCubic(real x[3], real a, real b, real c, real d) {
     if (a != 0) {
-        double bn = b/a;
-        if (fabs(bn) < 1e6) // Above this ratio, the numerical error gets larger than if we treated a as zero
+        real bn = b/a;
+        if (bn*bn < LARGE_RATIO) // Above this ratio, the numerical error gets larger than if we treated a as zero
             return solveCubicNormed(x, bn, c/a, d/a);
     }
     return solveQuadratic(x, b, c, d);

+ 4 - 2
core/equation-solver.h

@@ -1,12 +1,14 @@
 
 #pragma once
 
+#include "types.h"
+
 namespace msdfgen {
 
 // ax^2 + bx + c = 0
-int solveQuadratic(double x[2], double a, double b, double c);
+int solveQuadratic(real x[2], real a, real b, real c);
 
 // ax^3 + bx^2 + cx + d = 0
-int solveCubic(double x[3], double a, double b, double c, double d);
+int solveCubic(real x[3], real a, real b, real c, real d);
 
 }

+ 6 - 6
core/generator-config.h

@@ -2,7 +2,7 @@
 #pragma once
 
 #include <cstddef>
-#include "BitmapRef.hpp"
+#include "types.h"
 
 #ifndef MSDFGEN_PUBLIC
 #define MSDFGEN_PUBLIC // for DLL import/export
@@ -13,9 +13,9 @@ namespace msdfgen {
 /// The configuration of the MSDF error correction pass.
 struct ErrorCorrectionConfig {
     /// The default value of minDeviationRatio.
-    static MSDFGEN_PUBLIC const double defaultMinDeviationRatio;
+    static MSDFGEN_PUBLIC const real defaultMinDeviationRatio;
     /// The default value of minImproveRatio.
-    static MSDFGEN_PUBLIC const double defaultMinImproveRatio;
+    static MSDFGEN_PUBLIC const real defaultMinImproveRatio;
 
     /// Mode of operation.
     enum Mode {
@@ -38,13 +38,13 @@ struct ErrorCorrectionConfig {
         ALWAYS_CHECK_DISTANCE
     } distanceCheckMode;
     /// The minimum ratio between the actual and maximum expected distance delta to be considered an error.
-    double minDeviationRatio;
+    real minDeviationRatio;
     /// The minimum ratio between the pre-correction distance error and the post-correction distance error. Has no effect for DO_NOT_CHECK_DISTANCE.
-    double minImproveRatio;
+    real minImproveRatio;
     /// An optional buffer to avoid dynamic allocation. Must have at least as many bytes as the MSDF has pixels.
     byte *buffer;
 
-    inline explicit ErrorCorrectionConfig(Mode mode = EDGE_PRIORITY, DistanceCheckMode distanceCheckMode = CHECK_DISTANCE_AT_EDGE, double minDeviationRatio = defaultMinDeviationRatio, double minImproveRatio = defaultMinImproveRatio, byte *buffer = NULL) : mode(mode), distanceCheckMode(distanceCheckMode), minDeviationRatio(minDeviationRatio), minImproveRatio(minImproveRatio), buffer(buffer) { }
+    inline explicit ErrorCorrectionConfig(Mode mode = EDGE_PRIORITY, DistanceCheckMode distanceCheckMode = CHECK_DISTANCE_AT_EDGE, real minDeviationRatio = defaultMinDeviationRatio, real minImproveRatio = defaultMinImproveRatio, byte *buffer = NULL) : mode(mode), distanceCheckMode(distanceCheckMode), minDeviationRatio(minDeviationRatio), minImproveRatio(minImproveRatio), buffer(buffer) { }
 };
 
 /// The configuration of the distance field generator algorithm.

+ 9 - 9
core/msdf-error-correction.cpp

@@ -10,7 +10,7 @@
 namespace msdfgen {
 
 template <int N>
-static void msdfErrorCorrectionInner(const BitmapRef<float, N> &sdf, const Shape &shape, const Projection &projection, double range, const MSDFGeneratorConfig &config) {
+static void msdfErrorCorrectionInner(const BitmapRef<float, N> &sdf, const Shape &shape, const Projection &projection, real range, const MSDFGeneratorConfig &config) {
     if (config.errorCorrection.mode == ErrorCorrectionConfig::DISABLED)
         return;
     Bitmap<byte, 1> stencilBuffer;
@@ -49,7 +49,7 @@ static void msdfErrorCorrectionInner(const BitmapRef<float, N> &sdf, const Shape
 }
 
 template <int N>
-static void msdfErrorCorrectionShapeless(const BitmapRef<float, N> &sdf, const Projection &projection, double range, double minDeviationRatio, bool protectAll) {
+static void msdfErrorCorrectionShapeless(const BitmapRef<float, N> &sdf, const Projection &projection, real range, real minDeviationRatio, bool protectAll) {
     Bitmap<byte, 1> stencilBuffer(sdf.width, sdf.height);
     MSDFErrorCorrection ec(stencilBuffer, projection, range);
     ec.setMinDeviationRatio(minDeviationRatio);
@@ -59,31 +59,31 @@ static void msdfErrorCorrectionShapeless(const BitmapRef<float, N> &sdf, const P
     ec.apply(sdf);
 }
 
-void msdfErrorCorrection(const BitmapRef<float, 3> &sdf, const Shape &shape, const Projection &projection, double range, const MSDFGeneratorConfig &config) {
+void msdfErrorCorrection(const BitmapRef<float, 3> &sdf, const Shape &shape, const Projection &projection, real range, const MSDFGeneratorConfig &config) {
     msdfErrorCorrectionInner(sdf, shape, projection, range, config);
 }
-void msdfErrorCorrection(const BitmapRef<float, 4> &sdf, const Shape &shape, const Projection &projection, double range, const MSDFGeneratorConfig &config) {
+void msdfErrorCorrection(const BitmapRef<float, 4> &sdf, const Shape &shape, const Projection &projection, real range, const MSDFGeneratorConfig &config) {
     msdfErrorCorrectionInner(sdf, shape, projection, range, config);
 }
 
-void msdfFastDistanceErrorCorrection(const BitmapRef<float, 3> &sdf, const Projection &projection, double range, double minDeviationRatio) {
+void msdfFastDistanceErrorCorrection(const BitmapRef<float, 3> &sdf, const Projection &projection, real range, real minDeviationRatio) {
     msdfErrorCorrectionShapeless(sdf, projection, range, minDeviationRatio, false);
 }
-void msdfFastDistanceErrorCorrection(const BitmapRef<float, 4> &sdf, const Projection &projection, double range, double minDeviationRatio) {
+void msdfFastDistanceErrorCorrection(const BitmapRef<float, 4> &sdf, const Projection &projection, real range, real minDeviationRatio) {
     msdfErrorCorrectionShapeless(sdf, projection, range, minDeviationRatio, false);
 }
 
-void msdfFastEdgeErrorCorrection(const BitmapRef<float, 3> &sdf, const Projection &projection, double range, double minDeviationRatio) {
+void msdfFastEdgeErrorCorrection(const BitmapRef<float, 3> &sdf, const Projection &projection, real range, real minDeviationRatio) {
     msdfErrorCorrectionShapeless(sdf, projection, range, minDeviationRatio, true);
 }
-void msdfFastEdgeErrorCorrection(const BitmapRef<float, 4> &sdf, const Projection &projection, double range, double minDeviationRatio) {
+void msdfFastEdgeErrorCorrection(const BitmapRef<float, 4> &sdf, const Projection &projection, real range, real minDeviationRatio) {
     msdfErrorCorrectionShapeless(sdf, projection, range, minDeviationRatio, true);
 }
 
 
 // Legacy version
 
-inline static bool detectClash(const float *a, const float *b, double threshold) {
+inline static bool detectClash(const float *a, const float *b, real threshold) {
     // Sort channels so that pairs (a0, b0), (a1, b1), (a2, b2) go from biggest to smallest absolute difference
     float a0 = a[0], a1 = a[1], a2 = a[2];
     float b0 = b[0], b1 = b[1], b2 = b[2];

+ 7 - 6
core/msdf-error-correction.h

@@ -1,6 +1,7 @@
 
 #pragma once
 
+#include "types.h"
 #include "Vector2.hpp"
 #include "Projection.h"
 #include "Shape.h"
@@ -10,16 +11,16 @@
 namespace msdfgen {
 
 /// Predicts potential artifacts caused by the interpolation of the MSDF and corrects them by converting nearby texels to single-channel.
-void msdfErrorCorrection(const BitmapRef<float, 3> &sdf, const Shape &shape, const Projection &projection, double range, const MSDFGeneratorConfig &config = MSDFGeneratorConfig());
-void msdfErrorCorrection(const BitmapRef<float, 4> &sdf, const Shape &shape, const Projection &projection, double range, const MSDFGeneratorConfig &config = MSDFGeneratorConfig());
+void msdfErrorCorrection(const BitmapRef<float, 3> &sdf, const Shape &shape, const Projection &projection, real range, const MSDFGeneratorConfig &config = MSDFGeneratorConfig());
+void msdfErrorCorrection(const BitmapRef<float, 4> &sdf, const Shape &shape, const Projection &projection, real range, const MSDFGeneratorConfig &config = MSDFGeneratorConfig());
 
 /// Applies the simplified error correction to all discontiunous distances (INDISCRIMINATE mode). Does not need shape or translation.
-void msdfFastDistanceErrorCorrection(const BitmapRef<float, 3> &sdf, const Projection &projection, double range, double minDeviationRatio = ErrorCorrectionConfig::defaultMinDeviationRatio);
-void msdfFastDistanceErrorCorrection(const BitmapRef<float, 4> &sdf, const Projection &projection, double range, double minDeviationRatio = ErrorCorrectionConfig::defaultMinDeviationRatio);
+void msdfFastDistanceErrorCorrection(const BitmapRef<float, 3> &sdf, const Projection &projection, real range, real minDeviationRatio = ErrorCorrectionConfig::defaultMinDeviationRatio);
+void msdfFastDistanceErrorCorrection(const BitmapRef<float, 4> &sdf, const Projection &projection, real range, real minDeviationRatio = ErrorCorrectionConfig::defaultMinDeviationRatio);
 
 /// Applies the simplified error correction to edges only (EDGE_ONLY mode). Does not need shape or translation.
-void msdfFastEdgeErrorCorrection(const BitmapRef<float, 3> &sdf, const Projection &projection, double range, double minDeviationRatio = ErrorCorrectionConfig::defaultMinDeviationRatio);
-void msdfFastEdgeErrorCorrection(const BitmapRef<float, 4> &sdf, const Projection &projection, double range, double minDeviationRatio = ErrorCorrectionConfig::defaultMinDeviationRatio);
+void msdfFastEdgeErrorCorrection(const BitmapRef<float, 3> &sdf, const Projection &projection, real range, real minDeviationRatio = ErrorCorrectionConfig::defaultMinDeviationRatio);
+void msdfFastEdgeErrorCorrection(const BitmapRef<float, 4> &sdf, const Projection &projection, real range, real minDeviationRatio = ErrorCorrectionConfig::defaultMinDeviationRatio);
 
 /// The original version of the error correction algorithm.
 void msdfErrorCorrection_legacy(const BitmapRef<float, 3> &output, const Vector2 &threshold);

+ 50 - 50
core/msdfgen.cpp

@@ -13,45 +13,45 @@ template <typename DistanceType>
 class DistancePixelConversion;
 
 template <>
-class DistancePixelConversion<double> {
-    double invRange;
+class DistancePixelConversion<real> {
+    real invRange;
 public:
     typedef BitmapRef<float, 1> BitmapRefType;
-    inline explicit DistancePixelConversion(double range) : invRange(1/range) { }
-    inline void operator()(float *pixels, double distance) const {
-        *pixels = float(invRange*distance+.5);
+    inline explicit DistancePixelConversion(real range) : invRange(real(1)/range) { }
+    inline void operator()(float *pixels, real distance) const {
+        *pixels = float(invRange*distance+real(.5));
     }
 };
 
 template <>
 class DistancePixelConversion<MultiDistance> {
-    double invRange;
+    real invRange;
 public:
     typedef BitmapRef<float, 3> BitmapRefType;
-    inline explicit DistancePixelConversion(double range) : invRange(1/range) { }
+    inline explicit DistancePixelConversion(real range) : invRange(real(1)/range) { }
     inline void operator()(float *pixels, const MultiDistance &distance) const {
-        pixels[0] = float(invRange*distance.r+.5);
-        pixels[1] = float(invRange*distance.g+.5);
-        pixels[2] = float(invRange*distance.b+.5);
+        pixels[0] = float(invRange*distance.r+real(.5));
+        pixels[1] = float(invRange*distance.g+real(.5));
+        pixels[2] = float(invRange*distance.b+real(.5));
     }
 };
 
 template <>
 class DistancePixelConversion<MultiAndTrueDistance> {
-    double invRange;
+    real invRange;
 public:
     typedef BitmapRef<float, 4> BitmapRefType;
-    inline explicit DistancePixelConversion(double range) : invRange(1/range) { }
+    inline explicit DistancePixelConversion(real range) : invRange(real(1)/range) { }
     inline void operator()(float *pixels, const MultiAndTrueDistance &distance) const {
-        pixels[0] = float(invRange*distance.r+.5);
-        pixels[1] = float(invRange*distance.g+.5);
-        pixels[2] = float(invRange*distance.b+.5);
-        pixels[3] = float(invRange*distance.a+.5);
+        pixels[0] = float(invRange*distance.r+real(.5));
+        pixels[1] = float(invRange*distance.g+real(.5));
+        pixels[2] = float(invRange*distance.b+real(.5));
+        pixels[3] = float(invRange*distance.a+real(.5));
     }
 };
 
 template <class ContourCombiner>
-void generateDistanceField(const typename DistancePixelConversion<typename ContourCombiner::DistanceType>::BitmapRefType &output, const Shape &shape, const Projection &projection, double range) {
+void generateDistanceField(const typename DistancePixelConversion<typename ContourCombiner::DistanceType>::BitmapRefType &output, const Shape &shape, const Projection &projection, real range) {
     DistancePixelConversion<typename ContourCombiner::DistanceType> distancePixelConversion(range);
 #ifdef MSDFGEN_USE_OPENMP
     #pragma omp parallel
@@ -66,7 +66,7 @@ void generateDistanceField(const typename DistancePixelConversion<typename Conto
             int row = shape.inverseYAxis ? output.height-y-1 : y;
             for (int col = 0; col < output.width; ++col) {
                 int x = rightToLeft ? output.width-col-1 : col;
-                Point2 p = projection.unproject(Point2(x+.5, y+.5));
+                Point2 p = projection.unproject(Point2(real(x)+real(.5), real(y)+real(.5)));
                 typename ContourCombiner::DistanceType distance = distanceFinder.distance(p);
                 distancePixelConversion(output(x, row), distance);
             }
@@ -75,21 +75,21 @@ void generateDistanceField(const typename DistancePixelConversion<typename Conto
     }
 }
 
-void generateSDF(const BitmapRef<float, 1> &output, const Shape &shape, const Projection &projection, double range, const GeneratorConfig &config) {
+void generateSDF(const BitmapRef<float, 1> &output, const Shape &shape, const Projection &projection, real range, const GeneratorConfig &config) {
     if (config.overlapSupport)
         generateDistanceField<OverlappingContourCombiner<TrueDistanceSelector> >(output, shape, projection, range);
     else
         generateDistanceField<SimpleContourCombiner<TrueDistanceSelector> >(output, shape, projection, range);
 }
 
-void generatePseudoSDF(const BitmapRef<float, 1> &output, const Shape &shape, const Projection &projection, double range, const GeneratorConfig &config) {
+void generatePseudoSDF(const BitmapRef<float, 1> &output, const Shape &shape, const Projection &projection, real range, const GeneratorConfig &config) {
     if (config.overlapSupport)
         generateDistanceField<OverlappingContourCombiner<PseudoDistanceSelector> >(output, shape, projection, range);
     else
         generateDistanceField<SimpleContourCombiner<PseudoDistanceSelector> >(output, shape, projection, range);
 }
 
-void generateMSDF(const BitmapRef<float, 3> &output, const Shape &shape, const Projection &projection, double range, const MSDFGeneratorConfig &config) {
+void generateMSDF(const BitmapRef<float, 3> &output, const Shape &shape, const Projection &projection, real range, const MSDFGeneratorConfig &config) {
     if (config.overlapSupport)
         generateDistanceField<OverlappingContourCombiner<MultiDistanceSelector> >(output, shape, projection, range);
     else
@@ -97,7 +97,7 @@ void generateMSDF(const BitmapRef<float, 3> &output, const Shape &shape, const P
     msdfErrorCorrection(output, shape, projection, range, config);
 }
 
-void generateMTSDF(const BitmapRef<float, 4> &output, const Shape &shape, const Projection &projection, double range, const MSDFGeneratorConfig &config) {
+void generateMTSDF(const BitmapRef<float, 4> &output, const Shape &shape, const Projection &projection, real range, const MSDFGeneratorConfig &config) {
     if (config.overlapSupport)
         generateDistanceField<OverlappingContourCombiner<MultiAndTrueDistanceSelector> >(output, shape, projection, range);
     else
@@ -107,33 +107,33 @@ void generateMTSDF(const BitmapRef<float, 4> &output, const Shape &shape, const
 
 // Legacy API
 
-void generateSDF(const BitmapRef<float, 1> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, bool overlapSupport) {
+void generateSDF(const BitmapRef<float, 1> &output, const Shape &shape, real range, const Vector2 &scale, const Vector2 &translate, bool overlapSupport) {
     generateSDF(output, shape, Projection(scale, translate), range, GeneratorConfig(overlapSupport));
 }
 
-void generatePseudoSDF(const BitmapRef<float, 1> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, bool overlapSupport) {
+void generatePseudoSDF(const BitmapRef<float, 1> &output, const Shape &shape, real range, const Vector2 &scale, const Vector2 &translate, bool overlapSupport) {
     generatePseudoSDF(output, shape, Projection(scale, translate), range, GeneratorConfig(overlapSupport));
 }
 
-void generateMSDF(const BitmapRef<float, 3> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, const ErrorCorrectionConfig &errorCorrectionConfig, bool overlapSupport) {
+void generateMSDF(const BitmapRef<float, 3> &output, const Shape &shape, real range, const Vector2 &scale, const Vector2 &translate, const ErrorCorrectionConfig &errorCorrectionConfig, bool overlapSupport) {
     generateMSDF(output, shape, Projection(scale, translate), range, MSDFGeneratorConfig(overlapSupport, errorCorrectionConfig));
 }
 
-void generateMTSDF(const BitmapRef<float, 4> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, const ErrorCorrectionConfig &errorCorrectionConfig, bool overlapSupport) {
+void generateMTSDF(const BitmapRef<float, 4> &output, const Shape &shape, real range, const Vector2 &scale, const Vector2 &translate, const ErrorCorrectionConfig &errorCorrectionConfig, bool overlapSupport) {
     generateMTSDF(output, shape, Projection(scale, translate), range, MSDFGeneratorConfig(overlapSupport, errorCorrectionConfig));
 }
 
 // Legacy version
 
-void generateSDF_legacy(const BitmapRef<float, 1> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate) {
+void generateSDF_legacy(const BitmapRef<float, 1> &output, const Shape &shape, real range, const Vector2 &scale, const Vector2 &translate) {
 #ifdef MSDFGEN_USE_OPENMP
     #pragma omp parallel for
 #endif
     for (int y = 0; y < output.height; ++y) {
         int row = shape.inverseYAxis ? output.height-y-1 : y;
         for (int x = 0; x < output.width; ++x) {
-            double dummy;
-            Point2 p = Vector2(x+.5, y+.5)/scale-translate;
+            real dummy;
+            Point2 p = Vector2(real(x)+real(.5), real(y)+real(.5))/scale-translate;
             SignedDistance minDistance;
             for (std::vector<Contour>::const_iterator contour = shape.contours.begin(); contour != shape.contours.end(); ++contour)
                 for (std::vector<EdgeHolder>::const_iterator edge = contour->edges.begin(); edge != contour->edges.end(); ++edge) {
@@ -141,25 +141,25 @@ void generateSDF_legacy(const BitmapRef<float, 1> &output, const Shape &shape, d
                     if (distance < minDistance)
                         minDistance = distance;
                 }
-            *output(x, row) = float(minDistance.distance/range+.5);
+            *output(x, row) = float(minDistance.distance/range+real(.5));
         }
     }
 }
 
-void generatePseudoSDF_legacy(const BitmapRef<float, 1> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate) {
+void generatePseudoSDF_legacy(const BitmapRef<float, 1> &output, const Shape &shape, real range, const Vector2 &scale, const Vector2 &translate) {
 #ifdef MSDFGEN_USE_OPENMP
     #pragma omp parallel for
 #endif
     for (int y = 0; y < output.height; ++y) {
         int row = shape.inverseYAxis ? output.height-y-1 : y;
         for (int x = 0; x < output.width; ++x) {
-            Point2 p = Vector2(x+.5, y+.5)/scale-translate;
+            Point2 p = Vector2(real(x)+real(.5), real(y)+real(.5))/scale-translate;
             SignedDistance minDistance;
             const EdgeHolder *nearEdge = NULL;
-            double nearParam = 0;
+            real nearParam = 0;
             for (std::vector<Contour>::const_iterator contour = shape.contours.begin(); contour != shape.contours.end(); ++contour)
                 for (std::vector<EdgeHolder>::const_iterator edge = contour->edges.begin(); edge != contour->edges.end(); ++edge) {
-                    double param;
+                    real param;
                     SignedDistance distance = (*edge)->signedDistance(p, param);
                     if (distance < minDistance) {
                         minDistance = distance;
@@ -169,31 +169,31 @@ void generatePseudoSDF_legacy(const BitmapRef<float, 1> &output, const Shape &sh
                 }
             if (nearEdge)
                 (*nearEdge)->distanceToPseudoDistance(minDistance, p, nearParam);
-            *output(x, row) = float(minDistance.distance/range+.5);
+            *output(x, row) = float(minDistance.distance/range+real(.5));
         }
     }
 }
 
-void generateMSDF_legacy(const BitmapRef<float, 3> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, ErrorCorrectionConfig errorCorrectionConfig) {
+void generateMSDF_legacy(const BitmapRef<float, 3> &output, const Shape &shape, real range, const Vector2 &scale, const Vector2 &translate, ErrorCorrectionConfig errorCorrectionConfig) {
 #ifdef MSDFGEN_USE_OPENMP
     #pragma omp parallel for
 #endif
     for (int y = 0; y < output.height; ++y) {
         int row = shape.inverseYAxis ? output.height-y-1 : y;
         for (int x = 0; x < output.width; ++x) {
-            Point2 p = Vector2(x+.5, y+.5)/scale-translate;
+            Point2 p = Vector2(real(x)+real(.5), real(y)+real(.5))/scale-translate;
 
             struct {
                 SignedDistance minDistance;
                 const EdgeHolder *nearEdge;
-                double nearParam;
+                real nearParam;
             } r, g, b;
             r.nearEdge = g.nearEdge = b.nearEdge = NULL;
             r.nearParam = g.nearParam = b.nearParam = 0;
 
             for (std::vector<Contour>::const_iterator contour = shape.contours.begin(); contour != shape.contours.end(); ++contour)
                 for (std::vector<EdgeHolder>::const_iterator edge = contour->edges.begin(); edge != contour->edges.end(); ++edge) {
-                    double param;
+                    real param;
                     SignedDistance distance = (*edge)->signedDistance(p, param);
                     if ((*edge)->color&RED && distance < r.minDistance) {
                         r.minDistance = distance;
@@ -218,9 +218,9 @@ void generateMSDF_legacy(const BitmapRef<float, 3> &output, const Shape &shape,
                 (*g.nearEdge)->distanceToPseudoDistance(g.minDistance, p, g.nearParam);
             if (b.nearEdge)
                 (*b.nearEdge)->distanceToPseudoDistance(b.minDistance, p, b.nearParam);
-            output(x, row)[0] = float(r.minDistance.distance/range+.5);
-            output(x, row)[1] = float(g.minDistance.distance/range+.5);
-            output(x, row)[2] = float(b.minDistance.distance/range+.5);
+            output(x, row)[0] = float(r.minDistance.distance/range+real(.5));
+            output(x, row)[1] = float(g.minDistance.distance/range+real(.5));
+            output(x, row)[2] = float(b.minDistance.distance/range+real(.5));
         }
     }
 
@@ -228,27 +228,27 @@ void generateMSDF_legacy(const BitmapRef<float, 3> &output, const Shape &shape,
     msdfErrorCorrection(output, shape, Projection(scale, translate), range, MSDFGeneratorConfig(false, errorCorrectionConfig));
 }
 
-void generateMTSDF_legacy(const BitmapRef<float, 4> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, ErrorCorrectionConfig errorCorrectionConfig) {
+void generateMTSDF_legacy(const BitmapRef<float, 4> &output, const Shape &shape, real range, const Vector2 &scale, const Vector2 &translate, ErrorCorrectionConfig errorCorrectionConfig) {
 #ifdef MSDFGEN_USE_OPENMP
     #pragma omp parallel for
 #endif
     for (int y = 0; y < output.height; ++y) {
         int row = shape.inverseYAxis ? output.height-y-1 : y;
         for (int x = 0; x < output.width; ++x) {
-            Point2 p = Vector2(x+.5, y+.5)/scale-translate;
+            Point2 p = Vector2(real(x)+real(.5), real(y)+real(.5))/scale-translate;
 
             SignedDistance minDistance;
             struct {
                 SignedDistance minDistance;
                 const EdgeHolder *nearEdge;
-                double nearParam;
+                real nearParam;
             } r, g, b;
             r.nearEdge = g.nearEdge = b.nearEdge = NULL;
             r.nearParam = g.nearParam = b.nearParam = 0;
 
             for (std::vector<Contour>::const_iterator contour = shape.contours.begin(); contour != shape.contours.end(); ++contour)
                 for (std::vector<EdgeHolder>::const_iterator edge = contour->edges.begin(); edge != contour->edges.end(); ++edge) {
-                    double param;
+                    real param;
                     SignedDistance distance = (*edge)->signedDistance(p, param);
                     if (distance < minDistance)
                         minDistance = distance;
@@ -275,10 +275,10 @@ void generateMTSDF_legacy(const BitmapRef<float, 4> &output, const Shape &shape,
                 (*g.nearEdge)->distanceToPseudoDistance(g.minDistance, p, g.nearParam);
             if (b.nearEdge)
                 (*b.nearEdge)->distanceToPseudoDistance(b.minDistance, p, b.nearParam);
-            output(x, row)[0] = float(r.minDistance.distance/range+.5);
-            output(x, row)[1] = float(g.minDistance.distance/range+.5);
-            output(x, row)[2] = float(b.minDistance.distance/range+.5);
-            output(x, row)[3] = float(minDistance.distance/range+.5);
+            output(x, row)[0] = float(r.minDistance.distance/range+real(.5));
+            output(x, row)[1] = float(g.minDistance.distance/range+real(.5));
+            output(x, row)[2] = float(b.minDistance.distance/range+real(.5));
+            output(x, row)[3] = float(minDistance.distance/range+real(.5));
         }
     }
 

+ 1 - 2
core/pixel-conversion.hpp

@@ -1,12 +1,11 @@
 
 #pragma once
 
+#include "types.h"
 #include "arithmetics.hpp"
 
 namespace msdfgen {
 
-typedef unsigned char byte;
-
 inline byte pixelFloatToByte(float x) {
     return byte(clamp(256.f*x, 255.f));
 }

+ 6 - 6
core/rasterization.cpp

@@ -10,9 +10,9 @@ void rasterize(const BitmapRef<float, 1> &output, const Shape &shape, const Proj
     Scanline scanline;
     for (int y = 0; y < output.height; ++y) {
         int row = shape.inverseYAxis ? output.height-y-1 : y;
-        shape.scanline(scanline, projection.unprojectY(y+.5));
+        shape.scanline(scanline, projection.unprojectY(real(y)+real(.5)));
         for (int x = 0; x < output.width; ++x)
-            *output(x, row) = (float) scanline.filled(projection.unprojectX(x+.5), fillRule);
+            *output(x, row) = (float) scanline.filled(projection.unprojectX(real(x)+real(.5)), fillRule);
     }
 }
 
@@ -20,9 +20,9 @@ void distanceSignCorrection(const BitmapRef<float, 1> &sdf, const Shape &shape,
     Scanline scanline;
     for (int y = 0; y < sdf.height; ++y) {
         int row = shape.inverseYAxis ? sdf.height-y-1 : y;
-        shape.scanline(scanline, projection.unprojectY(y+.5));
+        shape.scanline(scanline, projection.unprojectY(real(y)+real(.5)));
         for (int x = 0; x < sdf.width; ++x) {
-            bool fill = scanline.filled(projection.unprojectX(x+.5), fillRule);
+            bool fill = scanline.filled(projection.unprojectX(real(x)+real(.5)), fillRule);
             float &sd = *sdf(x, row);
             if ((sd > .5f) != fill)
                 sd = 1.f-sd;
@@ -42,9 +42,9 @@ static void multiDistanceSignCorrection(const BitmapRef<float, N> &sdf, const Sh
     char *match = &matchMap[0];
     for (int y = 0; y < h; ++y) {
         int row = shape.inverseYAxis ? h-y-1 : y;
-        shape.scanline(scanline, projection.unprojectY(y+.5));
+        shape.scanline(scanline, projection.unprojectY(real(y)+real(.5)));
         for (int x = 0; x < w; ++x) {
-            bool fill = scanline.filled(projection.unprojectX(x+.5), fillRule);
+            bool fill = scanline.filled(projection.unprojectX(real(x)+real(.5)), fillRule);
             float *msd = sdf(x, row);
             float sd = median(msd[0], msd[1], msd[2]);
             if (sd == .5f)

+ 26 - 26
core/render-sdf.cpp

@@ -7,30 +7,30 @@
 
 namespace msdfgen {
 
-static float distVal(float dist, double pxRange, float midValue) {
+static float distVal(float dist, real pxRange, float midValue) {
     if (!pxRange)
         return (float) (dist > midValue);
-    return (float) clamp((dist-midValue)*pxRange+.5);
+    return (float) clamp((dist-midValue)*pxRange+real(.5));
 }
 
-void renderSDF(const BitmapRef<float, 1> &output, const BitmapConstRef<float, 1> &sdf, double pxRange, float midValue) {
-    Vector2 scale((double) sdf.width/output.width, (double) sdf.height/output.height);
-    pxRange *= (double) (output.width+output.height)/(sdf.width+sdf.height);
+void renderSDF(const BitmapRef<float, 1> &output, const BitmapConstRef<float, 1> &sdf, real pxRange, float midValue) {
+    Vector2 scale(real(sdf.width)/real(output.width), real(sdf.height)/real(output.height));
+    pxRange *= real(output.width+output.height)/real(sdf.width+sdf.height);
     for (int y = 0; y < output.height; ++y)
         for (int x = 0; x < output.width; ++x) {
             float sd;
-            interpolate(&sd, sdf, scale*Point2(x+.5, y+.5));
+            interpolate(&sd, sdf, scale*Point2(real(x)+real(.5), real(y)+real(.5)));
             *output(x, y) = distVal(sd, pxRange, midValue);
         }
 }
 
-void renderSDF(const BitmapRef<float, 3> &output, const BitmapConstRef<float, 1> &sdf, double pxRange, float midValue) {
-    Vector2 scale((double) sdf.width/output.width, (double) sdf.height/output.height);
-    pxRange *= (double) (output.width+output.height)/(sdf.width+sdf.height);
+void renderSDF(const BitmapRef<float, 3> &output, const BitmapConstRef<float, 1> &sdf, real pxRange, float midValue) {
+    Vector2 scale(real(sdf.width)/real(output.width), real(sdf.height)/real(output.height));
+    pxRange *= real(output.width+output.height)/real(sdf.width+sdf.height);
     for (int y = 0; y < output.height; ++y)
         for (int x = 0; x < output.width; ++x) {
             float sd;
-            interpolate(&sd, sdf, scale*Point2(x+.5, y+.5));
+            interpolate(&sd, sdf, scale*Point2(real(x)+real(.5), real(y)+real(.5)));
             float v = distVal(sd, pxRange, midValue);
             output(x, y)[0] = v;
             output(x, y)[1] = v;
@@ -38,48 +38,48 @@ void renderSDF(const BitmapRef<float, 3> &output, const BitmapConstRef<float, 1>
         }
 }
 
-void renderSDF(const BitmapRef<float, 1> &output, const BitmapConstRef<float, 3> &sdf, double pxRange, float midValue) {
-    Vector2 scale((double) sdf.width/output.width, (double) sdf.height/output.height);
-    pxRange *= (double) (output.width+output.height)/(sdf.width+sdf.height);
+void renderSDF(const BitmapRef<float, 1> &output, const BitmapConstRef<float, 3> &sdf, real pxRange, float midValue) {
+    Vector2 scale(real(sdf.width)/real(output.width), real(sdf.height)/real(output.height));
+    pxRange *= real(output.width+output.height)/real(sdf.width+sdf.height);
     for (int y = 0; y < output.height; ++y)
         for (int x = 0; x < output.width; ++x) {
             float sd[3];
-            interpolate(sd, sdf, scale*Point2(x+.5, y+.5));
+            interpolate(sd, sdf, scale*Point2(real(x)+real(.5), real(y)+real(.5)));
             *output(x, y) = distVal(median(sd[0], sd[1], sd[2]), pxRange, midValue);
         }
 }
 
-void renderSDF(const BitmapRef<float, 3> &output, const BitmapConstRef<float, 3> &sdf, double pxRange, float midValue) {
-    Vector2 scale((double) sdf.width/output.width, (double) sdf.height/output.height);
-    pxRange *= (double) (output.width+output.height)/(sdf.width+sdf.height);
+void renderSDF(const BitmapRef<float, 3> &output, const BitmapConstRef<float, 3> &sdf, real pxRange, float midValue) {
+    Vector2 scale(real(sdf.width)/real(output.width), real(sdf.height)/real(output.height));
+    pxRange *= real(output.width+output.height)/real(sdf.width+sdf.height);
     for (int y = 0; y < output.height; ++y)
         for (int x = 0; x < output.width; ++x) {
             float sd[3];
-            interpolate(sd, sdf, scale*Point2(x+.5, y+.5));
+            interpolate(sd, sdf, scale*Point2(real(x)+real(.5), real(y)+real(.5)));
             output(x, y)[0] = distVal(sd[0], pxRange, midValue);
             output(x, y)[1] = distVal(sd[1], pxRange, midValue);
             output(x, y)[2] = distVal(sd[2], pxRange, midValue);
         }
 }
 
-void renderSDF(const BitmapRef<float, 1> &output, const BitmapConstRef<float, 4> &sdf, double pxRange, float midValue) {
-    Vector2 scale((double) sdf.width/output.width, (double) sdf.height/output.height);
-    pxRange *= (double) (output.width+output.height)/(sdf.width+sdf.height);
+void renderSDF(const BitmapRef<float, 1> &output, const BitmapConstRef<float, 4> &sdf, real pxRange, float midValue) {
+    Vector2 scale(real(sdf.width)/real(output.width), real(sdf.height)/real(output.height));
+    pxRange *= real(output.width+output.height)/real(sdf.width+sdf.height);
     for (int y = 0; y < output.height; ++y)
         for (int x = 0; x < output.width; ++x) {
             float sd[4];
-            interpolate(sd, sdf, scale*Point2(x+.5, y+.5));
+            interpolate(sd, sdf, scale*Point2(real(x)+real(.5), real(y)+real(.5)));
             *output(x, y) = distVal(median(sd[0], sd[1], sd[2]), pxRange, midValue);
         }
 }
 
-void renderSDF(const BitmapRef<float, 4> &output, const BitmapConstRef<float, 4> &sdf, double pxRange, float midValue) {
-    Vector2 scale((double) sdf.width/output.width, (double) sdf.height/output.height);
-    pxRange *= (double) (output.width+output.height)/(sdf.width+sdf.height);
+void renderSDF(const BitmapRef<float, 4> &output, const BitmapConstRef<float, 4> &sdf, real pxRange, float midValue) {
+    Vector2 scale(real(sdf.width)/real(output.width), real(sdf.height)/real(output.height));
+    pxRange *= real(output.width+output.height)/real(sdf.width+sdf.height);
     for (int y = 0; y < output.height; ++y)
         for (int x = 0; x < output.width; ++x) {
             float sd[4];
-            interpolate(sd, sdf, scale*Point2(x+.5, y+.5));
+            interpolate(sd, sdf, scale*Point2(real(x)+real(.5), real(y)+real(.5)));
             output(x, y)[0] = distVal(sd[0], pxRange, midValue);
             output(x, y)[1] = distVal(sd[1], pxRange, midValue);
             output(x, y)[2] = distVal(sd[2], pxRange, midValue);

+ 7 - 6
core/render-sdf.h

@@ -1,18 +1,19 @@
 
 #pragma once
 
+#include "types.h"
 #include "Vector2.hpp"
 #include "BitmapRef.hpp"
 
 namespace msdfgen {
 
 /// Reconstructs the shape's appearance into output from the distance field sdf.
-void renderSDF(const BitmapRef<float, 1> &output, const BitmapConstRef<float, 1> &sdf, double pxRange = 0, float midValue = .5f);
-void renderSDF(const BitmapRef<float, 3> &output, const BitmapConstRef<float, 1> &sdf, double pxRange = 0, float midValue = .5f);
-void renderSDF(const BitmapRef<float, 1> &output, const BitmapConstRef<float, 3> &sdf, double pxRange = 0, float midValue = .5f);
-void renderSDF(const BitmapRef<float, 3> &output, const BitmapConstRef<float, 3> &sdf, double pxRange = 0, float midValue = .5f);
-void renderSDF(const BitmapRef<float, 1> &output, const BitmapConstRef<float, 4> &sdf, double pxRange = 0, float midValue = .5f);
-void renderSDF(const BitmapRef<float, 4> &output, const BitmapConstRef<float, 4> &sdf, double pxRange = 0, float midValue = .5f);
+void renderSDF(const BitmapRef<float, 1> &output, const BitmapConstRef<float, 1> &sdf, real pxRange = 0, float midValue = .5f);
+void renderSDF(const BitmapRef<float, 3> &output, const BitmapConstRef<float, 1> &sdf, real pxRange = 0, float midValue = .5f);
+void renderSDF(const BitmapRef<float, 1> &output, const BitmapConstRef<float, 3> &sdf, real pxRange = 0, float midValue = .5f);
+void renderSDF(const BitmapRef<float, 3> &output, const BitmapConstRef<float, 3> &sdf, real pxRange = 0, float midValue = .5f);
+void renderSDF(const BitmapRef<float, 1> &output, const BitmapConstRef<float, 4> &sdf, real pxRange = 0, float midValue = .5f);
+void renderSDF(const BitmapRef<float, 4> &output, const BitmapConstRef<float, 4> &sdf, real pxRange = 0, float midValue = .5f);
 
 /// Snaps the values of the floating-point bitmaps into one of the 256 values representable in a standard 8-bit bitmap.
 void simulate8bit(const BitmapRef<float, 1> &bitmap);

+ 1 - 0
core/save-bmp.h

@@ -1,6 +1,7 @@
 
 #pragma once
 
+#include "types.h"
 #include "BitmapRef.hpp"
 
 namespace msdfgen {

+ 36 - 35
core/sdf-error-estimation.cpp

@@ -2,19 +2,20 @@
 #include "sdf-error-estimation.h"
 
 #include <cmath>
+#include <cfloat>
 #include "arithmetics.hpp"
 
 namespace msdfgen {
 
-void scanlineSDF(Scanline &line, const BitmapConstRef<float, 1> &sdf, const Projection &projection, double y, bool inverseYAxis) {
+void scanlineSDF(Scanline &line, const BitmapConstRef<float, 1> &sdf, const Projection &projection, real y, bool inverseYAxis) {
     if (!(sdf.width > 0 && sdf.height > 0))
         return line.setIntersections(std::vector<Scanline::Intersection>());
-    double pixelY = clamp(projection.projectY(y)-.5, double(sdf.height-1));
+    real pixelY = clamp(projection.projectY(y)-real(.5), real(sdf.height-1));
     if (inverseYAxis)
         pixelY = sdf.height-1-pixelY;
     int b = (int) floor(pixelY);
     int t = b+1;
-    double bt = pixelY-b;
+    real bt = pixelY-real(b);
     if (t >= sdf.height) {
         b = sdf.height-1;
         t = sdf.height-1;
@@ -24,16 +25,16 @@ void scanlineSDF(Scanline &line, const BitmapConstRef<float, 1> &sdf, const Proj
     std::vector<Scanline::Intersection> intersections;
     float lv, rv = mix(*sdf(0, b), *sdf(0, t), bt);
     if ((inside = rv > .5f)) {
-        Scanline::Intersection intersection = { -1e240, 1 };
+        Scanline::Intersection intersection = { -FLT_MAX, 1 };
         intersections.push_back(intersection);
     }
     for (int l = 0, r = 1; r < sdf.width; ++l, ++r) {
         lv = rv;
         rv = mix(*sdf(r, b), *sdf(r, t), bt);
         if (lv != rv) {
-            double lr = double(.5f-lv)/double(rv-lv);
-            if (lr >= 0 && lr <= 1) {
-                Scanline::Intersection intersection = { projection.unprojectX(l+lr+.5), sign(rv-lv) };
+            real lr = real(.5f-lv)/real(rv-lv);
+            if (lr >= real(0) && lr <= real(1)) {
+                Scanline::Intersection intersection = { projection.unprojectX(real(l)+lr+real(.5)), sign(rv-lv) };
                 intersections.push_back(intersection);
             }
         }
@@ -46,15 +47,15 @@ void scanlineSDF(Scanline &line, const BitmapConstRef<float, 1> &sdf, const Proj
 }
 
 template <int N>
-void scanlineMSDF(Scanline &line, const BitmapConstRef<float, N> &sdf, const Projection &projection, double y, bool inverseYAxis) {
+void scanlineMSDF(Scanline &line, const BitmapConstRef<float, N> &sdf, const Projection &projection, real y, bool inverseYAxis) {
     if (!(sdf.width > 0 && sdf.height > 0))
         return line.setIntersections(std::vector<Scanline::Intersection>());
-    double pixelY = clamp(projection.projectY(y)-.5, double(sdf.height-1));
+    real pixelY = clamp(projection.projectY(y)-real(.5), real(sdf.height-1));
     if (inverseYAxis)
         pixelY = sdf.height-1-pixelY;
     int b = (int) floor(pixelY);
     int t = b+1;
-    double bt = pixelY-b;
+    real bt = pixelY-real(b);
     if (t >= sdf.height) {
         b = sdf.height-1;
         t = sdf.height-1;
@@ -67,7 +68,7 @@ void scanlineMSDF(Scanline &line, const BitmapConstRef<float, N> &sdf, const Pro
     rv[1] = mix(sdf(0, b)[1], sdf(0, t)[1], bt);
     rv[2] = mix(sdf(0, b)[2], sdf(0, t)[2], bt);
     if ((inside = median(rv[0], rv[1], rv[2]) > .5f)) {
-        Scanline::Intersection intersection = { -1e240, 1 };
+        Scanline::Intersection intersection = { -FLT_MAX, 1 };
         intersections.push_back(intersection);
     }
     for (int l = 0, r = 1; r < sdf.width; ++l, ++r) {
@@ -79,15 +80,15 @@ void scanlineMSDF(Scanline &line, const BitmapConstRef<float, N> &sdf, const Pro
         int newIntersectionCount = 0;
         for (int i = 0; i < 3; ++i) {
             if (lv[i] != rv[i]) {
-                double lr = double(.5f-lv[i])/double(rv[i]-lv[i]);
-                if (lr >= 0 && lr <= 1) {
+                real lr = real(.5f-lv[i])/real(rv[i]-lv[i]);
+                if (lr >= real(0) && lr <= real(1)) {
                     float v[3] = {
                         mix(lv[0], rv[0], lr),
                         mix(lv[1], rv[1], lr),
                         mix(lv[2], rv[2], lr)
                     };
                     if (median(v[0], v[1], v[2]) == v[i]) {
-                        newIntersections[newIntersectionCount].x = projection.unprojectX(l+lr+.5);
+                        newIntersections[newIntersectionCount].x = projection.unprojectX(real(l)+lr+real(.5));
                         newIntersections[newIntersectionCount].direction = sign(rv[i]-lv[i]);
                         ++newIntersectionCount;
                     }
@@ -124,68 +125,68 @@ void scanlineMSDF(Scanline &line, const BitmapConstRef<float, N> &sdf, const Pro
 #endif
 }
 
-void scanlineSDF(Scanline &line, const BitmapConstRef<float, 3> &sdf, const Projection &projection, double y, bool inverseYAxis) {
+void scanlineSDF(Scanline &line, const BitmapConstRef<float, 3> &sdf, const Projection &projection, real y, bool inverseYAxis) {
     scanlineMSDF(line, sdf, projection, y, inverseYAxis);
 }
-void scanlineSDF(Scanline &line, const BitmapConstRef<float, 4> &sdf, const Projection &projection, double y, bool inverseYAxis) {
+void scanlineSDF(Scanline &line, const BitmapConstRef<float, 4> &sdf, const Projection &projection, real y, bool inverseYAxis) {
     scanlineMSDF(line, sdf, projection, y, inverseYAxis);
 }
 
 template <int N>
-double estimateSDFErrorInner(const BitmapConstRef<float, N> &sdf, const Shape &shape, const Projection &projection, int scanlinesPerRow, FillRule fillRule) {
+real estimateSDFErrorInner(const BitmapConstRef<float, N> &sdf, const Shape &shape, const Projection &projection, int scanlinesPerRow, FillRule fillRule) {
     if (sdf.width <= 1 || sdf.height <= 1 || scanlinesPerRow < 1)
         return 0;
-    double subRowSize = 1./scanlinesPerRow;
-    double xFrom = projection.unprojectX(.5);
-    double xTo = projection.unprojectX(sdf.width-.5);
-    double overlapFactor = 1/(xTo-xFrom);
-    double error = 0;
+    real subRowSize = real(1)/real(scanlinesPerRow);
+    real xFrom = projection.unprojectX(real(.5));
+    real xTo = projection.unprojectX(real(sdf.width)-real(.5));
+    real overlapFactor = real(1)/(xTo-xFrom);
+    real error = 0;
     Scanline refScanline, sdfScanline;
     for (int row = 0; row < sdf.height-1; ++row) {
         for (int subRow = 0; subRow < scanlinesPerRow; ++subRow) {
-            double bt = (subRow+.5)*subRowSize;
-            double y = projection.unprojectY(row+bt+.5);
+            real bt = (real(subRow)+real(.5))*subRowSize;
+            real y = projection.unprojectY(real(row)+bt+real(.5));
             shape.scanline(refScanline, y);
             scanlineSDF(sdfScanline, sdf, projection, y, shape.inverseYAxis);
-            error += 1-overlapFactor*Scanline::overlap(refScanline, sdfScanline, xFrom, xTo, fillRule);
+            error += real(1)-overlapFactor*Scanline::overlap(refScanline, sdfScanline, xFrom, xTo, fillRule);
         }
     }
-    return error/((sdf.height-1)*scanlinesPerRow);
+    return error/real((sdf.height-1)*scanlinesPerRow);
 }
 
-double estimateSDFError(const BitmapConstRef<float, 1> &sdf, const Shape &shape, const Projection &projection, int scanlinesPerRow, FillRule fillRule) {
+real estimateSDFError(const BitmapConstRef<float, 1> &sdf, const Shape &shape, const Projection &projection, int scanlinesPerRow, FillRule fillRule) {
     return estimateSDFErrorInner(sdf, shape, projection, scanlinesPerRow, fillRule);
 }
-double estimateSDFError(const BitmapConstRef<float, 3> &sdf, const Shape &shape, const Projection &projection, int scanlinesPerRow, FillRule fillRule) {
+real estimateSDFError(const BitmapConstRef<float, 3> &sdf, const Shape &shape, const Projection &projection, int scanlinesPerRow, FillRule fillRule) {
     return estimateSDFErrorInner(sdf, shape, projection, scanlinesPerRow, fillRule);
 }
-double estimateSDFError(const BitmapConstRef<float, 4> &sdf, const Shape &shape, const Projection &projection, int scanlinesPerRow, FillRule fillRule) {
+real estimateSDFError(const BitmapConstRef<float, 4> &sdf, const Shape &shape, const Projection &projection, int scanlinesPerRow, FillRule fillRule) {
     return estimateSDFErrorInner(sdf, shape, projection, scanlinesPerRow, fillRule);
 }
 
 // Legacy API
 
-void scanlineSDF(Scanline &line, const BitmapConstRef<float, 1> &sdf, const Vector2 &scale, const Vector2 &translate, bool inverseYAxis, double y) {
+void scanlineSDF(Scanline &line, const BitmapConstRef<float, 1> &sdf, const Vector2 &scale, const Vector2 &translate, bool inverseYAxis, real y) {
     scanlineSDF(line, sdf, Projection(scale, translate), y, inverseYAxis);
 }
 
-void scanlineSDF(Scanline &line, const BitmapConstRef<float, 3> &sdf, const Vector2 &scale, const Vector2 &translate, bool inverseYAxis, double y) {
+void scanlineSDF(Scanline &line, const BitmapConstRef<float, 3> &sdf, const Vector2 &scale, const Vector2 &translate, bool inverseYAxis, real y) {
     scanlineSDF(line, sdf, Projection(scale, translate), y, inverseYAxis);
 }
 
-void scanlineSDF(Scanline &line, const BitmapConstRef<float, 4> &sdf, const Vector2 &scale, const Vector2 &translate, bool inverseYAxis, double y) {
+void scanlineSDF(Scanline &line, const BitmapConstRef<float, 4> &sdf, const Vector2 &scale, const Vector2 &translate, bool inverseYAxis, real y) {
     scanlineSDF(line, sdf, Projection(scale, translate), y, inverseYAxis);
 }
 
-double estimateSDFError(const BitmapConstRef<float, 1> &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, int scanlinesPerRow, FillRule fillRule) {
+real estimateSDFError(const BitmapConstRef<float, 1> &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, int scanlinesPerRow, FillRule fillRule) {
     return estimateSDFError(sdf, shape, Projection(scale, translate), scanlinesPerRow, fillRule);
 }
 
-double estimateSDFError(const BitmapConstRef<float, 3> &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, int scanlinesPerRow, FillRule fillRule) {
+real estimateSDFError(const BitmapConstRef<float, 3> &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, int scanlinesPerRow, FillRule fillRule) {
     return estimateSDFError(sdf, shape, Projection(scale, translate), scanlinesPerRow, fillRule);
 }
 
-double estimateSDFError(const BitmapConstRef<float, 4> &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, int scanlinesPerRow, FillRule fillRule) {
+real estimateSDFError(const BitmapConstRef<float, 4> &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, int scanlinesPerRow, FillRule fillRule) {
     return estimateSDFError(sdf, shape, Projection(scale, translate), scanlinesPerRow, fillRule);
 }
 

+ 13 - 12
core/sdf-error-estimation.h

@@ -1,6 +1,7 @@
 
 #pragma once
 
+#include "types.h"
 #include "Vector2.hpp"
 #include "Shape.h"
 #include "Projection.h"
@@ -10,21 +11,21 @@
 namespace msdfgen {
 
 /// Analytically constructs a scanline at y evaluating fill by linear interpolation of the SDF.
-void scanlineSDF(Scanline &line, const BitmapConstRef<float, 1> &sdf, const Projection &projection, double y, bool inverseYAxis = false);
-void scanlineSDF(Scanline &line, const BitmapConstRef<float, 3> &sdf, const Projection &projection, double y, bool inverseYAxis = false);
-void scanlineSDF(Scanline &line, const BitmapConstRef<float, 4> &sdf, const Projection &projection, double y, bool inverseYAxis = false);
+void scanlineSDF(Scanline &line, const BitmapConstRef<float, 1> &sdf, const Projection &projection, real y, bool inverseYAxis = false);
+void scanlineSDF(Scanline &line, const BitmapConstRef<float, 3> &sdf, const Projection &projection, real y, bool inverseYAxis = false);
+void scanlineSDF(Scanline &line, const BitmapConstRef<float, 4> &sdf, const Projection &projection, real y, bool inverseYAxis = false);
 
 /// Estimates the portion of the area that will be filled incorrectly when rendering using the SDF.
-double estimateSDFError(const BitmapConstRef<float, 1> &sdf, const Shape &shape, const Projection &projection, int scanlinesPerRow, FillRule fillRule = FILL_NONZERO);
-double estimateSDFError(const BitmapConstRef<float, 3> &sdf, const Shape &shape, const Projection &projection, int scanlinesPerRow, FillRule fillRule = FILL_NONZERO);
-double estimateSDFError(const BitmapConstRef<float, 4> &sdf, const Shape &shape, const Projection &projection, int scanlinesPerRow, FillRule fillRule = FILL_NONZERO);
+real estimateSDFError(const BitmapConstRef<float, 1> &sdf, const Shape &shape, const Projection &projection, int scanlinesPerRow, FillRule fillRule = FILL_NONZERO);
+real estimateSDFError(const BitmapConstRef<float, 3> &sdf, const Shape &shape, const Projection &projection, int scanlinesPerRow, FillRule fillRule = FILL_NONZERO);
+real estimateSDFError(const BitmapConstRef<float, 4> &sdf, const Shape &shape, const Projection &projection, int scanlinesPerRow, FillRule fillRule = FILL_NONZERO);
 
 // Old version of the function API's kept for backwards compatibility
-void scanlineSDF(Scanline &line, const BitmapConstRef<float, 1> &sdf, const Vector2 &scale, const Vector2 &translate, bool inverseYAxis, double y);
-void scanlineSDF(Scanline &line, const BitmapConstRef<float, 3> &sdf, const Vector2 &scale, const Vector2 &translate, bool inverseYAxis, double y);
-void scanlineSDF(Scanline &line, const BitmapConstRef<float, 4> &sdf, const Vector2 &scale, const Vector2 &translate, bool inverseYAxis, double y);
-double estimateSDFError(const BitmapConstRef<float, 1> &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, int scanlinesPerRow, FillRule fillRule = FILL_NONZERO);
-double estimateSDFError(const BitmapConstRef<float, 3> &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, int scanlinesPerRow, FillRule fillRule = FILL_NONZERO);
-double estimateSDFError(const BitmapConstRef<float, 4> &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, int scanlinesPerRow, FillRule fillRule = FILL_NONZERO);
+void scanlineSDF(Scanline &line, const BitmapConstRef<float, 1> &sdf, const Vector2 &scale, const Vector2 &translate, bool inverseYAxis, real y);
+void scanlineSDF(Scanline &line, const BitmapConstRef<float, 3> &sdf, const Vector2 &scale, const Vector2 &translate, bool inverseYAxis, real y);
+void scanlineSDF(Scanline &line, const BitmapConstRef<float, 4> &sdf, const Vector2 &scale, const Vector2 &translate, bool inverseYAxis, real y);
+real estimateSDFError(const BitmapConstRef<float, 1> &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, int scanlinesPerRow, FillRule fillRule = FILL_NONZERO);
+real estimateSDFError(const BitmapConstRef<float, 3> &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, int scanlinesPerRow, FillRule fillRule = FILL_NONZERO);
+real estimateSDFError(const BitmapConstRef<float, 4> &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, int scanlinesPerRow, FillRule fillRule = FILL_NONZERO);
 
 }

+ 9 - 4
core/shape-description.cpp

@@ -25,18 +25,23 @@ int readCharS(const char **input) {
 }
 
 int readCoordF(FILE *input, Point2 &coord) {
-    return fscanf(input, "%lf,%lf", &coord.x, &coord.y);
+    double x = 0, y = 0;
+    int n = fscanf(input, "%lf,%lf", &x, &y);
+    coord = Point2(x, y);
+    return n;
 }
 
 int readCoordS(const char **input, Point2 &coord) {
+    double x = 0, y = 0;
     int read = 0;
-    int result = sscanf(*input, "%lf,%lf%n", &coord.x, &coord.y, &read);
+    int n = sscanf(*input, "%lf,%lf%n", &x, &y, &read);
+    coord = Point2(x, y);
     *input += read;
-    return result;
+    return n;
 }
 
 static bool writeCoord(FILE *output, Point2 coord) {
-    fprintf(output, "%.12g, %.12g", coord.x, coord.y);
+    fprintf(output, "%.12g, %.12g", double(coord.x), double(coord.y));
     return true;
 }
 

+ 15 - 0
core/types.h

@@ -0,0 +1,15 @@
+
+#pragma once
+
+namespace msdfgen {
+
+typedef unsigned char byte;
+typedef unsigned unicode_t;
+
+#ifdef MSDFGEN_REAL
+typedef MSDFGEN_REAL real;
+#else
+typedef double real;
+#endif
+
+}

+ 31 - 31
ext/import-font.cpp

@@ -12,9 +12,9 @@
 
 namespace msdfgen {
 
-#define F26DOT6_TO_DOUBLE(x) (1/64.*double(x))
-#define F16DOT16_TO_DOUBLE(x) (1/65536.*double(x))
-#define DOUBLE_TO_F16DOT16(x) FT_Fixed(65536.*x)
+#define F26DOT6_TO_REAL(x) (::msdfgen::real(1)/::msdfgen::real(64)*::msdfgen::real(x))
+#define F16DOT16_TO_REAL(x) (::msdfgen::real(1)/::msdfgen::real(65536)*::msdfgen::real(x))
+#define REAL_TO_F16DOT16(x) FT_Fixed(::msdfgen::real(65536)*::msdfgen::real(x))
 
 class FreetypeHandle {
     friend FreetypeHandle *initializeFreetype();
@@ -22,7 +22,7 @@ class FreetypeHandle {
     friend FontHandle *loadFont(FreetypeHandle *library, const char *filename);
     friend FontHandle *loadFontData(FreetypeHandle *library, const byte *data, int length);
 #ifndef MSDFGEN_DISABLE_VARIABLE_FONTS
-    friend bool setFontVariationAxis(FreetypeHandle *library, FontHandle *font, const char *name, double coordinate);
+    friend bool setFontVariationAxis(FreetypeHandle *library, FontHandle *font, const char *name, real coordinate);
     friend bool listFontVariationAxes(std::vector<FontVariationAxis> &axes, FreetypeHandle *library, FontHandle *font);
 #endif
 
@@ -36,14 +36,14 @@ class FontHandle {
     friend FontHandle *loadFontData(FreetypeHandle *library, const byte *data, int length);
     friend void destroyFont(FontHandle *font);
     friend bool getFontMetrics(FontMetrics &metrics, FontHandle *font);
-    friend bool getFontWhitespaceWidth(double &spaceAdvance, double &tabAdvance, FontHandle *font);
+    friend bool getFontWhitespaceWidth(real &spaceAdvance, real &tabAdvance, FontHandle *font);
     friend bool getGlyphIndex(GlyphIndex &glyphIndex, FontHandle *font, unicode_t unicode);
-    friend bool loadGlyph(Shape &output, FontHandle *font, GlyphIndex glyphIndex, double *advance);
-    friend bool loadGlyph(Shape &output, FontHandle *font, unicode_t unicode, double *advance);
-    friend bool getKerning(double &output, FontHandle *font, GlyphIndex glyphIndex1, GlyphIndex glyphIndex2);
-    friend bool getKerning(double &output, FontHandle *font, unicode_t unicode1, unicode_t unicode2);
+    friend bool loadGlyph(Shape &output, FontHandle *font, GlyphIndex glyphIndex, real *advance);
+    friend bool loadGlyph(Shape &output, FontHandle *font, unicode_t unicode, real *advance);
+    friend bool getKerning(real &output, FontHandle *font, GlyphIndex glyphIndex1, GlyphIndex glyphIndex2);
+    friend bool getKerning(real &output, FontHandle *font, unicode_t unicode1, unicode_t unicode2);
 #ifndef MSDFGEN_DISABLE_VARIABLE_FONTS
-    friend bool setFontVariationAxis(FreetypeHandle *library, FontHandle *font, const char *name, double coordinate);
+    friend bool setFontVariationAxis(FreetypeHandle *library, FontHandle *font, const char *name, real coordinate);
     friend bool listFontVariationAxes(std::vector<FontVariationAxis> &axes, FreetypeHandle *library, FontHandle *font);
 #endif
 
@@ -59,7 +59,7 @@ struct FtContext {
 };
 
 static Point2 ftPoint2(const FT_Vector &vector) {
-    return Point2(F26DOT6_TO_DOUBLE(vector.x), F26DOT6_TO_DOUBLE(vector.y));
+    return Point2(F26DOT6_TO_REAL(vector.x), F26DOT6_TO_REAL(vector.y));
 }
 
 static int ftMoveTo(const FT_Vector *to, void *user) {
@@ -173,24 +173,24 @@ void destroyFont(FontHandle *font) {
 }
 
 bool getFontMetrics(FontMetrics &metrics, FontHandle *font) {
-    metrics.emSize = F26DOT6_TO_DOUBLE(font->face->units_per_EM);
-    metrics.ascenderY = F26DOT6_TO_DOUBLE(font->face->ascender);
-    metrics.descenderY = F26DOT6_TO_DOUBLE(font->face->descender);
-    metrics.lineHeight = F26DOT6_TO_DOUBLE(font->face->height);
-    metrics.underlineY = F26DOT6_TO_DOUBLE(font->face->underline_position);
-    metrics.underlineThickness = F26DOT6_TO_DOUBLE(font->face->underline_thickness);
+    metrics.emSize = F26DOT6_TO_REAL(font->face->units_per_EM);
+    metrics.ascenderY = F26DOT6_TO_REAL(font->face->ascender);
+    metrics.descenderY = F26DOT6_TO_REAL(font->face->descender);
+    metrics.lineHeight = F26DOT6_TO_REAL(font->face->height);
+    metrics.underlineY = F26DOT6_TO_REAL(font->face->underline_position);
+    metrics.underlineThickness = F26DOT6_TO_REAL(font->face->underline_thickness);
     return true;
 }
 
-bool getFontWhitespaceWidth(double &spaceAdvance, double &tabAdvance, FontHandle *font) {
+bool getFontWhitespaceWidth(real &spaceAdvance, real &tabAdvance, FontHandle *font) {
     FT_Error error = FT_Load_Char(font->face, ' ', FT_LOAD_NO_SCALE);
     if (error)
         return false;
-    spaceAdvance = F26DOT6_TO_DOUBLE(font->face->glyph->advance.x);
+    spaceAdvance = F26DOT6_TO_REAL(font->face->glyph->advance.x);
     error = FT_Load_Char(font->face, '\t', FT_LOAD_NO_SCALE);
     if (error)
         return false;
-    tabAdvance = F26DOT6_TO_DOUBLE(font->face->glyph->advance.x);
+    tabAdvance = F26DOT6_TO_REAL(font->face->glyph->advance.x);
     return true;
 }
 
@@ -199,38 +199,38 @@ bool getGlyphIndex(GlyphIndex &glyphIndex, FontHandle *font, unicode_t unicode)
     return glyphIndex.getIndex() != 0;
 }
 
-bool loadGlyph(Shape &output, FontHandle *font, GlyphIndex glyphIndex, double *advance) {
+bool loadGlyph(Shape &output, FontHandle *font, GlyphIndex glyphIndex, real *advance) {
     if (!font)
         return false;
     FT_Error error = FT_Load_Glyph(font->face, glyphIndex.getIndex(), FT_LOAD_NO_SCALE);
     if (error)
         return false;
     if (advance)
-        *advance = F26DOT6_TO_DOUBLE(font->face->glyph->advance.x);
+        *advance = F26DOT6_TO_REAL(font->face->glyph->advance.x);
     return !readFreetypeOutline(output, &font->face->glyph->outline);
 }
 
-bool loadGlyph(Shape &output, FontHandle *font, unicode_t unicode, double *advance) {
+bool loadGlyph(Shape &output, FontHandle *font, unicode_t unicode, real *advance) {
     return loadGlyph(output, font, GlyphIndex(FT_Get_Char_Index(font->face, unicode)), advance);
 }
 
-bool getKerning(double &output, FontHandle *font, GlyphIndex glyphIndex1, GlyphIndex glyphIndex2) {
+bool getKerning(real &output, FontHandle *font, GlyphIndex glyphIndex1, GlyphIndex glyphIndex2) {
     FT_Vector kerning;
     if (FT_Get_Kerning(font->face, glyphIndex1.getIndex(), glyphIndex2.getIndex(), FT_KERNING_UNSCALED, &kerning)) {
         output = 0;
         return false;
     }
-    output = F26DOT6_TO_DOUBLE(kerning.x);
+    output = F26DOT6_TO_REAL(kerning.x);
     return true;
 }
 
-bool getKerning(double &output, FontHandle *font, unicode_t unicode1, unicode_t unicode2) {
+bool getKerning(real &output, FontHandle *font, unicode_t unicode1, unicode_t unicode2) {
     return getKerning(output, font, GlyphIndex(FT_Get_Char_Index(font->face, unicode1)), GlyphIndex(FT_Get_Char_Index(font->face, unicode2)));
 }
 
 #ifndef MSDFGEN_DISABLE_VARIABLE_FONTS
 
-bool setFontVariationAxis(FreetypeHandle *library, FontHandle *font, const char *name, double coordinate) {
+bool setFontVariationAxis(FreetypeHandle *library, FontHandle *font, const char *name, real coordinate) {
     bool success = false;
     if (font->face->face_flags&FT_FACE_FLAG_MULTIPLE_MASTERS) {
         FT_MM_Var *master = NULL;
@@ -241,7 +241,7 @@ bool setFontVariationAxis(FreetypeHandle *library, FontHandle *font, const char
             if (!FT_Get_Var_Design_Coordinates(font->face, FT_UInt(coords.size()), &coords[0])) {
                 for (FT_UInt i = 0; i < master->num_axis; ++i) {
                     if (!strcmp(name, master->axis[i].name)) {
-                        coords[i] = DOUBLE_TO_F16DOT16(coordinate);
+                        coords[i] = REAL_TO_F16DOT16(coordinate);
                         success = true;
                         break;
                     }
@@ -264,9 +264,9 @@ bool listFontVariationAxes(std::vector<FontVariationAxis> &axes, FreetypeHandle
         for (FT_UInt i = 0; i < master->num_axis; ++i) {
             FontVariationAxis &axis = axes[i];
             axis.name = master->axis[i].name;
-            axis.minValue = F16DOT16_TO_DOUBLE(master->axis[i].minimum);
-            axis.maxValue = F16DOT16_TO_DOUBLE(master->axis[i].maximum);
-            axis.defaultValue = F16DOT16_TO_DOUBLE(master->axis[i].def);
+            axis.minValue = F16DOT16_TO_REAL(master->axis[i].minimum);
+            axis.maxValue = F16DOT16_TO_REAL(master->axis[i].maximum);
+            axis.defaultValue = F16DOT16_TO_REAL(master->axis[i].def);
         }
         FT_Done_MM_Var(library->library, master);
         return true;

+ 14 - 16
ext/import-font.h

@@ -2,13 +2,11 @@
 #pragma once
 
 #include <cstddef>
+#include "../core/types.h"
 #include "../core/Shape.h"
 
 namespace msdfgen {
 
-typedef unsigned char byte;
-typedef unsigned unicode_t;
-
 class FreetypeHandle;
 class FontHandle;
 
@@ -26,13 +24,13 @@ private:
 /// Global metrics of a typeface (in font units).
 struct FontMetrics {
     /// The size of one EM.
-    double emSize;
+    real emSize;
     /// The vertical position of the ascender and descender relative to the baseline.
-    double ascenderY, descenderY;
+    real ascenderY, descenderY;
     /// The vertical difference between consecutive baselines.
-    double lineHeight;
+    real lineHeight;
     /// The vertical position and thickness of the underline.
-    double underlineY, underlineThickness;
+    real underlineY, underlineThickness;
 };
 
 /// A structure to model a given axis of a variable font.
@@ -40,11 +38,11 @@ struct FontVariationAxis {
     /// The name of the variation axis.
     const char *name;
     /// The axis's minimum coordinate value.
-    double minValue;
+    real minValue;
     /// The axis's maximum coordinate value.
-    double maxValue;
+    real maxValue;
     /// The axis's default coordinate value. FreeType computes meaningful default values for Adobe MM fonts.
-    double defaultValue;
+    real defaultValue;
 };
 
 /// Initializes the FreeType library.
@@ -68,19 +66,19 @@ void destroyFont(FontHandle *font);
 /// Outputs the metrics of a font file.
 bool getFontMetrics(FontMetrics &metrics, FontHandle *font);
 /// Outputs the width of the space and tab characters.
-bool getFontWhitespaceWidth(double &spaceAdvance, double &tabAdvance, FontHandle *font);
+bool getFontWhitespaceWidth(real &spaceAdvance, real &tabAdvance, FontHandle *font);
 /// Outputs the glyph index corresponding to the specified Unicode character.
 bool getGlyphIndex(GlyphIndex &glyphIndex, FontHandle *font, unicode_t unicode);
 /// Loads the geometry of a glyph from a font file.
-bool loadGlyph(Shape &output, FontHandle *font, GlyphIndex glyphIndex, double *advance = NULL);
-bool loadGlyph(Shape &output, FontHandle *font, unicode_t unicode, double *advance = NULL);
+bool loadGlyph(Shape &output, FontHandle *font, GlyphIndex glyphIndex, real *advance = NULL);
+bool loadGlyph(Shape &output, FontHandle *font, unicode_t unicode, real *advance = NULL);
 /// Outputs the kerning distance adjustment between two specific glyphs.
-bool getKerning(double &output, FontHandle *font, GlyphIndex glyphIndex1, GlyphIndex glyphIndex2);
-bool getKerning(double &output, FontHandle *font, unicode_t unicode1, unicode_t unicode2);
+bool getKerning(real &output, FontHandle *font, GlyphIndex glyphIndex1, GlyphIndex glyphIndex2);
+bool getKerning(real &output, FontHandle *font, unicode_t unicode1, unicode_t unicode2);
 
 #ifndef MSDFGEN_DISABLE_VARIABLE_FONTS
 /// Sets a single variation axis of a variable font.
-bool setFontVariationAxis(FreetypeHandle *library, FontHandle *font, const char *name, double coordinate);
+bool setFontVariationAxis(FreetypeHandle *library, FontHandle *font, const char *name, real coordinate);
 /// Lists names and ranges of variation axes of a variable font.
 bool listFontVariationAxes(std::vector<FontVariationAxis> &axes, FreetypeHandle *library, FontHandle *font);
 #endif

+ 53 - 49
ext/import-svg.cpp

@@ -19,7 +19,7 @@
 #include "../core/arithmetics.hpp"
 
 #define ARC_SEGMENTS_PER_PI 2
-#define ENDPOINT_SNAP_RANGE_PROPORTION (1/16384.)
+#define ENDPOINT_SNAP_RANGE_PROPORTION (::msdfgen::real(1)/::msdfgen::real(16384))
 
 namespace msdfgen {
 
@@ -65,7 +65,7 @@ static bool readCoord(Point2 &output, const char *&pathDef) {
     return false;
 }
 
-static bool readDouble(double &output, const char *&pathDef) {
+static bool readReal(real &output, const char *&pathDef) {
     skipExtraChars(pathDef);
     int shift;
     double v;
@@ -89,51 +89,51 @@ static bool readBool(bool &output, const char *&pathDef) {
     return false;
 }
 
-static double arcAngle(Vector2 u, Vector2 v) {
-    return nonZeroSign(crossProduct(u, v))*acos(clamp(dotProduct(u, v)/(u.length()*v.length()), -1., +1.));
+static real arcAngle(Vector2 u, Vector2 v) {
+    return real(nonZeroSign(crossProduct(u, v)))*acos(clamp(dotProduct(u, v)/(u.length()*v.length()), real(-1), real(+1)));
 }
 
 static Vector2 rotateVector(Vector2 v, Vector2 direction) {
     return Vector2(direction.x*v.x-direction.y*v.y, direction.y*v.x+direction.x*v.y);
 }
 
-static void addArcApproximate(Contour &contour, Point2 startPoint, Point2 endPoint, Vector2 radius, double rotation, bool largeArc, bool sweep) {
+static void addArcApproximate(Contour &contour, Point2 startPoint, Point2 endPoint, Vector2 radius, real rotation, bool largeArc, bool sweep) {
     if (endPoint == startPoint)
         return;
-    if (radius.x == 0 || radius.y == 0)
+    if (radius.x == real(0) || radius.y == real(0))
         return contour.addEdge(new LinearSegment(startPoint, endPoint));
 
     radius.x = fabs(radius.x);
     radius.y = fabs(radius.y);
     Vector2 axis(cos(rotation), sin(rotation));
 
-    Vector2 rm = rotateVector(.5*(startPoint-endPoint), Vector2(axis.x, -axis.y));
+    Vector2 rm = rotateVector(real(.5)*(startPoint-endPoint), Vector2(axis.x, -axis.y));
     Vector2 rm2 = rm*rm;
     Vector2 radius2 = radius*radius;
-    double radiusGap = rm2.x/radius2.x+rm2.y/radius2.y;
-    if (radiusGap > 1) {
+    real radiusGap = rm2.x/radius2.x+rm2.y/radius2.y;
+    if (radiusGap > real(1)) {
         radius *= sqrt(radiusGap);
         radius2 = radius*radius;
     }
-    double dq = (radius2.x*rm2.y+radius2.y*rm2.x);
-    double pq = radius2.x*radius2.y/dq-1;
-    double q = (largeArc == sweep ? -1 : +1)*sqrt(max(pq, 0.));
+    real dq = (radius2.x*rm2.y+radius2.y*rm2.x);
+    real pq = radius2.x*radius2.y/dq-1;
+    real q = (largeArc == sweep ? real(-1) : real(+1))*sqrt(max(pq, real(0)));
     Vector2 rc(q*radius.x*rm.y/radius.y, -q*radius.y*rm.x/radius.x);
-    Point2 center = .5*(startPoint+endPoint)+rotateVector(rc, axis);
+    Point2 center = real(.5)*(startPoint+endPoint)+rotateVector(rc, axis);
 
-    double angleStart = arcAngle(Vector2(1, 0), (rm-rc)/radius);
-    double angleExtent = arcAngle((rm-rc)/radius, (-rm-rc)/radius);
-    if (!sweep && angleExtent > 0)
-        angleExtent -= 2*M_PI;
-    else if (sweep && angleExtent < 0)
-        angleExtent += 2*M_PI;
+    real angleStart = arcAngle(Vector2(1, 0), (rm-rc)/radius);
+    real angleExtent = arcAngle((rm-rc)/radius, (-rm-rc)/radius);
+    if (!sweep && angleExtent > real(0))
+        angleExtent -= real(2*M_PI);
+    else if (sweep && angleExtent < real(0))
+        angleExtent += real(2*M_PI);
 
-    int segments = (int) ceil(ARC_SEGMENTS_PER_PI/M_PI*fabs(angleExtent));
-    double angleIncrement = angleExtent/segments;
-    double cl = 4/3.*sin(.5*angleIncrement)/(1+cos(.5*angleIncrement));
+    int segments = (int) ceil(real(ARC_SEGMENTS_PER_PI)/real(M_PI)*fabs(angleExtent));
+    real angleIncrement = angleExtent/segments;
+    real cl = real(4)/real(3)*sin(real(.5)*angleIncrement)/(real(1)+cos(real(.5)*angleIncrement));
 
     Point2 prevNode = startPoint;
-    double angle = angleStart;
+    real angle = angleStart;
     for (int i = 0; i < segments; ++i) {
         Point2 controlPoint[2];
         Vector2 d(cos(angle), sin(angle));
@@ -187,7 +187,7 @@ static void findPathByBackwardIndex(tinyxml2::XMLElement *&path, int &flags, int
     }
 }
 
-bool buildShapeFromSvgPath(Shape &shape, const char *pathDef, double endpointSnapRange) {
+bool buildShapeFromSvgPath(Shape &shape, const char *pathDef, real endpointSnapRange) {
     char nodeType = '\0';
     char prevNodeType = '\0';
     Point2 prevNode(0, 0);
@@ -224,13 +224,13 @@ bool buildShapeFromSvgPath(Shape &shape, const char *pathDef, double endpointSna
                     contour.addEdge(new LinearSegment(prevNode, node));
                     break;
                 case 'H': case 'h':
-                    REQUIRE(readDouble(node.x, pathDef));
+                    REQUIRE(readReal(node.x, pathDef));
                     if (nodeType == 'h')
                         node.x += prevNode.x;
                     contour.addEdge(new LinearSegment(prevNode, node));
                     break;
                 case 'V': case 'v':
-                    REQUIRE(readDouble(node.y, pathDef));
+                    REQUIRE(readReal(node.y, pathDef));
                     if (nodeType == 'v')
                         node.y += prevNode.y;
                     contour.addEdge(new LinearSegment(prevNode, node));
@@ -281,17 +281,17 @@ bool buildShapeFromSvgPath(Shape &shape, const char *pathDef, double endpointSna
                 case 'A': case 'a':
                     {
                         Vector2 radius;
-                        double angle;
+                        real angle;
                         bool largeArg;
                         bool sweep;
                         REQUIRE(readCoord(radius, pathDef));
-                        REQUIRE(readDouble(angle, pathDef));
+                        REQUIRE(readReal(angle, pathDef));
                         REQUIRE(readBool(largeArg, pathDef));
                         REQUIRE(readBool(sweep, pathDef));
                         REQUIRE(readCoord(node, pathDef));
                         if (nodeType == 'a')
                             node += prevNode;
-                        angle *= M_PI/180.0;
+                        angle *= real(M_PI)/real(180);
                         addArcApproximate(contour, prevNode, node, radius, angle, largeArg, sweep);
                     }
                     break;
@@ -338,16 +338,16 @@ bool loadSvgShape(Shape &output, const char *filename, int pathIndex, Vector2 *d
     if (!pd)
         return false;
 
-    Vector2 dims(root->DoubleAttribute("width"), root->DoubleAttribute("height"));
-    double left, top;
+    double left, bottom;
+    double width = root->DoubleAttribute("width"), height = root->DoubleAttribute("height");
     const char *viewBox = root->Attribute("viewBox");
     if (viewBox)
-        sscanf(viewBox, "%lf %lf %lf %lf", &left, &top, &dims.x, &dims.y);
+        (void) sscanf(viewBox, "%lf %lf %lf %lf", &left, &bottom, &width, &height);
     if (dimensions)
-        *dimensions = dims;
+        *dimensions = Vector2(width, height);
     output.contours.clear();
     output.inverseYAxis = true;
-    return buildShapeFromSvgPath(output, pd, ENDPOINT_SNAP_RANGE_PROPORTION*dims.length());
+    return buildShapeFromSvgPath(output, pd, ENDPOINT_SNAP_RANGE_PROPORTION*Vector2(width, height).length());
 }
 
 #ifndef MSDFGEN_USE_SKIA
@@ -370,16 +370,18 @@ int loadSvgShape(Shape &output, Shape::Bounds &viewBox, const char *filename) {
     if (!pd)
         return SVG_IMPORT_FAILURE;
 
-    viewBox.l = 0, viewBox.b = 0;
-    Vector2 dims(root->DoubleAttribute("width"), root->DoubleAttribute("height"));
+    double left = 0, bottom = 0;
+    double width = root->DoubleAttribute("width"), height = root->DoubleAttribute("height");
     const char *viewBoxStr = root->Attribute("viewBox");
     if (viewBoxStr)
-        sscanf(viewBoxStr, "%lf %lf %lf %lf", &viewBox.l, &viewBox.b, &dims.x, &dims.y);
-    viewBox.r = viewBox.l+dims.x;
-    viewBox.t = viewBox.b+dims.y;
+        (void) sscanf(viewBoxStr, "%lf %lf %lf %lf", &left, &bottom, &width, &height);
+    viewBox.l = left;
+    viewBox.b = bottom;
+    viewBox.r = left+width;
+    viewBox.t = bottom+height;
     output.contours.clear();
     output.inverseYAxis = true;
-    if (!buildShapeFromSvgPath(output, pd, ENDPOINT_SNAP_RANGE_PROPORTION*dims.length()))
+    if (!buildShapeFromSvgPath(output, pd, ENDPOINT_SNAP_RANGE_PROPORTION*Vector2(width, height).length()))
         return SVG_IMPORT_FAILURE;
     return flags;
 }
@@ -397,8 +399,8 @@ static bool readTransformationOp(SkScalar dst[6], int &count, const char *&str,
             skipExtraChars(++curStr);
             count = 0;
             while (*curStr && *curStr != ')') {
-                double x;
-                if (!(count < 6 && readDouble(x, curStr)))
+                real x;
+                if (!(count < 6 && readReal(x, curStr)))
                     return false;
                 dst[count++] = SkScalar(x);
                 skipExtraChars(curStr);
@@ -435,9 +437,9 @@ static SkMatrix parseTransformation(int &flags, const char *str) {
             else
                 partial.setRotate(values[0]);
         } else if (readTransformationOp(values, count, str, "skewX") && count == 1) {
-            partial.setSkewX(SkScalar(tan(M_PI/180*values[0])));
+            partial.setSkewX(tan(SkScalar(M_PI)/SkScalar(180)*values[0]));
         } else if (readTransformationOp(values, count, str, "skewY") && count == 1) {
-            partial.setSkewY(SkScalar(tan(M_PI/180*values[0])));
+            partial.setSkewY(tan(SkScalar(M_PI)/SkScalar(180)*values[0]));
         } else {
             flags |= SVG_IMPORT_PARTIAL_FAILURE_FLAG;
             break;
@@ -545,13 +547,15 @@ int loadSvgShape(Shape &output, Shape::Bounds &viewBox, const char *filename) {
     output.inverseYAxis = true;
     output.orientContours();
 
-    viewBox.l = 0, viewBox.b = 0;
-    Vector2 dims(root->DoubleAttribute("width"), root->DoubleAttribute("height"));
+    double left = 0, bottom = 0;
+    double width = root->DoubleAttribute("width"), height = root->DoubleAttribute("height");
     const char *viewBoxStr = root->Attribute("viewBox");
     if (viewBoxStr)
-        sscanf(viewBoxStr, "%lf %lf %lf %lf", &viewBox.l, &viewBox.b, &dims.x, &dims.y);
-    viewBox.r = viewBox.l+dims.x;
-    viewBox.t = viewBox.b+dims.y;
+        (void) sscanf(viewBoxStr, "%lf %lf %lf %lf", &left, &bottom, &width, &height);
+    viewBox.l = left;
+    viewBox.b = bottom;
+    viewBox.r = left+width;
+    viewBox.t = bottom+height;
     return flags;
 }
 

+ 2 - 1
ext/import-svg.h

@@ -2,6 +2,7 @@
 #pragma once
 
 #include <cstddef>
+#include "../core/types.h"
 #include "../core/Shape.h"
 
 #ifndef MSDFGEN_DISABLE_SVG
@@ -20,7 +21,7 @@ extern MSDFGEN_EXT_PUBLIC const int SVG_IMPORT_UNSUPPORTED_FEATURE_FLAG;
 extern MSDFGEN_EXT_PUBLIC const int SVG_IMPORT_TRANSFORMATION_IGNORED_FLAG;
 
 /// Builds a shape from an SVG path string
-bool buildShapeFromSvgPath(Shape &shape, const char *pathDef, double endpointSnapRange = 0);
+bool buildShapeFromSvgPath(Shape &shape, const char *pathDef, real endpointSnapRange = 0);
 
 /// Reads a single <path> element found in the specified SVG file and converts it to output Shape
 bool loadSvgShape(Shape &output, const char *filename, int pathIndex = 0, Vector2 *dimensions = NULL);

+ 2 - 1
ext/resolve-shape-geometry.cpp

@@ -5,6 +5,7 @@
 
 #include <skia/core/SkPath.h>
 #include <skia/pathops/SkPathOps.h>
+#include "../core/types.h"
 #include "../core/Vector2.hpp"
 #include "../core/edge-segments.h"
 #include "../core/Contour.h"
@@ -16,7 +17,7 @@ SkPoint pointToSkiaPoint(Point2 p) {
 }
 
 Point2 pointFromSkiaPoint(const SkPoint p) {
-    return Point2((double) p.x(), (double) p.y());
+    return Point2((real) p.x(), (real) p.y());
 }
 
 void shapeToSkiaPath(SkPath &skPath, const Shape &shape) {

+ 1 - 0
ext/save-png.h

@@ -1,6 +1,7 @@
 
 #pragma once
 
+#include "../core/types.h"
 #include "../core/BitmapRef.hpp"
 
 #ifndef MSDFGEN_DISABLE_PNG

+ 55 - 47
main.cpp

@@ -23,7 +23,7 @@
 #include "core/ShapeDistanceFinder.h"
 
 #define SDF_ERROR_ESTIMATE_PRECISION 19
-#define DEFAULT_ANGLE_THRESHOLD 3.
+#define DEFAULT_ANGLE_THRESHOLD ::msdfgen::real(3)
 
 #if defined(MSDFGEN_EXTENSIONS) && !defined(MSDFGEN_DISABLE_PNG)
 #define DEFAULT_IMAGE_EXTENSION "png"
@@ -73,18 +73,26 @@ static bool parseUnsignedLL(unsigned long long &value, const char *arg) {
     return sscanf(arg, "%llu%c", &value, &c) == 1;
 }
 
-static bool parseDouble(double &value, const char *arg) {
+static bool parseReal(real &value, const char *arg) {
     char c;
-    return sscanf(arg, "%lf%c", &value, &c) == 1;
+    double dblValue;
+    if (sscanf(arg, "%lf%c", &dblValue, &c) == 1) {
+        value = dblValue;
+        return true;
+    }
+    return false;
 }
 
-static bool parseAngle(double &value, const char *arg) {
+static bool parseAngle(real &value, const char *arg) {
     char c1, c2;
-    int result = sscanf(arg, "%lf%c%c", &value, &c1, &c2);
-    if (result == 1)
+    double dblValue;
+    int result = sscanf(arg, "%lf%c%c", &dblValue, &c1, &c2);
+    if (result == 1) {
+        value = real(dblValue);
         return true;
+    }
     if (result == 2 && (c1 == 'd' || c1 == 'D')) {
-        value *= M_PI/180;
+        value = real(M_PI)/real(180)*real(dblValue);
         return true;
     }
     return false;
@@ -162,7 +170,7 @@ static FontHandle *loadVarFont(FreetypeHandle *library, const char *filename) {
                 double value = 0;
                 int skip = 0;
                 if (sscanf(++filename, "%lf%n", &value, &skip) == 1) {
-                    setFontVariationAxis(library, font, buffer.c_str(), value);
+                    setFontVariationAxis(library, font, buffer.c_str(), real(value));
                     filename += skip;
                 }
             }
@@ -542,12 +550,12 @@ int main(int argc, const char *const *argv) {
         RANGE_UNIT,
         RANGE_PX
     } rangeMode = RANGE_PX;
-    double range = 1;
-    double pxRange = 2;
+    real range = 1;
+    real pxRange = 2;
     Vector2 translate;
     Vector2 scale = 1;
     bool scaleSpecified = false;
-    double angleThreshold = DEFAULT_ANGLE_THRESHOLD;
+    real angleThreshold = DEFAULT_ANGLE_THRESHOLD;
     float outputDistanceShift = 0.f;
     const char *edgeAssignment = NULL;
     bool yFlip = false;
@@ -560,7 +568,7 @@ int main(int argc, const char *const *argv) {
         GUESS
     } orientation = KEEP;
     unsigned long long coloringSeed = 0;
-    void (*edgeColoring)(Shape &, double, unsigned long long) = edgeColoringSimple;
+    void (*edgeColoring)(Shape &, real, unsigned long long) = edgeColoringSimple;
     bool explicitErrorCorrectionMode = false;
 
     int argPos = 1;
@@ -741,8 +749,8 @@ int main(int argc, const char *const *argv) {
             continue;
         }
         ARG_CASE("-range", 1) {
-            double r;
-            if (!(parseDouble(r, argv[argPos+1]) && r > 0))
+            real r;
+            if (!(parseReal(r, argv[argPos+1]) && r > real(0)))
                 ABORT("Invalid range argument. Use -range <range> with a positive real number.");
             rangeMode = RANGE_UNIT;
             range = r;
@@ -750,8 +758,8 @@ int main(int argc, const char *const *argv) {
             continue;
         }
         ARG_CASE("-pxrange", 1) {
-            double r;
-            if (!(parseDouble(r, argv[argPos+1]) && r > 0))
+            real r;
+            if (!(parseReal(r, argv[argPos+1]) && r > real(0)))
                 ABORT("Invalid range argument. Use -pxrange <range> with a positive real number.");
             rangeMode = RANGE_PX;
             pxRange = r;
@@ -759,8 +767,8 @@ int main(int argc, const char *const *argv) {
             continue;
         }
         ARG_CASE("-scale", 1) {
-            double s;
-            if (!(parseDouble(s, argv[argPos+1]) && s > 0))
+            real s;
+            if (!(parseReal(s, argv[argPos+1]) && s > real(0)))
                 ABORT("Invalid scale argument. Use -scale <scale> with a positive real number.");
             scale = s;
             scaleSpecified = true;
@@ -768,8 +776,8 @@ int main(int argc, const char *const *argv) {
             continue;
         }
         ARG_CASE("-ascale", 2) {
-            double sx, sy;
-            if (!(parseDouble(sx, argv[argPos+1]) && parseDouble(sy, argv[argPos+2]) && sx > 0 && sy > 0))
+            real sx, sy;
+            if (!(parseReal(sx, argv[argPos+1]) && parseReal(sy, argv[argPos+2]) && sx > real(0) && sy > real(0)))
                 ABORT("Invalid scale arguments. Use -ascale <x> <y> with two positive real numbers.");
             scale.set(sx, sy);
             scaleSpecified = true;
@@ -777,15 +785,15 @@ int main(int argc, const char *const *argv) {
             continue;
         }
         ARG_CASE("-translate", 2) {
-            double tx, ty;
-            if (!(parseDouble(tx, argv[argPos+1]) && parseDouble(ty, argv[argPos+2])))
+            real tx, ty;
+            if (!(parseReal(tx, argv[argPos+1]) && parseReal(ty, argv[argPos+2])))
                 ABORT("Invalid translate arguments. Use -translate <x> <y> with two real numbers.");
             translate.set(tx, ty);
             argPos += 3;
             continue;
         }
         ARG_CASE("-angle", 1) {
-            double at;
+            real at;
             if (!parseAngle(at, argv[argPos+1]))
                 ABORT("Invalid angle threshold. Use -angle <min angle> with a positive real number less than PI or a value in degrees followed by 'd' below 180d.");
             angleThreshold = at;
@@ -827,16 +835,16 @@ int main(int argc, const char *const *argv) {
             continue;
         }
         ARG_CASE("-errordeviationratio", 1) {
-            double edr;
-            if (!(parseDouble(edr, argv[argPos+1]) && edr > 0))
+            real edr;
+            if (!(parseReal(edr, argv[argPos+1]) && edr > real(0)))
                 ABORT("Invalid error deviation ratio. Use -errordeviationratio <ratio> with a positive real number.");
             generatorConfig.errorCorrection.minDeviationRatio = edr;
             argPos += 2;
             continue;
         }
         ARG_CASE("-errorimproveratio", 1) {
-            double eir;
-            if (!(parseDouble(eir, argv[argPos+1]) && eir > 0))
+            real eir;
+            if (!(parseReal(eir, argv[argPos+1]) && eir > real(0)))
                 ABORT("Invalid error improvement ratio. Use -errorimproveratio <ratio> with a positive real number.");
             generatorConfig.errorCorrection.minImproveRatio = eir;
             argPos += 2;
@@ -865,8 +873,8 @@ int main(int argc, const char *const *argv) {
             continue;
         }
         ARG_CASE("-distanceshift", 1) {
-            double ds;
-            if (!parseDouble(ds, argv[argPos+1]))
+            real ds;
+            if (!parseReal(ds, argv[argPos+1]))
                 ABORT("Invalid distance shift. Use -distanceshift <shift> with a real value.");
             outputDistanceShift = (float) ds;
             argPos += 2;
@@ -948,7 +956,7 @@ int main(int argc, const char *const *argv) {
 
     // Load input
     Shape::Bounds svgViewBox = { };
-    double glyphAdvance = 0;
+    real glyphAdvance = 0;
     if (!inputType || !input) {
         #ifdef MSDFGEN_EXTENSIONS
             #ifdef MSDFGEN_DISABLE_SVG
@@ -1057,35 +1065,35 @@ int main(int argc, const char *const *argv) {
     if (yFlip)
         shape.inverseYAxis = !shape.inverseYAxis;
 
-    double avgScale = .5*(scale.x+scale.y);
+    real avgScale = real(.5)*(scale.x+scale.y);
     Shape::Bounds bounds = { };
     if (autoFrame || mode == METRICS || printMetrics || orientation == GUESS)
         bounds = shape.getBounds();
 
     // Auto-frame
     if (autoFrame) {
-        double l = bounds.l, b = bounds.b, r = bounds.r, t = bounds.t;
+        real l = bounds.l, b = bounds.b, r = bounds.r, t = bounds.t;
         Vector2 frame(width, height);
-        double m = .5+(double) outputDistanceShift;
+        real m = real(.5)+real(outputDistanceShift);
         if (!scaleSpecified) {
             if (rangeMode == RANGE_UNIT)
                 l -= m*range, b -= m*range, r += m*range, t += m*range;
             else
-                frame -= 2*m*pxRange;
+                frame -= real(2)*m*pxRange;
         }
         if (l >= r || b >= t)
             l = 0, b = 0, r = 1, t = 1;
-        if (frame.x <= 0 || frame.y <= 0)
+        if (frame.x <= real(0) || frame.y <= real(0))
             ABORT("Cannot fit the specified pixel range.");
         Vector2 dims(r-l, t-b);
         if (scaleSpecified)
-            translate = .5*(frame/scale-dims)-Vector2(l, b);
+            translate = real(.5)*(frame/scale-dims)-Vector2(l, b);
         else {
             if (dims.x*frame.y < dims.y*frame.x) {
-                translate.set(.5*(frame.x/frame.y*dims.y-dims.x)-l, -b);
+                translate.set(real(.5)*(frame.x/frame.y*dims.y-dims.x)-l, -b);
                 scale = avgScale = frame.y/dims.y;
             } else {
-                translate.set(-l, .5*(frame.y/frame.x*dims.x-dims.y)-b);
+                translate.set(-l, real(.5)*(frame.y/frame.x*dims.x-dims.y)-b);
                 scale = avgScale = frame.x/dims.x;
             }
         }
@@ -1106,18 +1114,18 @@ int main(int argc, const char *const *argv) {
         if (shape.inverseYAxis)
             fprintf(out, "inverseY = true\n");
         if (svgViewBox.l < svgViewBox.r && svgViewBox.b < svgViewBox.t)
-            fprintf(out, "view box = %.17g, %.17g, %.17g, %.17g\n", svgViewBox.l, svgViewBox.b, svgViewBox.r, svgViewBox.t);
+            fprintf(out, "view box = %.17g, %.17g, %.17g, %.17g\n", double(svgViewBox.l), double(svgViewBox.b), double(svgViewBox.r), double(svgViewBox.t));
         if (bounds.l < bounds.r && bounds.b < bounds.t)
-            fprintf(out, "bounds = %.17g, %.17g, %.17g, %.17g\n", bounds.l, bounds.b, bounds.r, bounds.t);
+            fprintf(out, "bounds = %.17g, %.17g, %.17g, %.17g\n", double(bounds.l), double(bounds.b), double(bounds.r), double(bounds.t));
         if (glyphAdvance != 0)
-            fprintf(out, "advance = %.17g\n", glyphAdvance);
+            fprintf(out, "advance = %.17g\n", double(glyphAdvance));
         if (autoFrame) {
             if (!scaleSpecified)
-                fprintf(out, "scale = %.17g\n", avgScale);
-            fprintf(out, "translate = %.17g, %.17g\n", translate.x, translate.y);
+                fprintf(out, "scale = %.17g\n", double(avgScale));
+            fprintf(out, "translate = %.17g, %.17g\n", double(translate.x), double(translate.y));
         }
         if (rangeMode == RANGE_PX)
-            fprintf(out, "range = %.17g\n", range);
+            fprintf(out, "range = %.17g\n", double(range));
         if (mode == METRICS && outputSpecified)
             fclose(out);
     }
@@ -1188,9 +1196,9 @@ int main(int argc, const char *const *argv) {
 
     if (orientation == GUESS) {
         // Get sign of signed distance outside bounds
-        Point2 p(bounds.l-(bounds.r-bounds.l)-1, bounds.b-(bounds.t-bounds.b)-1);
-        double distance = SimpleTrueShapeDistanceFinder::oneShotDistance(shape, p);
-        orientation = distance <= 0 ? KEEP : REVERSE;
+        Point2 p(bounds.l-(bounds.r-bounds.l)-real(1), bounds.b-(bounds.t-bounds.b)-real(1));
+        real distance = SimpleTrueShapeDistanceFinder::oneShotDistance(shape, p);
+        orientation = distance <= real(0) ? KEEP : REVERSE;
     }
     if (orientation == REVERSE) {
         switch (mode) {

+ 13 - 12
msdfgen.h

@@ -15,6 +15,7 @@
  *
  */
 
+#include "core/types.h"
 #include "core/arithmetics.hpp"
 #include "core/Vector2.hpp"
 #include "core/Projection.h"
@@ -37,27 +38,27 @@
 namespace msdfgen {
 
 /// Generates a conventional single-channel signed distance field.
-void generateSDF(const BitmapRef<float, 1> &output, const Shape &shape, const Projection &projection, double range, const GeneratorConfig &config = GeneratorConfig());
+void generateSDF(const BitmapRef<float, 1> &output, const Shape &shape, const Projection &projection, real range, const GeneratorConfig &config = GeneratorConfig());
 
 /// Generates a single-channel signed pseudo-distance field.
-void generatePseudoSDF(const BitmapRef<float, 1> &output, const Shape &shape, const Projection &projection, double range, const GeneratorConfig &config = GeneratorConfig());
+void generatePseudoSDF(const BitmapRef<float, 1> &output, const Shape &shape, const Projection &projection, real range, const GeneratorConfig &config = GeneratorConfig());
 
 /// Generates a multi-channel signed distance field. Edge colors must be assigned first! (See edgeColoringSimple)
-void generateMSDF(const BitmapRef<float, 3> &output, const Shape &shape, const Projection &projection, double range, const MSDFGeneratorConfig &config = MSDFGeneratorConfig());
+void generateMSDF(const BitmapRef<float, 3> &output, const Shape &shape, const Projection &projection, real range, const MSDFGeneratorConfig &config = MSDFGeneratorConfig());
 
 /// Generates a multi-channel signed distance field with true distance in the alpha channel. Edge colors must be assigned first.
-void generateMTSDF(const BitmapRef<float, 4> &output, const Shape &shape, const Projection &projection, double range, const MSDFGeneratorConfig &config = MSDFGeneratorConfig());
+void generateMTSDF(const BitmapRef<float, 4> &output, const Shape &shape, const Projection &projection, real range, const MSDFGeneratorConfig &config = MSDFGeneratorConfig());
 
 // Old version of the function API's kept for backwards compatibility
-void generateSDF(const BitmapRef<float, 1> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, bool overlapSupport = true);
-void generatePseudoSDF(const BitmapRef<float, 1> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, bool overlapSupport = true);
-void generateMSDF(const BitmapRef<float, 3> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, const ErrorCorrectionConfig &errorCorrectionConfig = ErrorCorrectionConfig(), bool overlapSupport = true);
-void generateMTSDF(const BitmapRef<float, 4> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, const ErrorCorrectionConfig &errorCorrectionConfig = ErrorCorrectionConfig(), bool overlapSupport = true);
+void generateSDF(const BitmapRef<float, 1> &output, const Shape &shape, real range, const Vector2 &scale, const Vector2 &translate, bool overlapSupport = true);
+void generatePseudoSDF(const BitmapRef<float, 1> &output, const Shape &shape, real range, const Vector2 &scale, const Vector2 &translate, bool overlapSupport = true);
+void generateMSDF(const BitmapRef<float, 3> &output, const Shape &shape, real range, const Vector2 &scale, const Vector2 &translate, const ErrorCorrectionConfig &errorCorrectionConfig = ErrorCorrectionConfig(), bool overlapSupport = true);
+void generateMTSDF(const BitmapRef<float, 4> &output, const Shape &shape, real range, const Vector2 &scale, const Vector2 &translate, const ErrorCorrectionConfig &errorCorrectionConfig = ErrorCorrectionConfig(), bool overlapSupport = true);
 
 // Original simpler versions of the previous functions, which work well under normal circumstances, but cannot deal with overlapping contours.
-void generateSDF_legacy(const BitmapRef<float, 1> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate);
-void generatePseudoSDF_legacy(const BitmapRef<float, 1> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate);
-void generateMSDF_legacy(const BitmapRef<float, 3> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, ErrorCorrectionConfig errorCorrectionConfig = ErrorCorrectionConfig());
-void generateMTSDF_legacy(const BitmapRef<float, 4> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, ErrorCorrectionConfig errorCorrectionConfig = ErrorCorrectionConfig());
+void generateSDF_legacy(const BitmapRef<float, 1> &output, const Shape &shape, real range, const Vector2 &scale, const Vector2 &translate);
+void generatePseudoSDF_legacy(const BitmapRef<float, 1> &output, const Shape &shape, real range, const Vector2 &scale, const Vector2 &translate);
+void generateMSDF_legacy(const BitmapRef<float, 3> &output, const Shape &shape, real range, const Vector2 &scale, const Vector2 &translate, ErrorCorrectionConfig errorCorrectionConfig = ErrorCorrectionConfig());
+void generateMTSDF_legacy(const BitmapRef<float, 4> &output, const Shape &shape, real range, const Vector2 &scale, const Vector2 &translate, ErrorCorrectionConfig errorCorrectionConfig = ErrorCorrectionConfig());
 
 }