Browse Source

Scale and translation arguments refactored as Projection, generator configuration WIP

Viktor Chlumský 4 năm trước cách đây
mục cha
commit
37a1fff421

+ 5 - 2
Msdfgen.vcxproj

@@ -464,9 +464,11 @@
     <ClInclude Include="core\EdgeColor.h" />
     <ClInclude Include="core\EdgeHolder.h" />
     <ClInclude Include="core\equation-solver.h" />
+    <ClInclude Include="core\generator-config.h" />
     <ClInclude Include="core\msdf-error-correction.h" />
+    <ClInclude Include="core\Projection.h" />
     <ClInclude Include="core\sdf-error-estimation.h" />
-    <ClInclude Include="core\msdf-edge-artifact-patcher.h" />
+    <ClInclude Include="core\msdf-artifact-patcher.h" />
     <ClInclude Include="core\pixel-conversion.hpp" />
     <ClInclude Include="core\rasterization.h" />
     <ClInclude Include="core\render-sdf.h" />
@@ -496,8 +498,9 @@
     <ClCompile Include="core\EdgeHolder.cpp" />
     <ClCompile Include="core\equation-solver.cpp" />
     <ClCompile Include="core\msdf-error-correction.cpp" />
+    <ClCompile Include="core\Projection.cpp" />
     <ClCompile Include="core\sdf-error-estimation.cpp" />
-    <ClCompile Include="core\msdf-edge-artifact-patcher.cpp" />
+    <ClCompile Include="core\msdf-artifact-patcher.cpp" />
     <ClCompile Include="core\rasterization.cpp" />
     <ClCompile Include="core\render-sdf.cpp" />
     <ClCompile Include="core\save-bmp.cpp" />

+ 15 - 6
Msdfgen.vcxproj.filters

@@ -117,12 +117,18 @@
     <ClInclude Include="core\msdf-error-correction.h">
       <Filter>Core</Filter>
     </ClInclude>
-    <ClInclude Include="core\msdf-edge-artifact-patcher.h">
-      <Filter>Core</Filter>
-    </ClInclude>
     <ClInclude Include="ext\resolve-shape-geometry.h">
       <Filter>Extensions</Filter>
     </ClInclude>
+    <ClInclude Include="core\Projection.h">
+      <Filter>Core</Filter>
+    </ClInclude>
+    <ClInclude Include="core\msdf-artifact-patcher.h">
+      <Filter>Core</Filter>
+    </ClInclude>
+    <ClInclude Include="core\generator-config.h">
+      <Filter>Core</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="main.cpp">
@@ -200,12 +206,15 @@
     <ClCompile Include="core\msdf-error-correction.cpp">
       <Filter>Core</Filter>
     </ClCompile>
-    <ClCompile Include="core\msdf-edge-artifact-patcher.cpp">
-      <Filter>Core</Filter>
-    </ClCompile>
     <ClCompile Include="ext\resolve-shape-geometry.cpp">
       <Filter>Extensions</Filter>
     </ClCompile>
+    <ClCompile Include="core\msdf-artifact-patcher.cpp">
+      <Filter>Core</Filter>
+    </ClCompile>
+    <ClCompile Include="core\Projection.cpp">
+      <Filter>Core</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ResourceCompile Include="Msdfgen.rc">

+ 42 - 0
core/Projection.cpp

@@ -0,0 +1,42 @@
+
+#include "Projection.h"
+
+namespace msdfgen {
+
+Projection::Projection() : scale(1), translate(0) { }
+
+Projection::Projection(const Vector2 &scale, const Vector2 &translate) : scale(scale), translate(translate) { }
+
+Point2 Projection::project(const Point2 &coord) const {
+    return scale*(coord+translate);
+}
+
+Point2 Projection::unproject(const Point2 &coord) const {
+    return coord/scale-translate;
+}
+
+Vector2 Projection::projectVector(const Vector2 &vector) const {
+    return scale*vector;
+}
+
+Vector2 Projection::unprojectVector(const Vector2 &vector) const {
+    return vector/scale;
+}
+
+double Projection::projectX(double x) const {
+    return scale.x*(x+translate.x);
+}
+
+double Projection::projectY(double y) const {
+    return scale.y*(y+translate.y);
+}
+
+double Projection::unprojectX(double x) const {
+    return x/scale.x-translate.x;
+}
+
+double Projection::unprojectY(double y) const {
+    return y/scale.y-translate.y;
+}
+
+}

+ 37 - 0
core/Projection.h

@@ -0,0 +1,37 @@
+
+#pragma once
+
+#include "Vector2.h"
+
+namespace msdfgen {
+
+/// A transformation from shape coordinates to pixel coordinates.
+class Projection {
+
+public:
+    Projection();
+    Projection(const Vector2 &scale, const Vector2 &translate);
+    /// Converts the shape coordinate to pixel coordinate.
+    Point2 project(const Point2 &coord) const;
+    /// Converts the pixel coordinate to shape coordinate.
+    Point2 unproject(const Point2 &coord) const;
+    /// Converts the vector to pixel coordinate space.
+    Vector2 projectVector(const Vector2 &vector) const;
+    /// 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;
+    /// Converts the Y-coordinate from shape to pixel coordinate space.
+    double projectY(double y) const;
+    /// Converts the X-coordinate from pixel to shape coordinate space.
+    double unprojectX(double x) const;
+    /// Converts the Y-coordinate from pixel to shape coordinate space.
+    double unprojectY(double y) const;
+
+private:
+    Vector2 scale;
+    Vector2 translate;
+
+};
+
+}

+ 31 - 0
core/generator-config.h

@@ -0,0 +1,31 @@
+
+#pragma once
+
+#define MSDFGEN_DEFAULT_ARTIFACT_PATCHER_MIN_IMPROVE_RATIO 1.001
+
+namespace msdfgen {
+
+/// The configuration of the distance field generator algorithm.
+struct GeneratorConfig {
+    /// Specifies whether to use the version of the algorithm that supports overlapping contours with the same winding. May be set to false to improve performance when no such contours are present.
+    bool overlapSupport;
+
+    inline explicit GeneratorConfig(bool overlapSupport = true) : overlapSupport(overlapSupport) { }
+};
+
+/// The configuration of the artifact patcher that processes the generated MSDF and fixes potential interpolation errors.
+struct ArtifactPatcherConfig {
+    /// The mode of operation.
+    enum Mode {
+        DISABLED,
+        INDISCRIMINATE,
+        EDGE_PRIORITY,
+        EDGE_ONLY
+    } mode;
+    /// The minimum ratio of improvement required to patch a pixel. Must be greater than or equal to 1.
+    double minImproveRatio;
+
+    inline explicit ArtifactPatcherConfig(Mode mode = EDGE_PRIORITY, double minImproveRatio = MSDFGEN_DEFAULT_ARTIFACT_PATCHER_MIN_IMPROVE_RATIO) : mode(mode), minImproveRatio(minImproveRatio) { }
+};
+
+}

+ 11 - 11
core/msdf-edge-artifact-patcher.cpp → core/msdf-artifact-patcher.cpp

@@ -1,5 +1,5 @@
 
-#include "msdf-edge-artifact-patcher.h"
+#include "msdf-artifact-patcher.h"
 
 #include <cstring>
 #include <vector>
@@ -118,14 +118,14 @@ void findHotspots(std::vector<Point2> &hotspots, const BitmapConstRef<float, N>
 }
 
 template <template <typename> class ContourCombiner, int N>
-static void msdfPatchEdgeArtifactsInner(const BitmapRef<float, N> &sdf, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate) {
+static void msdfPatchArtifactsInner(const BitmapRef<float, N> &sdf, const Shape &shape, const Projection &projection, double range) {
     ShapeDistanceFinder<ContourCombiner<PseudoDistanceSelector> > distanceFinder(shape);
     std::vector<Point2> hotspots;
     findHotspots(hotspots, BitmapConstRef<float, N>(sdf));
     std::vector<std::pair<int, int> > artifacts;
     artifacts.reserve(hotspots.size());
     for (std::vector<Point2>::const_iterator hotspot = hotspots.begin(); hotspot != hotspots.end(); ++hotspot) {
-        Point2 pos = *hotspot/scale-translate;
+        Point2 pos = projection.unproject(*hotspot);
         double actualDistance = distanceFinder.distance(pos);
         float sd = float(actualDistance/range+.5);
 
@@ -157,18 +157,18 @@ static void msdfPatchEdgeArtifactsInner(const BitmapRef<float, N> &sdf, const Sh
     }
 }
 
-void msdfPatchEdgeArtifacts(const BitmapRef<float, 3> &sdf, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, bool overlapSupport) {
-    if (overlapSupport)
-        msdfPatchEdgeArtifactsInner<OverlappingContourCombiner>(sdf, shape, range, scale, translate);
+void msdfPatchArtifacts(const BitmapRef<float, 3> &sdf, const Shape &shape, const Projection &projection, double range, const GeneratorConfig &generatorConfig, const ArtifactPatcherConfig &config) {
+    if (generatorConfig.overlapSupport)
+        msdfPatchArtifactsInner<OverlappingContourCombiner>(sdf, shape, projection, range);
     else
-        msdfPatchEdgeArtifactsInner<SimpleContourCombiner>(sdf, shape, range, scale, translate);
+        msdfPatchArtifactsInner<SimpleContourCombiner>(sdf, shape, projection, range);
 }
 
-void msdfPatchEdgeArtifacts(const BitmapRef<float, 4> &sdf, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, bool overlapSupport) {
-    if (overlapSupport)
-        msdfPatchEdgeArtifactsInner<OverlappingContourCombiner>(sdf, shape, range, scale, translate);
+void msdfPatchArtifacts(const BitmapRef<float, 4> &sdf, const Shape &shape, const Projection &projection, double range, const GeneratorConfig &generatorConfig, const ArtifactPatcherConfig &config) {
+    if (generatorConfig.overlapSupport)
+        msdfPatchArtifactsInner<OverlappingContourCombiner>(sdf, shape, projection, range);
     else
-        msdfPatchEdgeArtifactsInner<SimpleContourCombiner>(sdf, shape, range, scale, translate);
+        msdfPatchArtifactsInner<SimpleContourCombiner>(sdf, shape, projection, range);
 }
 
 }

+ 15 - 0
core/msdf-artifact-patcher.h

@@ -0,0 +1,15 @@
+
+#pragma once
+
+#include "Vector2.h"
+#include "Shape.h"
+#include "Projection.h"
+#include "BitmapRef.hpp"
+#include "generator-config.h"
+
+namespace msdfgen {
+
+void msdfPatchArtifacts(const BitmapRef<float, 3> &sdf, const Shape &shape, const Projection &projection, double range, const GeneratorConfig &generatorConfig = GeneratorConfig(), const ArtifactPatcherConfig &config = ArtifactPatcherConfig());
+void msdfPatchArtifacts(const BitmapRef<float, 4> &sdf, const Shape &shape, const Projection &projection, double range, const GeneratorConfig &generatorConfig = GeneratorConfig(), const ArtifactPatcherConfig &config = ArtifactPatcherConfig());
+
+}

+ 0 - 13
core/msdf-edge-artifact-patcher.h

@@ -1,13 +0,0 @@
-
-#pragma once
-
-#include "Vector2.h"
-#include "Shape.h"
-#include "BitmapRef.hpp"
-
-namespace msdfgen {
-
-void msdfPatchEdgeArtifacts(const BitmapRef<float, 3> &sdf, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, bool overlapSupport = true);
-void msdfPatchEdgeArtifacts(const BitmapRef<float, 4> &sdf, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, bool overlapSupport = true);
-
-}

+ 7 - 0
core/msdf-error-correction.cpp

@@ -74,4 +74,11 @@ void msdfErrorCorrection(const BitmapRef<float, 4> &output, const Vector2 &thres
     msdfErrorCorrectionInner(output, threshold);
 }
 
+void msdfErrorCorrection(const BitmapRef<float, 3> &output, double threshold, const Projection &projection, double range) {
+    msdfErrorCorrectionInner(output, projection.unprojectVector(Vector2(threshold/range)));
+}
+void msdfErrorCorrection(const BitmapRef<float, 4> &output, double threshold, const Projection &projection, double range) {
+    msdfErrorCorrectionInner(output, projection.unprojectVector(Vector2(threshold/range)));
+}
+
 }

+ 5 - 0
core/msdf-error-correction.h

@@ -2,6 +2,7 @@
 #pragma once
 
 #include "Vector2.h"
+#include "Projection.h"
 #include "BitmapRef.hpp"
 
 #define MSDFGEN_DEFAULT_ERROR_CORRECTION_THRESHOLD 1.001
@@ -12,4 +13,8 @@ namespace msdfgen {
 void msdfErrorCorrection(const BitmapRef<float, 3> &output, const Vector2 &threshold);
 void msdfErrorCorrection(const BitmapRef<float, 4> &output, const Vector2 &threshold);
 
+// Alternate API - threshold specified in pixels
+void msdfErrorCorrection(const BitmapRef<float, 3> &output, double threshold, const Projection &projection, double range);
+void msdfErrorCorrection(const BitmapRef<float, 4> &output, double threshold, const Projection &projection, double range);
+
 }

+ 43 - 27
core/msdfgen.cpp

@@ -5,7 +5,7 @@
 #include "edge-selectors.h"
 #include "contour-combiners.h"
 #include "ShapeDistanceFinder.h"
-#include "msdf-edge-artifact-patcher.h"
+#include "msdf-artifact-patcher.h"
 
 namespace msdfgen {
 
@@ -45,23 +45,21 @@ public:
 };
 
 template <class ContourCombiner>
-void generateDistanceField(const typename DistancePixelConversion<typename ContourCombiner::DistanceType>::BitmapRefType &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate) {
+void generateDistanceField(const typename DistancePixelConversion<typename ContourCombiner::DistanceType>::BitmapRefType &output, const Shape &shape, const Projection &projection, double range) {
 #ifdef MSDFGEN_USE_OPENMP
     #pragma omp parallel
 #endif
     {
         ShapeDistanceFinder<ContourCombiner> distanceFinder(shape);
         bool rightToLeft = false;
-        Point2 p;
 #ifdef MSDFGEN_USE_OPENMP
         #pragma omp for
 #endif
         for (int y = 0; y < output.height; ++y) {
             int row = shape.inverseYAxis ? output.height-y-1 : y;
-            p.y = (y+.5)/scale.y-translate.y;
             for (int col = 0; col < output.width; ++col) {
                 int x = rightToLeft ? output.width-col-1 : col;
-                p.x = (x+.5)/scale.x-translate.x;
+                Point2 p = projection.unproject(Point2(x+.5, y+.5));
                 typename ContourCombiner::DistanceType distance = distanceFinder.distance(p);
                 DistancePixelConversion<typename ContourCombiner::DistanceType>::convert(output(x, row), distance, range);
             }
@@ -70,38 +68,56 @@ void generateDistanceField(const typename DistancePixelConversion<typename Conto
     }
 }
 
-void generateSDF(const BitmapRef<float, 1> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, bool overlapSupport) {
-    if (overlapSupport)
-        generateDistanceField<OverlappingContourCombiner<TrueDistanceSelector> >(output, shape, range, scale, translate);
+void generateSDF(const BitmapRef<float, 1> &output, const Shape &shape, const Projection &projection, double range, const GeneratorConfig &config) {
+    if (config.overlapSupport)
+        generateDistanceField<OverlappingContourCombiner<TrueDistanceSelector> >(output, shape, projection, range);
     else
-        generateDistanceField<SimpleContourCombiner<TrueDistanceSelector> >(output, shape, range, scale, translate);
+        generateDistanceField<SimpleContourCombiner<TrueDistanceSelector> >(output, shape, projection, range);
 }
 
-void generatePseudoSDF(const BitmapRef<float, 1> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, bool overlapSupport) {
-    if (overlapSupport)
-        generateDistanceField<OverlappingContourCombiner<PseudoDistanceSelector> >(output, shape, range, scale, translate);
+void generatePseudoSDF(const BitmapRef<float, 1> &output, const Shape &shape, const Projection &projection, double range, const GeneratorConfig &config) {
+    if (config.overlapSupport)
+        generateDistanceField<OverlappingContourCombiner<PseudoDistanceSelector> >(output, shape, projection, range);
     else
-        generateDistanceField<SimpleContourCombiner<PseudoDistanceSelector> >(output, shape, range, scale, translate);
+        generateDistanceField<SimpleContourCombiner<PseudoDistanceSelector> >(output, shape, projection, range);
 }
 
-void generateMSDF(const BitmapRef<float, 3> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, double edgeThreshold, bool overlapSupport) {
-    if (overlapSupport)
-        generateDistanceField<OverlappingContourCombiner<MultiDistanceSelector> >(output, shape, range, scale, translate);
+void generateMSDF(const BitmapRef<float, 3> &output, const Shape &shape, const Projection &projection, double range, const GeneratorConfig &config, const ArtifactPatcherConfig &artifactPatcherConfig) {
+    if (config.overlapSupport)
+        generateDistanceField<OverlappingContourCombiner<MultiDistanceSelector> >(output, shape, projection, range);
     else
-        generateDistanceField<SimpleContourCombiner<MultiDistanceSelector> >(output, shape, range, scale, translate);
-    if (edgeThreshold > 0)
-        msdfErrorCorrection(output, edgeThreshold/(scale*range));
-    msdfPatchEdgeArtifacts(output, shape, range, scale, translate, overlapSupport);
+        generateDistanceField<SimpleContourCombiner<MultiDistanceSelector> >(output, shape, projection, range);
+    if (artifactPatcherConfig.minImproveRatio > 0) // TEMPORARILY SERVES AS ERROR CORRECTION THRESHOLD
+        msdfErrorCorrection(output, artifactPatcherConfig.minImproveRatio, projection, range);
+    msdfPatchArtifacts(output, shape, projection, range, config, artifactPatcherConfig);
 }
 
-void generateMTSDF(const BitmapRef<float, 4> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, double edgeThreshold, bool overlapSupport) {
-    if (overlapSupport)
-        generateDistanceField<OverlappingContourCombiner<MultiAndTrueDistanceSelector> >(output, shape, range, scale, translate);
+void generateMTSDF(const BitmapRef<float, 4> &output, const Shape &shape, const Projection &projection, double range, const GeneratorConfig &config, const ArtifactPatcherConfig &artifactPatcherConfig) {
+    if (config.overlapSupport)
+        generateDistanceField<OverlappingContourCombiner<MultiAndTrueDistanceSelector> >(output, shape, projection, range);
     else
-        generateDistanceField<SimpleContourCombiner<MultiAndTrueDistanceSelector> >(output, shape, range, scale, translate);
-    if (edgeThreshold > 0)
-        msdfErrorCorrection(output, edgeThreshold/(scale*range));
-    msdfPatchEdgeArtifacts(output, shape, range, scale, translate, overlapSupport);
+        generateDistanceField<SimpleContourCombiner<MultiAndTrueDistanceSelector> >(output, shape, projection, range);
+    if (artifactPatcherConfig.minImproveRatio > 0) // TEMPORARILY SERVES AS ERROR CORRECTION THRESHOLD
+        msdfErrorCorrection(output, artifactPatcherConfig.minImproveRatio, projection, range);
+    msdfPatchArtifacts(output, shape, projection, range, config, artifactPatcherConfig);
+}
+
+// Legacy API
+
+void generateSDF(const BitmapRef<float, 1> &output, const Shape &shape, double 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) {
+    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, double errorMinImproveRatio, bool overlapSupport) {
+    generateMSDF(output, shape, Projection(scale, translate), range, GeneratorConfig(overlapSupport), ArtifactPatcherConfig(ArtifactPatcherConfig::EDGE_PRIORITY, errorMinImproveRatio));
+}
+
+void generateMTSDF(const BitmapRef<float, 4> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, double errorMinImproveRatio, bool overlapSupport) {
+    generateMTSDF(output, shape, Projection(scale, translate), range, GeneratorConfig(overlapSupport), ArtifactPatcherConfig(ArtifactPatcherConfig::EDGE_PRIORITY, errorMinImproveRatio));
 }
 
 // Legacy version

+ 30 - 23
core/rasterization.cpp

@@ -6,31 +6,23 @@
 
 namespace msdfgen {
 
-void rasterize(const BitmapRef<float, 1> &output, const Shape &shape, const Vector2 &scale, const Vector2 &translate, FillRule fillRule) {
-    Point2 p;
+void rasterize(const BitmapRef<float, 1> &output, const Shape &shape, const Projection &projection, FillRule fillRule) {
     Scanline scanline;
     for (int y = 0; y < output.height; ++y) {
         int row = shape.inverseYAxis ? output.height-y-1 : y;
-        p.y = (y+.5)/scale.y-translate.y;
-        shape.scanline(scanline, p.y);
-        for (int x = 0; x < output.width; ++x) {
-            p.x = (x+.5)/scale.x-translate.x;
-            bool fill = scanline.filled(p.x, fillRule);
-            *output(x, row) = (float) fill;
-        }
+        shape.scanline(scanline, projection.unprojectY(y+.5));
+        for (int x = 0; x < output.width; ++x)
+            *output(x, row) = (float) scanline.filled(projection.unprojectX(x+.5), fillRule);
     }
 }
 
-void distanceSignCorrection(const BitmapRef<float, 1> &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, FillRule fillRule) {
-    Point2 p;
+void distanceSignCorrection(const BitmapRef<float, 1> &sdf, const Shape &shape, const Projection &projection, FillRule fillRule) {
     Scanline scanline;
     for (int y = 0; y < sdf.height; ++y) {
         int row = shape.inverseYAxis ? sdf.height-y-1 : y;
-        p.y = (y+.5)/scale.y-translate.y;
-        shape.scanline(scanline, p.y);
+        shape.scanline(scanline, projection.unprojectY(y+.5));
         for (int x = 0; x < sdf.width; ++x) {
-            p.x = (x+.5)/scale.x-translate.x;
-            bool fill = scanline.filled(p.x, fillRule);
+            bool fill = scanline.filled(projection.unprojectX(x+.5), fillRule);
             float &sd = *sdf(x, row);
             if ((sd > .5f) != fill)
                 sd = 1.f-sd;
@@ -39,11 +31,10 @@ void distanceSignCorrection(const BitmapRef<float, 1> &sdf, const Shape &shape,
 }
 
 template <int N>
-static void multiDistanceSignCorrection(const BitmapRef<float, N> &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, FillRule fillRule) {
+static void multiDistanceSignCorrection(const BitmapRef<float, N> &sdf, const Shape &shape, const Projection &projection, FillRule fillRule) {
     int w = sdf.width, h = sdf.height;
     if (!(w*h))
         return;
-    Point2 p;
     Scanline scanline;
     bool ambiguous = false;
     std::vector<char> matchMap;
@@ -51,11 +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;
-        p.y = (y+.5)/scale.y-translate.y;
-        shape.scanline(scanline, p.y);
+        shape.scanline(scanline, projection.unprojectY(y+.5));
         for (int x = 0; x < w; ++x) {
-            p.x = (x+.5)/scale.x-translate.x;
-            bool fill = scanline.filled(p.x, fillRule);
+            bool fill = scanline.filled(projection.unprojectX(x+.5), fillRule);
             float *msd = sdf(x, row);
             float sd = median(msd[0], msd[1], msd[2]);
             if (sd == .5f)
@@ -97,12 +86,30 @@ static void multiDistanceSignCorrection(const BitmapRef<float, N> &sdf, const Sh
     }
 }
 
+void distanceSignCorrection(const BitmapRef<float, 3> &sdf, const Shape &shape, const Projection &projection, FillRule fillRule) {
+    multiDistanceSignCorrection(sdf, shape, projection, fillRule);
+}
+
+void distanceSignCorrection(const BitmapRef<float, 4> &sdf, const Shape &shape, const Projection &projection, FillRule fillRule) {
+    multiDistanceSignCorrection(sdf, shape, projection, fillRule);
+}
+
+// Legacy API
+
+void rasterize(const BitmapRef<float, 1> &output, const Shape &shape, const Vector2 &scale, const Vector2 &translate, FillRule fillRule) {
+    rasterize(output, shape, Projection(scale, translate), fillRule);
+}
+
+void distanceSignCorrection(const BitmapRef<float, 1> &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, FillRule fillRule) {
+    distanceSignCorrection(sdf, shape, Projection(scale, translate), fillRule);
+}
+
 void distanceSignCorrection(const BitmapRef<float, 3> &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, FillRule fillRule) {
-    multiDistanceSignCorrection(sdf, shape, scale, translate, fillRule);
+    distanceSignCorrection(sdf, shape, Projection(scale, translate), fillRule);
 }
 
 void distanceSignCorrection(const BitmapRef<float, 4> &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, FillRule fillRule) {
-    multiDistanceSignCorrection(sdf, shape, scale, translate, fillRule);
+    distanceSignCorrection(sdf, shape, Projection(scale, translate), fillRule);
 }
 
 }

+ 9 - 2
core/rasterization.h

@@ -2,15 +2,22 @@
 #pragma once
 
 #include "Vector2.h"
-#include "Scanline.h"
 #include "Shape.h"
+#include "Projection.h"
+#include "Scanline.h"
 #include "BitmapRef.hpp"
 
 namespace msdfgen {
 
 /// Rasterizes the shape into a monochrome bitmap.
-void rasterize(const BitmapRef<float, 1> &output, const Shape &shape, const Vector2 &scale, const Vector2 &translate, FillRule fillRule = FILL_NONZERO);
+void rasterize(const BitmapRef<float, 1> &output, const Shape &shape, const Projection &projection, FillRule fillRule = FILL_NONZERO);
 /// Fixes the sign of the input signed distance field, so that it matches the shape's rasterized fill.
+void distanceSignCorrection(const BitmapRef<float, 1> &sdf, const Shape &shape, const Projection &projection, FillRule fillRule = FILL_NONZERO);
+void distanceSignCorrection(const BitmapRef<float, 3> &sdf, const Shape &shape, const Projection &projection, FillRule fillRule = FILL_NONZERO);
+void distanceSignCorrection(const BitmapRef<float, 4> &sdf, const Shape &shape, const Projection &projection, FillRule fillRule = FILL_NONZERO);
+
+// Old version of the function API's kept for backwards compatibility
+void rasterize(const BitmapRef<float, 1> &output, const Shape &shape, const Vector2 &scale, const Vector2 &translate, FillRule fillRule = FILL_NONZERO);
 void distanceSignCorrection(const BitmapRef<float, 1> &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, FillRule fillRule = FILL_NONZERO);
 void distanceSignCorrection(const BitmapRef<float, 3> &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, FillRule fillRule = FILL_NONZERO);
 void distanceSignCorrection(const BitmapRef<float, 4> &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, FillRule fillRule = FILL_NONZERO);

+ 44 - 18
core/sdf-error-estimation.cpp

@@ -6,10 +6,10 @@
 
 namespace msdfgen {
 
-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 Projection &projection, double y, bool inverseYAxis) {
     if (!(sdf.width > 0 && sdf.height > 0))
         return line.setIntersections(std::vector<Scanline::Intersection>());
-    double pixelY = clamp(scale.x*(y+translate.y)-.5, double(sdf.height-1));
+    double pixelY = clamp(projection.projectY(y)-.5, double(sdf.height-1));
     if (inverseYAxis)
         pixelY = sdf.height-1-pixelY;
     int b = (int) floor(pixelY);
@@ -33,7 +33,7 @@ void scanlineSDF(Scanline &line, const BitmapConstRef<float, 1> &sdf, const Vect
         if (lv != rv) {
             double lr = double(.5f-lv)/double(rv-lv);
             if (lr >= 0 && lr <= 1) {
-                Scanline::Intersection intersection = { (l+lr+.5)/scale.x-translate.x, sign(rv-lv) };
+                Scanline::Intersection intersection = { projection.unprojectX(l+lr+.5), sign(rv-lv) };
                 intersections.push_back(intersection);
             }
         }
@@ -46,10 +46,10 @@ void scanlineSDF(Scanline &line, const BitmapConstRef<float, 1> &sdf, const Vect
 }
 
 template <int N>
-void scanlineMSDF(Scanline &line, const BitmapConstRef<float, N> &sdf, const Vector2 &scale, const Vector2 &translate, bool inverseYAxis, double y) {
+void scanlineMSDF(Scanline &line, const BitmapConstRef<float, N> &sdf, const Projection &projection, double y, bool inverseYAxis) {
     if (!(sdf.width > 0 && sdf.height > 0))
         return line.setIntersections(std::vector<Scanline::Intersection>());
-    double pixelY = clamp(scale.x*(y+translate.y)-.5, double(sdf.height-1));
+    double pixelY = clamp(projection.projectY(y)-.5, double(sdf.height-1));
     if (inverseYAxis)
         pixelY = sdf.height-1-pixelY;
     int b = (int) floor(pixelY);
@@ -87,7 +87,7 @@ void scanlineMSDF(Scanline &line, const BitmapConstRef<float, N> &sdf, const Vec
                         mix(lv[2], rv[2], lr)
                     };
                     if (median(v[0], v[1], v[2]) == v[i]) {
-                        newIntersections[newIntersectionCount].x = (l+lr+.5)/scale.x-translate.x;
+                        newIntersections[newIntersectionCount].x = projection.unprojectX(l+lr+.5);
                         newIntersections[newIntersectionCount].direction = sign(rv[i]-lv[i]);
                         ++newIntersectionCount;
                     }
@@ -124,43 +124,69 @@ void scanlineMSDF(Scanline &line, const BitmapConstRef<float, N> &sdf, const Vec
 #endif
 }
 
-void scanlineSDF(Scanline &line, const BitmapConstRef<float, 3> &sdf, const Vector2 &scale, const Vector2 &translate, bool inverseYAxis, double y) {
-    scanlineMSDF(line, sdf, scale, translate, inverseYAxis, y);
+void scanlineSDF(Scanline &line, const BitmapConstRef<float, 3> &sdf, const Projection &projection, double y, bool inverseYAxis) {
+    scanlineMSDF(line, sdf, projection, y, inverseYAxis);
 }
-void scanlineSDF(Scanline &line, const BitmapConstRef<float, 4> &sdf, const Vector2 &scale, const Vector2 &translate, bool inverseYAxis, double y) {
-    scanlineMSDF(line, sdf, scale, translate, inverseYAxis, y);
+void scanlineSDF(Scanline &line, const BitmapConstRef<float, 4> &sdf, const Projection &projection, double y, bool inverseYAxis) {
+    scanlineMSDF(line, sdf, projection, y, inverseYAxis);
 }
 
 template <int N>
-double estimateSDFErrorInner(const BitmapConstRef<float, N> &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, int scanlinesPerRow, FillRule fillRule) {
+double 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 = .5/scale.x-translate.x;
-    double xTo = (sdf.width-.5)/scale.x-translate.x;
+    double xFrom = projection.unprojectX(.5);
+    double xTo = projection.unprojectX(sdf.width-.5);
     double overlapFactor = 1/(xTo-xFrom);
     double 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 = (row+bt+.5)/scale.y-translate.y;
+            double y = projection.unprojectY(row+bt+.5);
             shape.scanline(refScanline, y);
-            scanlineSDF(sdfScanline, sdf, scale, translate, shape.inverseYAxis, y);
+            scanlineSDF(sdfScanline, sdf, projection, y, shape.inverseYAxis);
             error += 1-overlapFactor*Scanline::overlap(refScanline, sdfScanline, xFrom, xTo, fillRule);
         }
     }
     return error/((sdf.height-1)*scanlinesPerRow);
 }
 
+double 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) {
+    return estimateSDFErrorInner(sdf, shape, projection, scanlinesPerRow, fillRule);
+}
+double 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) {
+    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) {
+    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) {
+    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) {
-    return estimateSDFErrorInner(sdf, shape, scale, translate, scanlinesPerRow, 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) {
-    return estimateSDFErrorInner(sdf, shape, scale, translate, scanlinesPerRow, 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) {
-    return estimateSDFErrorInner(sdf, shape, scale, translate, scanlinesPerRow, fillRule);
+    return estimateSDFError(sdf, shape, Projection(scale, translate), scanlinesPerRow, fillRule);
 }
 
 }

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

@@ -2,18 +2,27 @@
 #pragma once
 
 #include "Vector2.h"
-#include "Scanline.h"
 #include "Shape.h"
+#include "Projection.h"
+#include "Scanline.h"
 #include "BitmapRef.hpp"
 
 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);
+
+/// 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);
+
+// 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);
-
-/// 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 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);

+ 17 - 13
main.cpp

@@ -398,7 +398,8 @@ int main(int argc, const char * const *argv) {
             false
         #endif
     );
-    bool overlapSupport = !geometryPreproc;
+    GeneratorConfig generatorConfig;
+    generatorConfig.overlapSupport = !geometryPreproc;
     bool scanlinePass = !geometryPreproc;
     FillRule fillRule = FILL_NONZERO;
     Format format = AUTO;
@@ -427,6 +428,7 @@ int main(int argc, const char * const *argv) {
     bool scaleSpecified = false;
     double angleThreshold = DEFAULT_ANGLE_THRESHOLD;
     double errorCorrectionThreshold = MSDFGEN_DEFAULT_ERROR_CORRECTION_THRESHOLD;
+    ArtifactPatcherConfig artifactPatcherConfig;
     float outputDistanceShift = 0.f;
     const char *edgeAssignment = NULL;
     bool yFlip = false;
@@ -524,12 +526,12 @@ int main(int argc, const char * const *argv) {
             continue;
         }
         ARG_CASE("-nooverlap", 0) {
-            overlapSupport = false;
+            generatorConfig.overlapSupport = false;
             argPos += 1;
             continue;
         }
         ARG_CASE("-overlap", 0) {
-            overlapSupport = true;
+            generatorConfig.overlapSupport = true;
             argPos += 1;
             continue;
         }
@@ -879,16 +881,18 @@ int main(int argc, const char * const *argv) {
     }
 
     // Compute output
+    Projection projection(scale, translate);
     Bitmap<float, 1> sdf;
     Bitmap<float, 3> msdf;
     Bitmap<float, 4> mtsdf;
+    artifactPatcherConfig.minImproveRatio = errorCorrectionThreshold; // TEMPORARILY SERVES AS ERROR CORRECTION THRESHOLD
     switch (mode) {
         case SINGLE: {
             sdf = Bitmap<float, 1>(width, height);
             if (legacyMode)
                 generateSDF_legacy(sdf, shape, range, scale, translate);
             else
-                generateSDF(sdf, shape, range, scale, translate, overlapSupport);
+                generateSDF(sdf, shape, projection, range, generatorConfig);
             break;
         }
         case PSEUDO: {
@@ -896,7 +900,7 @@ int main(int argc, const char * const *argv) {
             if (legacyMode)
                 generatePseudoSDF_legacy(sdf, shape, range, scale, translate);
             else
-                generatePseudoSDF(sdf, shape, range, scale, translate, overlapSupport);
+                generatePseudoSDF(sdf, shape, projection, range, generatorConfig);
             break;
         }
         case MULTI: {
@@ -908,7 +912,7 @@ int main(int argc, const char * const *argv) {
             if (legacyMode)
                 generateMSDF_legacy(msdf, shape, range, scale, translate, scanlinePass ? 0 : errorCorrectionThreshold);
             else
-                generateMSDF(msdf, shape, range, scale, translate, errorCorrectionThreshold, overlapSupport);
+                generateMSDF(msdf, shape, projection, range, generatorConfig, artifactPatcherConfig);
             break;
         }
         case MULTI_AND_TRUE: {
@@ -920,7 +924,7 @@ int main(int argc, const char * const *argv) {
             if (legacyMode)
                 generateMTSDF_legacy(mtsdf, shape, range, scale, translate, scanlinePass ? 0 : errorCorrectionThreshold);
             else
-                generateMTSDF(mtsdf, shape, range, scale, translate, errorCorrectionThreshold, overlapSupport);
+                generateMTSDF(mtsdf, shape, projection, range, generatorConfig, artifactPatcherConfig);
             break;
         }
         default:;
@@ -951,15 +955,15 @@ int main(int argc, const char * const *argv) {
         switch (mode) {
             case SINGLE:
             case PSEUDO:
-                distanceSignCorrection(sdf, shape, scale, translate, fillRule);
+                distanceSignCorrection(sdf, shape, projection, fillRule);
                 break;
             case MULTI:
-                distanceSignCorrection(msdf, shape, scale, translate, fillRule);
+                distanceSignCorrection(msdf, shape, projection, fillRule);
                 if (errorCorrectionThreshold > 0)
                     msdfErrorCorrection(msdf, errorCorrectionThreshold/(scale*range));
                 break;
             case MULTI_AND_TRUE:
-                distanceSignCorrection(mtsdf, shape, scale, translate, fillRule);
+                distanceSignCorrection(mtsdf, shape, projection, fillRule);
                 if (errorCorrectionThreshold > 0)
                     msdfErrorCorrection(mtsdf, errorCorrectionThreshold/(scale*range));
                 break;
@@ -1007,7 +1011,7 @@ int main(int argc, const char * const *argv) {
             if (is8bitFormat(format) && (testRenderMulti || testRender || estimateError))
                 simulate8bit(sdf);
             if (estimateError) {
-                double sdfError = estimateSDFError(sdf, shape, scale, translate, SDF_ERROR_ESTIMATE_PRECISION, fillRule);
+                double sdfError = estimateSDFError(sdf, shape, projection, SDF_ERROR_ESTIMATE_PRECISION, fillRule);
                 printf("SDF error ~ %e\n", sdfError);
             }
             if (testRenderMulti) {
@@ -1030,7 +1034,7 @@ int main(int argc, const char * const *argv) {
             if (is8bitFormat(format) && (testRenderMulti || testRender || estimateError))
                 simulate8bit(msdf);
             if (estimateError) {
-                double sdfError = estimateSDFError(msdf, shape, scale, translate, SDF_ERROR_ESTIMATE_PRECISION, fillRule);
+                double sdfError = estimateSDFError(msdf, shape, projection, SDF_ERROR_ESTIMATE_PRECISION, fillRule);
                 printf("SDF error ~ %e\n", sdfError);
             }
             if (testRenderMulti) {
@@ -1053,7 +1057,7 @@ int main(int argc, const char * const *argv) {
             if (is8bitFormat(format) && (testRenderMulti || testRender || estimateError))
                 simulate8bit(mtsdf);
             if (estimateError) {
-                double sdfError = estimateSDFError(mtsdf, shape, scale, translate, SDF_ERROR_ESTIMATE_PRECISION, fillRule);
+                double sdfError = estimateSDFError(mtsdf, shape, projection, SDF_ERROR_ESTIMATE_PRECISION, fillRule);
                 printf("SDF error ~ %e\n", sdfError);
             }
             if (testRenderMulti) {

+ 12 - 4
msdfgen.h

@@ -17,6 +17,7 @@
 
 #include "core/arithmetics.hpp"
 #include "core/Vector2.h"
+#include "core/Projection.h"
 #include "core/Scanline.h"
 #include "core/Shape.h"
 #include "core/BitmapRef.hpp"
@@ -24,6 +25,7 @@
 #include "core/bitmap-interpolation.hpp"
 #include "core/pixel-conversion.hpp"
 #include "core/edge-coloring.h"
+#include "core/generator-config.h"
 #include "core/msdf-error-correction.h"
 #include "core/render-sdf.h"
 #include "core/rasterization.h"
@@ -37,16 +39,22 @@
 namespace msdfgen {
 
 /// Generates a conventional single-channel signed distance field.
-void generateSDF(const BitmapRef<float, 1> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, bool overlapSupport = true);
+void generateSDF(const BitmapRef<float, 1> &output, const Shape &shape, const Projection &projection, double range, const GeneratorConfig &config = GeneratorConfig());
 
 /// Generates a single-channel signed pseudo-distance field.
-void generatePseudoSDF(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, const Projection &projection, double 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, double range, const Vector2 &scale, const Vector2 &translate, double edgeThreshold = MSDFGEN_DEFAULT_ERROR_CORRECTION_THRESHOLD, bool overlapSupport = true);
+void generateMSDF(const BitmapRef<float, 3> &output, const Shape &shape, const Projection &projection, double range, const GeneratorConfig &config = GeneratorConfig(), const ArtifactPatcherConfig &artifactPatcherConfig = ArtifactPatcherConfig());
 
 /// 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, double range, const Vector2 &scale, const Vector2 &translate, double edgeThreshold = MSDFGEN_DEFAULT_ERROR_CORRECTION_THRESHOLD, bool overlapSupport = true);
+void generateMTSDF(const BitmapRef<float, 4> &output, const Shape &shape, const Projection &projection, double range, const GeneratorConfig &config = GeneratorConfig(), const ArtifactPatcherConfig &artifactPatcherConfig = ArtifactPatcherConfig());
+
+// 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, double errorMinImproveRatio = MSDFGEN_DEFAULT_ARTIFACT_PATCHER_MIN_IMPROVE_RATIO, bool overlapSupport = true);
+void generateMTSDF(const BitmapRef<float, 4> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, double errorMinImproveRatio = MSDFGEN_DEFAULT_ARTIFACT_PATCHER_MIN_IMPROVE_RATIO, 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);