Przeglądaj źródła

Scale and translation arguments refactored as Projection, generator configuration WIP

Viktor Chlumský 4 lat temu
rodzic
commit
37a1fff421

+ 5 - 2
Msdfgen.vcxproj

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

+ 15 - 6
Msdfgen.vcxproj.filters

@@ -117,12 +117,18 @@
     <ClInclude Include="core\msdf-error-correction.h">
     <ClInclude Include="core\msdf-error-correction.h">
       <Filter>Core</Filter>
       <Filter>Core</Filter>
     </ClInclude>
     </ClInclude>
-    <ClInclude Include="core\msdf-edge-artifact-patcher.h">
-      <Filter>Core</Filter>
-    </ClInclude>
     <ClInclude Include="ext\resolve-shape-geometry.h">
     <ClInclude Include="ext\resolve-shape-geometry.h">
       <Filter>Extensions</Filter>
       <Filter>Extensions</Filter>
     </ClInclude>
     </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>
   <ItemGroup>
   <ItemGroup>
     <ClCompile Include="main.cpp">
     <ClCompile Include="main.cpp">
@@ -200,12 +206,15 @@
     <ClCompile Include="core\msdf-error-correction.cpp">
     <ClCompile Include="core\msdf-error-correction.cpp">
       <Filter>Core</Filter>
       <Filter>Core</Filter>
     </ClCompile>
     </ClCompile>
-    <ClCompile Include="core\msdf-edge-artifact-patcher.cpp">
-      <Filter>Core</Filter>
-    </ClCompile>
     <ClCompile Include="ext\resolve-shape-geometry.cpp">
     <ClCompile Include="ext\resolve-shape-geometry.cpp">
       <Filter>Extensions</Filter>
       <Filter>Extensions</Filter>
     </ClCompile>
     </ClCompile>
+    <ClCompile Include="core\msdf-artifact-patcher.cpp">
+      <Filter>Core</Filter>
+    </ClCompile>
+    <ClCompile Include="core\Projection.cpp">
+      <Filter>Core</Filter>
+    </ClCompile>
   </ItemGroup>
   </ItemGroup>
   <ItemGroup>
   <ItemGroup>
     <ResourceCompile Include="Msdfgen.rc">
     <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 <cstring>
 #include <vector>
 #include <vector>
@@ -118,14 +118,14 @@ void findHotspots(std::vector<Point2> &hotspots, const BitmapConstRef<float, N>
 }
 }
 
 
 template <template <typename> class ContourCombiner, int 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);
     ShapeDistanceFinder<ContourCombiner<PseudoDistanceSelector> > distanceFinder(shape);
     std::vector<Point2> hotspots;
     std::vector<Point2> hotspots;
     findHotspots(hotspots, BitmapConstRef<float, N>(sdf));
     findHotspots(hotspots, BitmapConstRef<float, N>(sdf));
     std::vector<std::pair<int, int> > artifacts;
     std::vector<std::pair<int, int> > artifacts;
     artifacts.reserve(hotspots.size());
     artifacts.reserve(hotspots.size());
     for (std::vector<Point2>::const_iterator hotspot = hotspots.begin(); hotspot != hotspots.end(); ++hotspot) {
     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);
         double actualDistance = distanceFinder.distance(pos);
         float sd = float(actualDistance/range+.5);
         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
     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
     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);
     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
 #pragma once
 
 
 #include "Vector2.h"
 #include "Vector2.h"
+#include "Projection.h"
 #include "BitmapRef.hpp"
 #include "BitmapRef.hpp"
 
 
 #define MSDFGEN_DEFAULT_ERROR_CORRECTION_THRESHOLD 1.001
 #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, 3> &output, const Vector2 &threshold);
 void msdfErrorCorrection(const BitmapRef<float, 4> &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 "edge-selectors.h"
 #include "contour-combiners.h"
 #include "contour-combiners.h"
 #include "ShapeDistanceFinder.h"
 #include "ShapeDistanceFinder.h"
-#include "msdf-edge-artifact-patcher.h"
+#include "msdf-artifact-patcher.h"
 
 
 namespace msdfgen {
 namespace msdfgen {
 
 
@@ -45,23 +45,21 @@ public:
 };
 };
 
 
 template <class ContourCombiner>
 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
 #ifdef MSDFGEN_USE_OPENMP
     #pragma omp parallel
     #pragma omp parallel
 #endif
 #endif
     {
     {
         ShapeDistanceFinder<ContourCombiner> distanceFinder(shape);
         ShapeDistanceFinder<ContourCombiner> distanceFinder(shape);
         bool rightToLeft = false;
         bool rightToLeft = false;
-        Point2 p;
 #ifdef MSDFGEN_USE_OPENMP
 #ifdef MSDFGEN_USE_OPENMP
         #pragma omp for
         #pragma omp for
 #endif
 #endif
         for (int y = 0; y < output.height; ++y) {
         for (int y = 0; y < output.height; ++y) {
             int row = shape.inverseYAxis ? output.height-y-1 : 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) {
             for (int col = 0; col < output.width; ++col) {
                 int x = rightToLeft ? output.width-col-1 : 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);
                 typename ContourCombiner::DistanceType distance = distanceFinder.distance(p);
                 DistancePixelConversion<typename ContourCombiner::DistanceType>::convert(output(x, row), distance, range);
                 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
     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
     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
     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
     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
 // Legacy version

+ 30 - 23
core/rasterization.cpp

@@ -6,31 +6,23 @@
 
 
 namespace msdfgen {
 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;
     Scanline scanline;
     for (int y = 0; y < output.height; ++y) {
     for (int y = 0; y < output.height; ++y) {
         int row = shape.inverseYAxis ? output.height-y-1 : 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;
     Scanline scanline;
     for (int y = 0; y < sdf.height; ++y) {
     for (int y = 0; y < sdf.height; ++y) {
         int row = shape.inverseYAxis ? sdf.height-y-1 : 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) {
         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);
             float &sd = *sdf(x, row);
             if ((sd > .5f) != fill)
             if ((sd > .5f) != fill)
                 sd = 1.f-sd;
                 sd = 1.f-sd;
@@ -39,11 +31,10 @@ void distanceSignCorrection(const BitmapRef<float, 1> &sdf, const Shape &shape,
 }
 }
 
 
 template <int N>
 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;
     int w = sdf.width, h = sdf.height;
     if (!(w*h))
     if (!(w*h))
         return;
         return;
-    Point2 p;
     Scanline scanline;
     Scanline scanline;
     bool ambiguous = false;
     bool ambiguous = false;
     std::vector<char> matchMap;
     std::vector<char> matchMap;
@@ -51,11 +42,9 @@ static void multiDistanceSignCorrection(const BitmapRef<float, N> &sdf, const Sh
     char *match = &matchMap[0];
     char *match = &matchMap[0];
     for (int y = 0; y < h; ++y) {
     for (int y = 0; y < h; ++y) {
         int row = shape.inverseYAxis ? h-y-1 : 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) {
         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 *msd = sdf(x, row);
             float sd = median(msd[0], msd[1], msd[2]);
             float sd = median(msd[0], msd[1], msd[2]);
             if (sd == .5f)
             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) {
 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) {
 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
 #pragma once
 
 
 #include "Vector2.h"
 #include "Vector2.h"
-#include "Scanline.h"
 #include "Shape.h"
 #include "Shape.h"
+#include "Projection.h"
+#include "Scanline.h"
 #include "BitmapRef.hpp"
 #include "BitmapRef.hpp"
 
 
 namespace msdfgen {
 namespace msdfgen {
 
 
 /// Rasterizes the shape into a monochrome bitmap.
 /// 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.
 /// 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, 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, 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);
 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 {
 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))
     if (!(sdf.width > 0 && sdf.height > 0))
         return line.setIntersections(std::vector<Scanline::Intersection>());
         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)
     if (inverseYAxis)
         pixelY = sdf.height-1-pixelY;
         pixelY = sdf.height-1-pixelY;
     int b = (int) floor(pixelY);
     int b = (int) floor(pixelY);
@@ -33,7 +33,7 @@ void scanlineSDF(Scanline &line, const BitmapConstRef<float, 1> &sdf, const Vect
         if (lv != rv) {
         if (lv != rv) {
             double lr = double(.5f-lv)/double(rv-lv);
             double lr = double(.5f-lv)/double(rv-lv);
             if (lr >= 0 && lr <= 1) {
             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);
                 intersections.push_back(intersection);
             }
             }
         }
         }
@@ -46,10 +46,10 @@ void scanlineSDF(Scanline &line, const BitmapConstRef<float, 1> &sdf, const Vect
 }
 }
 
 
 template <int N>
 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))
     if (!(sdf.width > 0 && sdf.height > 0))
         return line.setIntersections(std::vector<Scanline::Intersection>());
         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)
     if (inverseYAxis)
         pixelY = sdf.height-1-pixelY;
         pixelY = sdf.height-1-pixelY;
     int b = (int) floor(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)
                         mix(lv[2], rv[2], lr)
                     };
                     };
                     if (median(v[0], v[1], v[2]) == v[i]) {
                     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]);
                         newIntersections[newIntersectionCount].direction = sign(rv[i]-lv[i]);
                         ++newIntersectionCount;
                         ++newIntersectionCount;
                     }
                     }
@@ -124,43 +124,69 @@ void scanlineMSDF(Scanline &line, const BitmapConstRef<float, N> &sdf, const Vec
 #endif
 #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>
 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)
     if (sdf.width <= 1 || sdf.height <= 1 || scanlinesPerRow < 1)
         return 0;
         return 0;
     double subRowSize = 1./scanlinesPerRow;
     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 overlapFactor = 1/(xTo-xFrom);
     double error = 0;
     double error = 0;
     Scanline refScanline, sdfScanline;
     Scanline refScanline, sdfScanline;
     for (int row = 0; row < sdf.height-1; ++row) {
     for (int row = 0; row < sdf.height-1; ++row) {
         for (int subRow = 0; subRow < scanlinesPerRow; ++subRow) {
         for (int subRow = 0; subRow < scanlinesPerRow; ++subRow) {
             double bt = (subRow+.5)*subRowSize;
             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);
             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);
             error += 1-overlapFactor*Scanline::overlap(refScanline, sdfScanline, xFrom, xTo, fillRule);
         }
         }
     }
     }
     return error/((sdf.height-1)*scanlinesPerRow);
     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) {
 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) {
 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) {
 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
 #pragma once
 
 
 #include "Vector2.h"
 #include "Vector2.h"
-#include "Scanline.h"
 #include "Shape.h"
 #include "Shape.h"
+#include "Projection.h"
+#include "Scanline.h"
 #include "BitmapRef.hpp"
 #include "BitmapRef.hpp"
 
 
 namespace msdfgen {
 namespace msdfgen {
 
 
 /// Analytically constructs a scanline at y evaluating fill by linear interpolation of the SDF.
 /// 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, 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, 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);
 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, 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, 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);
 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
             false
         #endif
         #endif
     );
     );
-    bool overlapSupport = !geometryPreproc;
+    GeneratorConfig generatorConfig;
+    generatorConfig.overlapSupport = !geometryPreproc;
     bool scanlinePass = !geometryPreproc;
     bool scanlinePass = !geometryPreproc;
     FillRule fillRule = FILL_NONZERO;
     FillRule fillRule = FILL_NONZERO;
     Format format = AUTO;
     Format format = AUTO;
@@ -427,6 +428,7 @@ int main(int argc, const char * const *argv) {
     bool scaleSpecified = false;
     bool scaleSpecified = false;
     double angleThreshold = DEFAULT_ANGLE_THRESHOLD;
     double angleThreshold = DEFAULT_ANGLE_THRESHOLD;
     double errorCorrectionThreshold = MSDFGEN_DEFAULT_ERROR_CORRECTION_THRESHOLD;
     double errorCorrectionThreshold = MSDFGEN_DEFAULT_ERROR_CORRECTION_THRESHOLD;
+    ArtifactPatcherConfig artifactPatcherConfig;
     float outputDistanceShift = 0.f;
     float outputDistanceShift = 0.f;
     const char *edgeAssignment = NULL;
     const char *edgeAssignment = NULL;
     bool yFlip = false;
     bool yFlip = false;
@@ -524,12 +526,12 @@ int main(int argc, const char * const *argv) {
             continue;
             continue;
         }
         }
         ARG_CASE("-nooverlap", 0) {
         ARG_CASE("-nooverlap", 0) {
-            overlapSupport = false;
+            generatorConfig.overlapSupport = false;
             argPos += 1;
             argPos += 1;
             continue;
             continue;
         }
         }
         ARG_CASE("-overlap", 0) {
         ARG_CASE("-overlap", 0) {
-            overlapSupport = true;
+            generatorConfig.overlapSupport = true;
             argPos += 1;
             argPos += 1;
             continue;
             continue;
         }
         }
@@ -879,16 +881,18 @@ int main(int argc, const char * const *argv) {
     }
     }
 
 
     // Compute output
     // Compute output
+    Projection projection(scale, translate);
     Bitmap<float, 1> sdf;
     Bitmap<float, 1> sdf;
     Bitmap<float, 3> msdf;
     Bitmap<float, 3> msdf;
     Bitmap<float, 4> mtsdf;
     Bitmap<float, 4> mtsdf;
+    artifactPatcherConfig.minImproveRatio = errorCorrectionThreshold; // TEMPORARILY SERVES AS ERROR CORRECTION THRESHOLD
     switch (mode) {
     switch (mode) {
         case SINGLE: {
         case SINGLE: {
             sdf = Bitmap<float, 1>(width, height);
             sdf = Bitmap<float, 1>(width, height);
             if (legacyMode)
             if (legacyMode)
                 generateSDF_legacy(sdf, shape, range, scale, translate);
                 generateSDF_legacy(sdf, shape, range, scale, translate);
             else
             else
-                generateSDF(sdf, shape, range, scale, translate, overlapSupport);
+                generateSDF(sdf, shape, projection, range, generatorConfig);
             break;
             break;
         }
         }
         case PSEUDO: {
         case PSEUDO: {
@@ -896,7 +900,7 @@ int main(int argc, const char * const *argv) {
             if (legacyMode)
             if (legacyMode)
                 generatePseudoSDF_legacy(sdf, shape, range, scale, translate);
                 generatePseudoSDF_legacy(sdf, shape, range, scale, translate);
             else
             else
-                generatePseudoSDF(sdf, shape, range, scale, translate, overlapSupport);
+                generatePseudoSDF(sdf, shape, projection, range, generatorConfig);
             break;
             break;
         }
         }
         case MULTI: {
         case MULTI: {
@@ -908,7 +912,7 @@ int main(int argc, const char * const *argv) {
             if (legacyMode)
             if (legacyMode)
                 generateMSDF_legacy(msdf, shape, range, scale, translate, scanlinePass ? 0 : errorCorrectionThreshold);
                 generateMSDF_legacy(msdf, shape, range, scale, translate, scanlinePass ? 0 : errorCorrectionThreshold);
             else
             else
-                generateMSDF(msdf, shape, range, scale, translate, errorCorrectionThreshold, overlapSupport);
+                generateMSDF(msdf, shape, projection, range, generatorConfig, artifactPatcherConfig);
             break;
             break;
         }
         }
         case MULTI_AND_TRUE: {
         case MULTI_AND_TRUE: {
@@ -920,7 +924,7 @@ int main(int argc, const char * const *argv) {
             if (legacyMode)
             if (legacyMode)
                 generateMTSDF_legacy(mtsdf, shape, range, scale, translate, scanlinePass ? 0 : errorCorrectionThreshold);
                 generateMTSDF_legacy(mtsdf, shape, range, scale, translate, scanlinePass ? 0 : errorCorrectionThreshold);
             else
             else
-                generateMTSDF(mtsdf, shape, range, scale, translate, errorCorrectionThreshold, overlapSupport);
+                generateMTSDF(mtsdf, shape, projection, range, generatorConfig, artifactPatcherConfig);
             break;
             break;
         }
         }
         default:;
         default:;
@@ -951,15 +955,15 @@ int main(int argc, const char * const *argv) {
         switch (mode) {
         switch (mode) {
             case SINGLE:
             case SINGLE:
             case PSEUDO:
             case PSEUDO:
-                distanceSignCorrection(sdf, shape, scale, translate, fillRule);
+                distanceSignCorrection(sdf, shape, projection, fillRule);
                 break;
                 break;
             case MULTI:
             case MULTI:
-                distanceSignCorrection(msdf, shape, scale, translate, fillRule);
+                distanceSignCorrection(msdf, shape, projection, fillRule);
                 if (errorCorrectionThreshold > 0)
                 if (errorCorrectionThreshold > 0)
                     msdfErrorCorrection(msdf, errorCorrectionThreshold/(scale*range));
                     msdfErrorCorrection(msdf, errorCorrectionThreshold/(scale*range));
                 break;
                 break;
             case MULTI_AND_TRUE:
             case MULTI_AND_TRUE:
-                distanceSignCorrection(mtsdf, shape, scale, translate, fillRule);
+                distanceSignCorrection(mtsdf, shape, projection, fillRule);
                 if (errorCorrectionThreshold > 0)
                 if (errorCorrectionThreshold > 0)
                     msdfErrorCorrection(mtsdf, errorCorrectionThreshold/(scale*range));
                     msdfErrorCorrection(mtsdf, errorCorrectionThreshold/(scale*range));
                 break;
                 break;
@@ -1007,7 +1011,7 @@ int main(int argc, const char * const *argv) {
             if (is8bitFormat(format) && (testRenderMulti || testRender || estimateError))
             if (is8bitFormat(format) && (testRenderMulti || testRender || estimateError))
                 simulate8bit(sdf);
                 simulate8bit(sdf);
             if (estimateError) {
             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);
                 printf("SDF error ~ %e\n", sdfError);
             }
             }
             if (testRenderMulti) {
             if (testRenderMulti) {
@@ -1030,7 +1034,7 @@ int main(int argc, const char * const *argv) {
             if (is8bitFormat(format) && (testRenderMulti || testRender || estimateError))
             if (is8bitFormat(format) && (testRenderMulti || testRender || estimateError))
                 simulate8bit(msdf);
                 simulate8bit(msdf);
             if (estimateError) {
             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);
                 printf("SDF error ~ %e\n", sdfError);
             }
             }
             if (testRenderMulti) {
             if (testRenderMulti) {
@@ -1053,7 +1057,7 @@ int main(int argc, const char * const *argv) {
             if (is8bitFormat(format) && (testRenderMulti || testRender || estimateError))
             if (is8bitFormat(format) && (testRenderMulti || testRender || estimateError))
                 simulate8bit(mtsdf);
                 simulate8bit(mtsdf);
             if (estimateError) {
             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);
                 printf("SDF error ~ %e\n", sdfError);
             }
             }
             if (testRenderMulti) {
             if (testRenderMulti) {

+ 12 - 4
msdfgen.h

@@ -17,6 +17,7 @@
 
 
 #include "core/arithmetics.hpp"
 #include "core/arithmetics.hpp"
 #include "core/Vector2.h"
 #include "core/Vector2.h"
+#include "core/Projection.h"
 #include "core/Scanline.h"
 #include "core/Scanline.h"
 #include "core/Shape.h"
 #include "core/Shape.h"
 #include "core/BitmapRef.hpp"
 #include "core/BitmapRef.hpp"
@@ -24,6 +25,7 @@
 #include "core/bitmap-interpolation.hpp"
 #include "core/bitmap-interpolation.hpp"
 #include "core/pixel-conversion.hpp"
 #include "core/pixel-conversion.hpp"
 #include "core/edge-coloring.h"
 #include "core/edge-coloring.h"
+#include "core/generator-config.h"
 #include "core/msdf-error-correction.h"
 #include "core/msdf-error-correction.h"
 #include "core/render-sdf.h"
 #include "core/render-sdf.h"
 #include "core/rasterization.h"
 #include "core/rasterization.h"
@@ -37,16 +39,22 @@
 namespace msdfgen {
 namespace msdfgen {
 
 
 /// Generates a conventional single-channel signed distance field.
 /// 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.
 /// 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)
 /// 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.
 /// 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.
 // 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 generateSDF_legacy(const BitmapRef<float, 1> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate);