Asymmetrical range

This commit is contained in:
Chlumsky 2024-04-17 21:17:57 +02:00
parent 937f31ff41
commit 6d252a7dc3
14 changed files with 525 additions and 228 deletions

View File

@ -128,10 +128,11 @@ int main() {
shape.normalize(); shape.normalize();
// max. angle // max. angle
edgeColoringSimple(shape, 3.0); edgeColoringSimple(shape, 3.0);
// image width, height // output width, height
Bitmap<float, 3> msdf(32, 32); Bitmap<float, 3> msdf(32, 32);
// range, scale, translation // scale, translation
generateMSDF(msdf, shape, 4.0, 1.0, Vector2(4.0, 4.0)); SDFTransformation t(Projection(1.0, Vector2(4.0, 4.0)), Range(4.0));
generateMSDF(msdf, shape, t);
savePng(msdf, "output.png"); savePng(msdf, "output.png");
} }
destroyFont(font); destroyFont(font);

27
core/DistanceMapping.cpp Normal file
View File

@ -0,0 +1,27 @@
#include "DistanceMapping.h"
namespace msdfgen {
DistanceMapping DistanceMapping::inverse(Range range) {
double rangeWidth = range.upper-range.lower;
return DistanceMapping(rangeWidth, range.lower/(rangeWidth ? rangeWidth : 1));
}
DistanceMapping::DistanceMapping() : scale(1), translate(0) { }
DistanceMapping::DistanceMapping(Range range) : scale(1/(range.upper-range.lower)), translate(-range.lower) { }
double DistanceMapping::operator()(double d) const {
return scale*(d+translate);
}
double DistanceMapping::operator()(Delta d) const {
return scale*d.value;
}
DistanceMapping DistanceMapping::inverse() const {
return DistanceMapping(1/scale, -scale*translate);
}
}

36
core/DistanceMapping.h Normal file
View File

@ -0,0 +1,36 @@
#pragma once
#include "Range.hpp"
namespace msdfgen {
/// Linear transformation of signed distance values.
class DistanceMapping {
public:
/// Explicitly designates value as distance delta rather than an absolute distance.
class Delta {
public:
double value;
inline explicit Delta(double distanceDelta) : value(distanceDelta) { }
inline operator double() const { return value; }
};
static DistanceMapping inverse(Range range);
DistanceMapping();
DistanceMapping(Range range);
double operator()(double d) const;
double operator()(Delta d) const;
DistanceMapping inverse() const;
private:
double scale;
double translate;
inline DistanceMapping(double scale, double translate) : scale(scale), translate(translate) { }
};
}

View File

@ -74,7 +74,7 @@ public:
// Compute the evaluated distance (interpolated median) before and after error correction, as well as the exact shape distance. // Compute the evaluated distance (interpolated median) before and after error correction, as well as the exact shape distance.
float oldPSD = median(oldMSD[0], oldMSD[1], oldMSD[2]); float oldPSD = median(oldMSD[0], oldMSD[1], oldMSD[2]);
float newPSD = median(newMSD[0], newMSD[1], newMSD[2]); float newPSD = median(newMSD[0], newMSD[1], newMSD[2]);
float refPSD = float(parent->invRange*parent->distanceFinder.distance(parent->shapeCoord+tVector*parent->texelSize)+.5); float refPSD = float(parent->distanceMapping(parent->distanceFinder.distance(parent->shapeCoord+tVector*parent->texelSize)));
// Compare the differences of the exact distance and the before and after distances. // Compare the differences of the exact distance and the before and after distances.
return parent->minImproveRatio*fabsf(newPSD-refPSD) < double(fabsf(oldPSD-refPSD)); return parent->minImproveRatio*fabsf(newPSD-refPSD) < double(fabsf(oldPSD-refPSD));
} }
@ -87,7 +87,7 @@ public:
Point2 shapeCoord, sdfCoord; Point2 shapeCoord, sdfCoord;
const float *msd; const float *msd;
bool protectedFlag; bool protectedFlag;
inline ShapeDistanceChecker(const BitmapConstRef<float, N> &sdf, const Shape &shape, const Projection &projection, double invRange, double minImproveRatio) : distanceFinder(shape), sdf(sdf), invRange(invRange), minImproveRatio(minImproveRatio) { inline ShapeDistanceChecker(const BitmapConstRef<float, N> &sdf, const Shape &shape, const Projection &projection, DistanceMapping distanceMapping, double minImproveRatio) : distanceFinder(shape), sdf(sdf), distanceMapping(distanceMapping), minImproveRatio(minImproveRatio) {
texelSize = projection.unprojectVector(Vector2(1)); texelSize = projection.unprojectVector(Vector2(1));
} }
inline ArtifactClassifier classifier(const Vector2 &direction, double span) { inline ArtifactClassifier classifier(const Vector2 &direction, double span) {
@ -96,15 +96,14 @@ public:
private: private:
ShapeDistanceFinder<ContourCombiner<PerpendicularDistanceSelector> > distanceFinder; ShapeDistanceFinder<ContourCombiner<PerpendicularDistanceSelector> > distanceFinder;
BitmapConstRef<float, N> sdf; BitmapConstRef<float, N> sdf;
double invRange; DistanceMapping distanceMapping;
Vector2 texelSize; Vector2 texelSize;
double minImproveRatio; double minImproveRatio;
}; };
MSDFErrorCorrection::MSDFErrorCorrection() { } MSDFErrorCorrection::MSDFErrorCorrection() { }
MSDFErrorCorrection::MSDFErrorCorrection(const BitmapRef<byte, 1> &stencil, const Projection &projection, double range) : stencil(stencil), projection(projection) { MSDFErrorCorrection::MSDFErrorCorrection(const BitmapRef<byte, 1> &stencil, const SDFTransformation &transformation) : stencil(stencil), transformation(transformation) {
invRange = 1/range;
minDeviationRatio = ErrorCorrectionConfig::defaultMinDeviationRatio; minDeviationRatio = ErrorCorrectionConfig::defaultMinDeviationRatio;
minImproveRatio = ErrorCorrectionConfig::defaultMinImproveRatio; minImproveRatio = ErrorCorrectionConfig::defaultMinImproveRatio;
memset(stencil.pixels, 0, sizeof(byte)*stencil.width*stencil.height); memset(stencil.pixels, 0, sizeof(byte)*stencil.width*stencil.height);
@ -127,7 +126,7 @@ void MSDFErrorCorrection::protectCorners(const Shape &shape) {
// If the color changes from prevEdge to edge, this is a corner. // If the color changes from prevEdge to edge, this is a corner.
if (!(commonColor&(commonColor-1))) { if (!(commonColor&(commonColor-1))) {
// Find the four texels that envelop the corner and mark them as protected. // Find the four texels that envelop the corner and mark them as protected.
Point2 p = projection.project((*edge)->point(0)); Point2 p = transformation.project((*edge)->point(0));
if (shape.inverseYAxis) if (shape.inverseYAxis)
p.y = stencil.height-p.y; p.y = stencil.height-p.y;
int l = (int) floor(p.x-.5); int l = (int) floor(p.x-.5);
@ -191,7 +190,7 @@ template <int N>
void MSDFErrorCorrection::protectEdges(const BitmapConstRef<float, N> &sdf) { void MSDFErrorCorrection::protectEdges(const BitmapConstRef<float, N> &sdf) {
float radius; float radius;
// Horizontal texel pairs // Horizontal texel pairs
radius = float(PROTECTION_RADIUS_TOLERANCE*projection.unprojectVector(Vector2(invRange, 0)).length()); radius = float(PROTECTION_RADIUS_TOLERANCE*transformation.unprojectVector(Vector2(transformation.distanceMapping(DistanceMapping::Delta(1)), 0)).length());
for (int y = 0; y < sdf.height; ++y) { for (int y = 0; y < sdf.height; ++y) {
const float *left = sdf(0, y); const float *left = sdf(0, y);
const float *right = sdf(1, y); const float *right = sdf(1, y);
@ -207,7 +206,7 @@ void MSDFErrorCorrection::protectEdges(const BitmapConstRef<float, N> &sdf) {
} }
} }
// Vertical texel pairs // Vertical texel pairs
radius = float(PROTECTION_RADIUS_TOLERANCE*projection.unprojectVector(Vector2(0, invRange)).length()); radius = float(PROTECTION_RADIUS_TOLERANCE*transformation.unprojectVector(Vector2(0, transformation.distanceMapping(DistanceMapping::Delta(1)))).length());
for (int y = 0; y < sdf.height-1; ++y) { for (int y = 0; y < sdf.height-1; ++y) {
const float *bottom = sdf(0, y); const float *bottom = sdf(0, y);
const float *top = sdf(0, y+1); const float *top = sdf(0, y+1);
@ -223,7 +222,7 @@ void MSDFErrorCorrection::protectEdges(const BitmapConstRef<float, N> &sdf) {
} }
} }
// Diagonal texel pairs // Diagonal texel pairs
radius = float(PROTECTION_RADIUS_TOLERANCE*projection.unprojectVector(Vector2(invRange)).length()); radius = float(PROTECTION_RADIUS_TOLERANCE*transformation.unprojectVector(Vector2(transformation.distanceMapping(DistanceMapping::Delta(1)))).length());
for (int y = 0; y < sdf.height-1; ++y) { for (int y = 0; y < sdf.height-1; ++y) {
const float *lb = sdf(0, y); const float *lb = sdf(0, y);
const float *rb = sdf(1, y); const float *rb = sdf(1, y);
@ -391,9 +390,9 @@ static bool hasDiagonalArtifact(const ArtifactClassifier &artifactClassifier, fl
template <int N> template <int N>
void MSDFErrorCorrection::findErrors(const BitmapConstRef<float, N> &sdf) { void MSDFErrorCorrection::findErrors(const BitmapConstRef<float, N> &sdf) {
// Compute the expected deltas between values of horizontally, vertically, and diagonally adjacent texels. // Compute the expected deltas between values of horizontally, vertically, and diagonally adjacent texels.
double hSpan = minDeviationRatio*projection.unprojectVector(Vector2(invRange, 0)).length(); double hSpan = minDeviationRatio*transformation.unprojectVector(Vector2(transformation.distanceMapping(DistanceMapping::Delta(1)), 0)).length();
double vSpan = minDeviationRatio*projection.unprojectVector(Vector2(0, invRange)).length(); double vSpan = minDeviationRatio*transformation.unprojectVector(Vector2(0, transformation.distanceMapping(DistanceMapping::Delta(1)))).length();
double dSpan = minDeviationRatio*projection.unprojectVector(Vector2(invRange)).length(); double dSpan = minDeviationRatio*transformation.unprojectVector(Vector2(transformation.distanceMapping(DistanceMapping::Delta(1)))).length();
// Inspect all texels. // Inspect all texels.
for (int y = 0; y < sdf.height; ++y) { for (int y = 0; y < sdf.height; ++y) {
for (int x = 0; x < sdf.width; ++x) { for (int x = 0; x < sdf.width; ++x) {
@ -419,14 +418,14 @@ void MSDFErrorCorrection::findErrors(const BitmapConstRef<float, N> &sdf) {
template <template <typename> class ContourCombiner, int N> template <template <typename> class ContourCombiner, int N>
void MSDFErrorCorrection::findErrors(const BitmapConstRef<float, N> &sdf, const Shape &shape) { void MSDFErrorCorrection::findErrors(const BitmapConstRef<float, N> &sdf, const Shape &shape) {
// Compute the expected deltas between values of horizontally, vertically, and diagonally adjacent texels. // Compute the expected deltas between values of horizontally, vertically, and diagonally adjacent texels.
double hSpan = minDeviationRatio*projection.unprojectVector(Vector2(invRange, 0)).length(); double hSpan = minDeviationRatio*transformation.unprojectVector(Vector2(transformation.distanceMapping(DistanceMapping::Delta(1)), 0)).length();
double vSpan = minDeviationRatio*projection.unprojectVector(Vector2(0, invRange)).length(); double vSpan = minDeviationRatio*transformation.unprojectVector(Vector2(0, transformation.distanceMapping(DistanceMapping::Delta(1)))).length();
double dSpan = minDeviationRatio*projection.unprojectVector(Vector2(invRange)).length(); double dSpan = minDeviationRatio*transformation.unprojectVector(Vector2(transformation.distanceMapping(DistanceMapping::Delta(1)))).length();
#ifdef MSDFGEN_USE_OPENMP #ifdef MSDFGEN_USE_OPENMP
#pragma omp parallel #pragma omp parallel
#endif #endif
{ {
ShapeDistanceChecker<ContourCombiner, N> shapeDistanceChecker(sdf, shape, projection, invRange, minImproveRatio); ShapeDistanceChecker<ContourCombiner, N> shapeDistanceChecker(sdf, shape, transformation, transformation.distanceMapping, minImproveRatio);
bool rightToLeft = false; bool rightToLeft = false;
// Inspect all texels. // Inspect all texels.
#ifdef MSDFGEN_USE_OPENMP #ifdef MSDFGEN_USE_OPENMP
@ -439,7 +438,7 @@ void MSDFErrorCorrection::findErrors(const BitmapConstRef<float, N> &sdf, const
if ((*stencil(x, row)&ERROR)) if ((*stencil(x, row)&ERROR))
continue; continue;
const float *c = sdf(x, row); const float *c = sdf(x, row);
shapeDistanceChecker.shapeCoord = projection.unproject(Point2(x+.5, y+.5)); shapeDistanceChecker.shapeCoord = transformation.unproject(Point2(x+.5, y+.5));
shapeDistanceChecker.sdfCoord = Point2(x+.5, row+.5); shapeDistanceChecker.sdfCoord = Point2(x+.5, row+.5);
shapeDistanceChecker.msd = c; shapeDistanceChecker.msd = c;
shapeDistanceChecker.protectedFlag = (*stencil(x, row)&PROTECTED) != 0; shapeDistanceChecker.protectedFlag = (*stencil(x, row)&PROTECTED) != 0;

View File

@ -1,7 +1,7 @@
#pragma once #pragma once
#include "Projection.h" #include "SDFTransformation.h"
#include "Shape.h" #include "Shape.h"
#include "BitmapRef.hpp" #include "BitmapRef.hpp"
@ -20,7 +20,7 @@ public:
}; };
MSDFErrorCorrection(); MSDFErrorCorrection();
explicit MSDFErrorCorrection(const BitmapRef<byte, 1> &stencil, const Projection &projection, double range); explicit MSDFErrorCorrection(const BitmapRef<byte, 1> &stencil, const SDFTransformation &transformation);
/// Sets the minimum ratio between the actual and maximum expected distance delta to be considered an error. /// Sets the minimum ratio between the actual and maximum expected distance delta to be considered an error.
void setMinDeviationRatio(double minDeviationRatio); void setMinDeviationRatio(double minDeviationRatio);
/// Sets the minimum ratio between the pre-correction distance error and the post-correction distance error. /// Sets the minimum ratio between the pre-correction distance error and the post-correction distance error.
@ -46,8 +46,7 @@ public:
private: private:
BitmapRef<byte, 1> stencil; BitmapRef<byte, 1> stencil;
Projection projection; SDFTransformation transformation;
double invRange;
double minDeviationRatio; double minDeviationRatio;
double minImproveRatio; double minImproveRatio;

46
core/Range.hpp Normal file
View File

@ -0,0 +1,46 @@
#pragma once
#include "base.h"
namespace msdfgen {
/**
* Represents the range between two real values.
* For example, the range of representable signed distances.
*/
struct Range {
double lower, upper;
inline Range(double symmetricalWidth = 0) : lower(-.5*symmetricalWidth), upper(.5*symmetricalWidth) { }
inline Range(double lowerBound, double upperBound) : lower(lowerBound), upper(upperBound) { }
inline Range &operator*=(double factor) {
lower *= factor;
upper *= factor;
return *this;
}
inline Range &operator/=(double divisor) {
lower /= divisor;
upper /= divisor;
return *this;
}
inline Range operator*(double factor) const {
return Range(lower*factor, upper*factor);
}
inline Range operator/(double divisor) const {
return Range(lower/divisor, upper/divisor);
}
};
inline Range operator*(double factor, const Range &range) {
return Range(factor*range.lower, factor*range.upper);
}
}

24
core/SDFTransformation.h Normal file
View File

@ -0,0 +1,24 @@
#pragma once
#include "Projection.h"
#include "DistanceMapping.h"
namespace msdfgen {
/**
* Full signed distance field transformation specifies both spatial transformation (Projection)
* as well as distance value transformation (DistanceMapping).
*/
class SDFTransformation : public Projection {
public:
DistanceMapping distanceMapping;
inline SDFTransformation() { }
inline SDFTransformation(const Projection &projection, const DistanceMapping &distanceMapping) : Projection(projection), distanceMapping(distanceMapping) { }
};
}

View File

@ -10,7 +10,7 @@
namespace msdfgen { namespace msdfgen {
template <int N> template <int N>
static void msdfErrorCorrectionInner(const BitmapRef<float, N> &sdf, const Shape &shape, const Projection &projection, double range, const MSDFGeneratorConfig &config) { static void msdfErrorCorrectionInner(const BitmapRef<float, N> &sdf, const Shape &shape, const SDFTransformation &transformation, const MSDFGeneratorConfig &config) {
if (config.errorCorrection.mode == ErrorCorrectionConfig::DISABLED) if (config.errorCorrection.mode == ErrorCorrectionConfig::DISABLED)
return; return;
Bitmap<byte, 1> stencilBuffer; Bitmap<byte, 1> stencilBuffer;
@ -19,7 +19,7 @@ static void msdfErrorCorrectionInner(const BitmapRef<float, N> &sdf, const Shape
BitmapRef<byte, 1> stencil; BitmapRef<byte, 1> stencil;
stencil.pixels = config.errorCorrection.buffer ? config.errorCorrection.buffer : (byte *) stencilBuffer; stencil.pixels = config.errorCorrection.buffer ? config.errorCorrection.buffer : (byte *) stencilBuffer;
stencil.width = sdf.width, stencil.height = sdf.height; stencil.width = sdf.width, stencil.height = sdf.height;
MSDFErrorCorrection ec(stencil, projection, range); MSDFErrorCorrection ec(stencil, transformation);
ec.setMinDeviationRatio(config.errorCorrection.minDeviationRatio); ec.setMinDeviationRatio(config.errorCorrection.minDeviationRatio);
ec.setMinImproveRatio(config.errorCorrection.minImproveRatio); ec.setMinImproveRatio(config.errorCorrection.minImproveRatio);
switch (config.errorCorrection.mode) { switch (config.errorCorrection.mode) {
@ -49,9 +49,9 @@ static void msdfErrorCorrectionInner(const BitmapRef<float, N> &sdf, const Shape
} }
template <int N> template <int N>
static void msdfErrorCorrectionShapeless(const BitmapRef<float, N> &sdf, const Projection &projection, double range, double minDeviationRatio, bool protectAll) { static void msdfErrorCorrectionShapeless(const BitmapRef<float, N> &sdf, const SDFTransformation &transformation, double minDeviationRatio, bool protectAll) {
Bitmap<byte, 1> stencilBuffer(sdf.width, sdf.height); Bitmap<byte, 1> stencilBuffer(sdf.width, sdf.height);
MSDFErrorCorrection ec(stencilBuffer, projection, range); MSDFErrorCorrection ec(stencilBuffer, transformation);
ec.setMinDeviationRatio(minDeviationRatio); ec.setMinDeviationRatio(minDeviationRatio);
if (protectAll) if (protectAll)
ec.protectAll(); ec.protectAll();
@ -59,25 +59,43 @@ static void msdfErrorCorrectionShapeless(const BitmapRef<float, N> &sdf, const P
ec.apply(sdf); ec.apply(sdf);
} }
void msdfErrorCorrection(const BitmapRef<float, 3> &sdf, const Shape &shape, const Projection &projection, double range, const MSDFGeneratorConfig &config) { void msdfErrorCorrection(const BitmapRef<float, 3> &sdf, const Shape &shape, const SDFTransformation &transformation, const MSDFGeneratorConfig &config) {
msdfErrorCorrectionInner(sdf, shape, projection, range, config); msdfErrorCorrectionInner(sdf, shape, transformation, config);
} }
void msdfErrorCorrection(const BitmapRef<float, 4> &sdf, const Shape &shape, const Projection &projection, double range, const MSDFGeneratorConfig &config) { void msdfErrorCorrection(const BitmapRef<float, 4> &sdf, const Shape &shape, const SDFTransformation &transformation, const MSDFGeneratorConfig &config) {
msdfErrorCorrectionInner(sdf, shape, projection, range, config); msdfErrorCorrectionInner(sdf, shape, transformation, config);
}
void msdfErrorCorrection(const BitmapRef<float, 3> &sdf, const Shape &shape, const Projection &projection, Range range, const MSDFGeneratorConfig &config) {
msdfErrorCorrectionInner(sdf, shape, SDFTransformation(projection, range), config);
}
void msdfErrorCorrection(const BitmapRef<float, 4> &sdf, const Shape &shape, const Projection &projection, Range range, const MSDFGeneratorConfig &config) {
msdfErrorCorrectionInner(sdf, shape, SDFTransformation(projection, range), config);
} }
void msdfFastDistanceErrorCorrection(const BitmapRef<float, 3> &sdf, const Projection &projection, double range, double minDeviationRatio) { void msdfFastDistanceErrorCorrection(const BitmapRef<float, 3> &sdf, const SDFTransformation &transformation, double minDeviationRatio) {
msdfErrorCorrectionShapeless(sdf, projection, range, minDeviationRatio, false); msdfErrorCorrectionShapeless(sdf, transformation, minDeviationRatio, false);
} }
void msdfFastDistanceErrorCorrection(const BitmapRef<float, 4> &sdf, const Projection &projection, double range, double minDeviationRatio) { void msdfFastDistanceErrorCorrection(const BitmapRef<float, 4> &sdf, const SDFTransformation &transformation, double minDeviationRatio) {
msdfErrorCorrectionShapeless(sdf, projection, range, minDeviationRatio, false); msdfErrorCorrectionShapeless(sdf, transformation, minDeviationRatio, false);
}
void msdfFastDistanceErrorCorrection(const BitmapRef<float, 3> &sdf, const Projection &projection, Range range, double minDeviationRatio) {
msdfErrorCorrectionShapeless(sdf, SDFTransformation(projection, range), minDeviationRatio, false);
}
void msdfFastDistanceErrorCorrection(const BitmapRef<float, 4> &sdf, const Projection &projection, Range range, double minDeviationRatio) {
msdfErrorCorrectionShapeless(sdf, SDFTransformation(projection, range), minDeviationRatio, false);
} }
void msdfFastEdgeErrorCorrection(const BitmapRef<float, 3> &sdf, const Projection &projection, double range, double minDeviationRatio) { void msdfFastEdgeErrorCorrection(const BitmapRef<float, 3> &sdf, const SDFTransformation &transformation, double minDeviationRatio) {
msdfErrorCorrectionShapeless(sdf, projection, range, minDeviationRatio, true); msdfErrorCorrectionShapeless(sdf, transformation, minDeviationRatio, true);
} }
void msdfFastEdgeErrorCorrection(const BitmapRef<float, 4> &sdf, const Projection &projection, double range, double minDeviationRatio) { void msdfFastEdgeErrorCorrection(const BitmapRef<float, 4> &sdf, const SDFTransformation &transformation, double minDeviationRatio) {
msdfErrorCorrectionShapeless(sdf, projection, range, minDeviationRatio, true); msdfErrorCorrectionShapeless(sdf, transformation, minDeviationRatio, true);
}
void msdfFastEdgeErrorCorrection(const BitmapRef<float, 3> &sdf, const Projection &projection, Range range, double minDeviationRatio) {
msdfErrorCorrectionShapeless(sdf, SDFTransformation(projection, range), minDeviationRatio, true);
}
void msdfFastEdgeErrorCorrection(const BitmapRef<float, 4> &sdf, const Projection &projection, Range range, double minDeviationRatio) {
msdfErrorCorrectionShapeless(sdf, SDFTransformation(projection, range), minDeviationRatio, true);
} }

View File

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

View File

@ -13,45 +13,45 @@ class DistancePixelConversion;
template <> template <>
class DistancePixelConversion<double> { class DistancePixelConversion<double> {
double invRange; DistanceMapping mapping;
public: public:
typedef BitmapRef<float, 1> BitmapRefType; typedef BitmapRef<float, 1> BitmapRefType;
inline explicit DistancePixelConversion(double range) : invRange(1/range) { } inline explicit DistancePixelConversion(DistanceMapping mapping) : mapping(mapping) { }
inline void operator()(float *pixels, double distance) const { inline void operator()(float *pixels, double distance) const {
*pixels = float(invRange*distance+.5); *pixels = float(mapping(distance));
} }
}; };
template <> template <>
class DistancePixelConversion<MultiDistance> { class DistancePixelConversion<MultiDistance> {
double invRange; DistanceMapping mapping;
public: public:
typedef BitmapRef<float, 3> BitmapRefType; typedef BitmapRef<float, 3> BitmapRefType;
inline explicit DistancePixelConversion(double range) : invRange(1/range) { } inline explicit DistancePixelConversion(DistanceMapping mapping) : mapping(mapping) { }
inline void operator()(float *pixels, const MultiDistance &distance) const { inline void operator()(float *pixels, const MultiDistance &distance) const {
pixels[0] = float(invRange*distance.r+.5); pixels[0] = float(mapping(distance.r));
pixels[1] = float(invRange*distance.g+.5); pixels[1] = float(mapping(distance.g));
pixels[2] = float(invRange*distance.b+.5); pixels[2] = float(mapping(distance.b));
} }
}; };
template <> template <>
class DistancePixelConversion<MultiAndTrueDistance> { class DistancePixelConversion<MultiAndTrueDistance> {
double invRange; DistanceMapping mapping;
public: public:
typedef BitmapRef<float, 4> BitmapRefType; typedef BitmapRef<float, 4> BitmapRefType;
inline explicit DistancePixelConversion(double range) : invRange(1/range) { } inline explicit DistancePixelConversion(DistanceMapping mapping) : mapping(mapping) { }
inline void operator()(float *pixels, const MultiAndTrueDistance &distance) const { inline void operator()(float *pixels, const MultiAndTrueDistance &distance) const {
pixels[0] = float(invRange*distance.r+.5); pixels[0] = float(mapping(distance.r));
pixels[1] = float(invRange*distance.g+.5); pixels[1] = float(mapping(distance.g));
pixels[2] = float(invRange*distance.b+.5); pixels[2] = float(mapping(distance.b));
pixels[3] = float(invRange*distance.a+.5); pixels[3] = float(mapping(distance.a));
} }
}; };
template <class ContourCombiner> template <class ContourCombiner>
void generateDistanceField(const typename DistancePixelConversion<typename ContourCombiner::DistanceType>::BitmapRefType &output, const Shape &shape, const Projection &projection, double range) { void generateDistanceField(const typename DistancePixelConversion<typename ContourCombiner::DistanceType>::BitmapRefType &output, const Shape &shape, const SDFTransformation &transformation) {
DistancePixelConversion<typename ContourCombiner::DistanceType> distancePixelConversion(range); DistancePixelConversion<typename ContourCombiner::DistanceType> distancePixelConversion(transformation.distanceMapping);
#ifdef MSDFGEN_USE_OPENMP #ifdef MSDFGEN_USE_OPENMP
#pragma omp parallel #pragma omp parallel
#endif #endif
@ -65,7 +65,7 @@ void generateDistanceField(const typename DistancePixelConversion<typename Conto
int row = shape.inverseYAxis ? output.height-y-1 : y; int row = shape.inverseYAxis ? output.height-y-1 : 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;
Point2 p = projection.unproject(Point2(x+.5, y+.5)); Point2 p = transformation.unproject(Point2(x+.5, y+.5));
typename ContourCombiner::DistanceType distance = distanceFinder.distance(p); typename ContourCombiner::DistanceType distance = distanceFinder.distance(p);
distancePixelConversion(output(x, row), distance); distancePixelConversion(output(x, row), distance);
} }
@ -74,65 +74,96 @@ void generateDistanceField(const typename DistancePixelConversion<typename Conto
} }
} }
void generateSDF(const BitmapRef<float, 1> &output, const Shape &shape, const Projection &projection, double range, const GeneratorConfig &config) { void generateSDF(const BitmapRef<float, 1> &output, const Shape &shape, const SDFTransformation &transformation, const GeneratorConfig &config) {
if (config.overlapSupport) if (config.overlapSupport)
generateDistanceField<OverlappingContourCombiner<TrueDistanceSelector> >(output, shape, projection, range); generateDistanceField<OverlappingContourCombiner<TrueDistanceSelector> >(output, shape, transformation);
else else
generateDistanceField<SimpleContourCombiner<TrueDistanceSelector> >(output, shape, projection, range); generateDistanceField<SimpleContourCombiner<TrueDistanceSelector> >(output, shape, transformation);
} }
void generatePSDF(const BitmapRef<float, 1> &output, const Shape &shape, const Projection &projection, double range, const GeneratorConfig &config) { void generatePSDF(const BitmapRef<float, 1> &output, const Shape &shape, const SDFTransformation &transformation, const GeneratorConfig &config) {
if (config.overlapSupport) if (config.overlapSupport)
generateDistanceField<OverlappingContourCombiner<PerpendicularDistanceSelector> >(output, shape, projection, range); generateDistanceField<OverlappingContourCombiner<PerpendicularDistanceSelector> >(output, shape, transformation);
else else
generateDistanceField<SimpleContourCombiner<PerpendicularDistanceSelector> >(output, shape, projection, range); generateDistanceField<SimpleContourCombiner<PerpendicularDistanceSelector> >(output, shape, transformation);
} }
void generateMSDF(const BitmapRef<float, 3> &output, const Shape &shape, const Projection &projection, double range, const MSDFGeneratorConfig &config) { void generateMSDF(const BitmapRef<float, 3> &output, const Shape &shape, const SDFTransformation &transformation, const MSDFGeneratorConfig &config) {
if (config.overlapSupport) if (config.overlapSupport)
generateDistanceField<OverlappingContourCombiner<MultiDistanceSelector> >(output, shape, projection, range); generateDistanceField<OverlappingContourCombiner<MultiDistanceSelector> >(output, shape, transformation);
else else
generateDistanceField<SimpleContourCombiner<MultiDistanceSelector> >(output, shape, projection, range); generateDistanceField<SimpleContourCombiner<MultiDistanceSelector> >(output, shape, transformation);
msdfErrorCorrection(output, shape, projection, range, config); msdfErrorCorrection(output, shape, transformation, config);
} }
void generateMTSDF(const BitmapRef<float, 4> &output, const Shape &shape, const Projection &projection, double range, const MSDFGeneratorConfig &config) { void generateMTSDF(const BitmapRef<float, 4> &output, const Shape &shape, const SDFTransformation &transformation, const MSDFGeneratorConfig &config) {
if (config.overlapSupport) if (config.overlapSupport)
generateDistanceField<OverlappingContourCombiner<MultiAndTrueDistanceSelector> >(output, shape, projection, range); generateDistanceField<OverlappingContourCombiner<MultiAndTrueDistanceSelector> >(output, shape, transformation);
else else
generateDistanceField<SimpleContourCombiner<MultiAndTrueDistanceSelector> >(output, shape, projection, range); generateDistanceField<SimpleContourCombiner<MultiAndTrueDistanceSelector> >(output, shape, transformation);
msdfErrorCorrection(output, shape, projection, range, config); msdfErrorCorrection(output, shape, transformation, config);
}
void generateSDF(const BitmapRef<float, 1> &output, const Shape &shape, const Projection &projection, Range range, const GeneratorConfig &config) {
if (config.overlapSupport)
generateDistanceField<OverlappingContourCombiner<TrueDistanceSelector> >(output, shape, SDFTransformation(projection, range));
else
generateDistanceField<SimpleContourCombiner<TrueDistanceSelector> >(output, shape, SDFTransformation(projection, range));
}
void generatePSDF(const BitmapRef<float, 1> &output, const Shape &shape, const Projection &projection, Range range, const GeneratorConfig &config) {
if (config.overlapSupport)
generateDistanceField<OverlappingContourCombiner<PerpendicularDistanceSelector> >(output, shape, SDFTransformation(projection, range));
else
generateDistanceField<SimpleContourCombiner<PerpendicularDistanceSelector> >(output, shape, SDFTransformation(projection, range));
}
void generateMSDF(const BitmapRef<float, 3> &output, const Shape &shape, const Projection &projection, Range range, const MSDFGeneratorConfig &config) {
if (config.overlapSupport)
generateDistanceField<OverlappingContourCombiner<MultiDistanceSelector> >(output, shape, SDFTransformation(projection, range));
else
generateDistanceField<SimpleContourCombiner<MultiDistanceSelector> >(output, shape, SDFTransformation(projection, range));
msdfErrorCorrection(output, shape, SDFTransformation(projection, range), config);
}
void generateMTSDF(const BitmapRef<float, 4> &output, const Shape &shape, const Projection &projection, Range range, const MSDFGeneratorConfig &config) {
if (config.overlapSupport)
generateDistanceField<OverlappingContourCombiner<MultiAndTrueDistanceSelector> >(output, shape, SDFTransformation(projection, range));
else
generateDistanceField<SimpleContourCombiner<MultiAndTrueDistanceSelector> >(output, shape, SDFTransformation(projection, range));
msdfErrorCorrection(output, shape, SDFTransformation(projection, range), config);
} }
// Legacy API // Legacy API
void generatePseudoSDF(const BitmapRef<float, 1> &output, const Shape &shape, const Projection &projection, double range, const GeneratorConfig &config) { void generatePseudoSDF(const BitmapRef<float, 1> &output, const Shape &shape, const Projection &projection, Range range, const GeneratorConfig &config) {
generatePSDF(output, shape, projection, range, config); generatePSDF(output, shape, SDFTransformation(projection, range), config);
} }
void generateSDF(const BitmapRef<float, 1> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, bool overlapSupport) { void generateSDF(const BitmapRef<float, 1> &output, const Shape &shape, Range range, const Vector2 &scale, const Vector2 &translate, bool overlapSupport) {
generateSDF(output, shape, Projection(scale, translate), range, GeneratorConfig(overlapSupport)); generateSDF(output, shape, Projection(scale, translate), range, GeneratorConfig(overlapSupport));
} }
void generatePSDF(const BitmapRef<float, 1> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, bool overlapSupport) { void generatePSDF(const BitmapRef<float, 1> &output, const Shape &shape, Range range, const Vector2 &scale, const Vector2 &translate, bool overlapSupport) {
generatePSDF(output, shape, Projection(scale, translate), range, GeneratorConfig(overlapSupport)); generatePSDF(output, shape, Projection(scale, translate), range, GeneratorConfig(overlapSupport));
} }
void generatePseudoSDF(const BitmapRef<float, 1> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, bool overlapSupport) { void generatePseudoSDF(const BitmapRef<float, 1> &output, const Shape &shape, Range range, const Vector2 &scale, const Vector2 &translate, bool overlapSupport) {
generatePSDF(output, shape, Projection(scale, translate), range, GeneratorConfig(overlapSupport)); generatePSDF(output, shape, Projection(scale, translate), range, GeneratorConfig(overlapSupport));
} }
void generateMSDF(const BitmapRef<float, 3> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, const ErrorCorrectionConfig &errorCorrectionConfig, bool overlapSupport) { void generateMSDF(const BitmapRef<float, 3> &output, const Shape &shape, Range range, const Vector2 &scale, const Vector2 &translate, const ErrorCorrectionConfig &errorCorrectionConfig, bool overlapSupport) {
generateMSDF(output, shape, Projection(scale, translate), range, MSDFGeneratorConfig(overlapSupport, errorCorrectionConfig)); generateMSDF(output, shape, Projection(scale, translate), range, MSDFGeneratorConfig(overlapSupport, errorCorrectionConfig));
} }
void generateMTSDF(const BitmapRef<float, 4> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, const ErrorCorrectionConfig &errorCorrectionConfig, bool overlapSupport) { void generateMTSDF(const BitmapRef<float, 4> &output, const Shape &shape, Range range, const Vector2 &scale, const Vector2 &translate, const ErrorCorrectionConfig &errorCorrectionConfig, bool overlapSupport) {
generateMTSDF(output, shape, Projection(scale, translate), range, MSDFGeneratorConfig(overlapSupport, errorCorrectionConfig)); generateMTSDF(output, shape, Projection(scale, translate), range, MSDFGeneratorConfig(overlapSupport, errorCorrectionConfig));
} }
// Legacy version // Legacy version
void generateSDF_legacy(const BitmapRef<float, 1> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate) { void generateSDF_legacy(const BitmapRef<float, 1> &output, const Shape &shape, Range range, const Vector2 &scale, const Vector2 &translate) {
DistanceMapping distanceMapping(range);
#ifdef MSDFGEN_USE_OPENMP #ifdef MSDFGEN_USE_OPENMP
#pragma omp parallel for #pragma omp parallel for
#endif #endif
@ -148,12 +179,13 @@ void generateSDF_legacy(const BitmapRef<float, 1> &output, const Shape &shape, d
if (distance < minDistance) if (distance < minDistance)
minDistance = distance; minDistance = distance;
} }
*output(x, row) = float(minDistance.distance/range+.5); *output(x, row) = float(distanceMapping(minDistance.distance));
} }
} }
} }
void generatePSDF_legacy(const BitmapRef<float, 1> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate) { void generatePSDF_legacy(const BitmapRef<float, 1> &output, const Shape &shape, Range range, const Vector2 &scale, const Vector2 &translate) {
DistanceMapping distanceMapping(range);
#ifdef MSDFGEN_USE_OPENMP #ifdef MSDFGEN_USE_OPENMP
#pragma omp parallel for #pragma omp parallel for
#endif #endif
@ -176,16 +208,17 @@ void generatePSDF_legacy(const BitmapRef<float, 1> &output, const Shape &shape,
} }
if (nearEdge) if (nearEdge)
(*nearEdge)->distanceToPerpendicularDistance(minDistance, p, nearParam); (*nearEdge)->distanceToPerpendicularDistance(minDistance, p, nearParam);
*output(x, row) = float(minDistance.distance/range+.5); *output(x, row) = float(distanceMapping(minDistance.distance));
} }
} }
} }
void generatePseudoSDF_legacy(const BitmapRef<float, 1> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate) { void generatePseudoSDF_legacy(const BitmapRef<float, 1> &output, const Shape &shape, Range range, const Vector2 &scale, const Vector2 &translate) {
generatePSDF_legacy(output, shape, range, scale, translate); generatePSDF_legacy(output, shape, range, scale, translate);
} }
void generateMSDF_legacy(const BitmapRef<float, 3> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, ErrorCorrectionConfig errorCorrectionConfig) { void generateMSDF_legacy(const BitmapRef<float, 3> &output, const Shape &shape, Range range, const Vector2 &scale, const Vector2 &translate, ErrorCorrectionConfig errorCorrectionConfig) {
DistanceMapping distanceMapping(range);
#ifdef MSDFGEN_USE_OPENMP #ifdef MSDFGEN_USE_OPENMP
#pragma omp parallel for #pragma omp parallel for
#endif #endif
@ -229,9 +262,9 @@ void generateMSDF_legacy(const BitmapRef<float, 3> &output, const Shape &shape,
(*g.nearEdge)->distanceToPerpendicularDistance(g.minDistance, p, g.nearParam); (*g.nearEdge)->distanceToPerpendicularDistance(g.minDistance, p, g.nearParam);
if (b.nearEdge) if (b.nearEdge)
(*b.nearEdge)->distanceToPerpendicularDistance(b.minDistance, p, b.nearParam); (*b.nearEdge)->distanceToPerpendicularDistance(b.minDistance, p, b.nearParam);
output(x, row)[0] = float(r.minDistance.distance/range+.5); output(x, row)[0] = float(distanceMapping(r.minDistance.distance));
output(x, row)[1] = float(g.minDistance.distance/range+.5); output(x, row)[1] = float(distanceMapping(g.minDistance.distance));
output(x, row)[2] = float(b.minDistance.distance/range+.5); output(x, row)[2] = float(distanceMapping(b.minDistance.distance));
} }
} }
@ -239,7 +272,8 @@ void generateMSDF_legacy(const BitmapRef<float, 3> &output, const Shape &shape,
msdfErrorCorrection(output, shape, Projection(scale, translate), range, MSDFGeneratorConfig(false, errorCorrectionConfig)); msdfErrorCorrection(output, shape, Projection(scale, translate), range, MSDFGeneratorConfig(false, errorCorrectionConfig));
} }
void generateMTSDF_legacy(const BitmapRef<float, 4> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, ErrorCorrectionConfig errorCorrectionConfig) { void generateMTSDF_legacy(const BitmapRef<float, 4> &output, const Shape &shape, Range range, const Vector2 &scale, const Vector2 &translate, ErrorCorrectionConfig errorCorrectionConfig) {
DistanceMapping distanceMapping(range);
#ifdef MSDFGEN_USE_OPENMP #ifdef MSDFGEN_USE_OPENMP
#pragma omp parallel for #pragma omp parallel for
#endif #endif
@ -286,10 +320,10 @@ void generateMTSDF_legacy(const BitmapRef<float, 4> &output, const Shape &shape,
(*g.nearEdge)->distanceToPerpendicularDistance(g.minDistance, p, g.nearParam); (*g.nearEdge)->distanceToPerpendicularDistance(g.minDistance, p, g.nearParam);
if (b.nearEdge) if (b.nearEdge)
(*b.nearEdge)->distanceToPerpendicularDistance(b.minDistance, p, b.nearParam); (*b.nearEdge)->distanceToPerpendicularDistance(b.minDistance, p, b.nearParam);
output(x, row)[0] = float(r.minDistance.distance/range+.5); output(x, row)[0] = float(distanceMapping(r.minDistance.distance));
output(x, row)[1] = float(g.minDistance.distance/range+.5); output(x, row)[1] = float(distanceMapping(g.minDistance.distance));
output(x, row)[2] = float(b.minDistance.distance/range+.5); output(x, row)[2] = float(distanceMapping(b.minDistance.distance));
output(x, row)[3] = float(minDistance.distance/range+.5); output(x, row)[3] = float(distanceMapping(minDistance.distance));
} }
} }

View File

@ -2,89 +2,175 @@
#include "render-sdf.h" #include "render-sdf.h"
#include "arithmetics.hpp" #include "arithmetics.hpp"
#include "DistanceMapping.h"
#include "pixel-conversion.hpp" #include "pixel-conversion.hpp"
#include "bitmap-interpolation.hpp" #include "bitmap-interpolation.hpp"
namespace msdfgen { namespace msdfgen {
static float distVal(float dist, double pxRange, float midValue) { static float distVal(float dist, DistanceMapping mapping) {
if (!pxRange) return (float) clamp(mapping(dist)+.5);
return (float) (dist > midValue);
return (float) clamp((dist-midValue)*pxRange+.5);
} }
void renderSDF(const BitmapRef<float, 1> &output, const BitmapConstRef<float, 1> &sdf, double pxRange, float midValue) { void renderSDF(const BitmapRef<float, 1> &output, const BitmapConstRef<float, 1> &sdf, Range sdfPxRange, float sdThreshold) {
Vector2 scale((double) sdf.width/output.width, (double) sdf.height/output.height); Vector2 scale((double) sdf.width/output.width, (double) sdf.height/output.height);
pxRange *= (double) (output.width+output.height)/(sdf.width+sdf.height); if (sdfPxRange.lower == sdfPxRange.upper) {
for (int y = 0; y < output.height; ++y) for (int y = 0; y < output.height; ++y) {
for (int x = 0; x < output.width; ++x) { for (int x = 0; x < output.width; ++x) {
float sd; float sd;
interpolate(&sd, sdf, scale*Point2(x+.5, y+.5)); interpolate(&sd, sdf, scale*Point2(x+.5, y+.5));
*output(x, y) = distVal(sd, pxRange, midValue); *output(x, y) = float(sd >= sdThreshold);
}
} }
} else {
sdfPxRange *= (double) (output.width+output.height)/(sdf.width+sdf.height);
DistanceMapping distanceMapping = DistanceMapping::inverse(sdfPxRange);
float sdBias = .5f-sdThreshold;
for (int y = 0; y < output.height; ++y) {
for (int x = 0; x < output.width; ++x) {
float sd;
interpolate(&sd, sdf, scale*Point2(x+.5, y+.5));
*output(x, y) = distVal(sd+sdBias, distanceMapping);
}
}
}
} }
void renderSDF(const BitmapRef<float, 3> &output, const BitmapConstRef<float, 1> &sdf, double pxRange, float midValue) { void renderSDF(const BitmapRef<float, 3> &output, const BitmapConstRef<float, 1> &sdf, Range sdfPxRange, float sdThreshold) {
Vector2 scale((double) sdf.width/output.width, (double) sdf.height/output.height); Vector2 scale((double) sdf.width/output.width, (double) sdf.height/output.height);
pxRange *= (double) (output.width+output.height)/(sdf.width+sdf.height); if (sdfPxRange.lower == sdfPxRange.upper) {
for (int y = 0; y < output.height; ++y) for (int y = 0; y < output.height; ++y) {
for (int x = 0; x < output.width; ++x) { for (int x = 0; x < output.width; ++x) {
float sd; float sd;
interpolate(&sd, sdf, scale*Point2(x+.5, y+.5)); interpolate(&sd, sdf, scale*Point2(x+.5, y+.5));
float v = distVal(sd, pxRange, midValue); float v = float(sd >= sdThreshold);
output(x, y)[0] = v; output(x, y)[0] = v;
output(x, y)[1] = v; output(x, y)[1] = v;
output(x, y)[2] = v; output(x, y)[2] = v;
}
} }
} else {
sdfPxRange *= (double) (output.width+output.height)/(sdf.width+sdf.height);
DistanceMapping distanceMapping = DistanceMapping::inverse(sdfPxRange);
float sdBias = .5f-sdThreshold;
for (int y = 0; y < output.height; ++y) {
for (int x = 0; x < output.width; ++x) {
float sd;
interpolate(&sd, sdf, scale*Point2(x+.5, y+.5));
float v = distVal(sd+sdBias, distanceMapping);
output(x, y)[0] = v;
output(x, y)[1] = v;
output(x, y)[2] = v;
}
}
}
} }
void renderSDF(const BitmapRef<float, 1> &output, const BitmapConstRef<float, 3> &sdf, double pxRange, float midValue) { void renderSDF(const BitmapRef<float, 1> &output, const BitmapConstRef<float, 3> &sdf, Range sdfPxRange, float sdThreshold) {
Vector2 scale((double) sdf.width/output.width, (double) sdf.height/output.height); Vector2 scale((double) sdf.width/output.width, (double) sdf.height/output.height);
pxRange *= (double) (output.width+output.height)/(sdf.width+sdf.height); if (sdfPxRange.lower == sdfPxRange.upper) {
for (int y = 0; y < output.height; ++y) for (int y = 0; y < output.height; ++y) {
for (int x = 0; x < output.width; ++x) { for (int x = 0; x < output.width; ++x) {
float sd[3]; float sd[3];
interpolate(sd, sdf, scale*Point2(x+.5, y+.5)); interpolate(sd, sdf, scale*Point2(x+.5, y+.5));
*output(x, y) = distVal(median(sd[0], sd[1], sd[2]), pxRange, midValue); *output(x, y) = float(median(sd[0], sd[1], sd[2]) >= sdThreshold);
}
} }
} else {
sdfPxRange *= (double) (output.width+output.height)/(sdf.width+sdf.height);
DistanceMapping distanceMapping = DistanceMapping::inverse(sdfPxRange);
float sdBias = .5f-sdThreshold;
for (int y = 0; y < output.height; ++y) {
for (int x = 0; x < output.width; ++x) {
float sd[3];
interpolate(sd, sdf, scale*Point2(x+.5, y+.5));
*output(x, y) = distVal(median(sd[0], sd[1], sd[2])+sdBias, distanceMapping);
}
}
}
} }
void renderSDF(const BitmapRef<float, 3> &output, const BitmapConstRef<float, 3> &sdf, double pxRange, float midValue) { void renderSDF(const BitmapRef<float, 3> &output, const BitmapConstRef<float, 3> &sdf, Range sdfPxRange, float sdThreshold) {
Vector2 scale((double) sdf.width/output.width, (double) sdf.height/output.height); Vector2 scale((double) sdf.width/output.width, (double) sdf.height/output.height);
pxRange *= (double) (output.width+output.height)/(sdf.width+sdf.height); if (sdfPxRange.lower == sdfPxRange.upper) {
for (int y = 0; y < output.height; ++y) for (int y = 0; y < output.height; ++y) {
for (int x = 0; x < output.width; ++x) { for (int x = 0; x < output.width; ++x) {
float sd[3]; float sd[3];
interpolate(sd, sdf, scale*Point2(x+.5, y+.5)); interpolate(sd, sdf, scale*Point2(x+.5, y+.5));
output(x, y)[0] = distVal(sd[0], pxRange, midValue); output(x, y)[0] = float(sd[0] >= sdThreshold);
output(x, y)[1] = distVal(sd[1], pxRange, midValue); output(x, y)[1] = float(sd[1] >= sdThreshold);
output(x, y)[2] = distVal(sd[2], pxRange, midValue); output(x, y)[2] = float(sd[2] >= sdThreshold);
}
} }
} else {
sdfPxRange *= (double) (output.width+output.height)/(sdf.width+sdf.height);
DistanceMapping distanceMapping = DistanceMapping::inverse(sdfPxRange);
float sdBias = .5f-sdThreshold;
for (int y = 0; y < output.height; ++y) {
for (int x = 0; x < output.width; ++x) {
float sd[3];
interpolate(sd, sdf, scale*Point2(x+.5, y+.5));
output(x, y)[0] = distVal(sd[0]+sdBias, distanceMapping);
output(x, y)[1] = distVal(sd[1]+sdBias, distanceMapping);
output(x, y)[2] = distVal(sd[2]+sdBias, distanceMapping);
}
}
}
} }
void renderSDF(const BitmapRef<float, 1> &output, const BitmapConstRef<float, 4> &sdf, double pxRange, float midValue) { void renderSDF(const BitmapRef<float, 1> &output, const BitmapConstRef<float, 4> &sdf, Range sdfPxRange, float sdThreshold) {
Vector2 scale((double) sdf.width/output.width, (double) sdf.height/output.height); Vector2 scale((double) sdf.width/output.width, (double) sdf.height/output.height);
pxRange *= (double) (output.width+output.height)/(sdf.width+sdf.height); if (sdfPxRange.lower == sdfPxRange.upper) {
for (int y = 0; y < output.height; ++y) for (int y = 0; y < output.height; ++y) {
for (int x = 0; x < output.width; ++x) { for (int x = 0; x < output.width; ++x) {
float sd[4]; float sd[4];
interpolate(sd, sdf, scale*Point2(x+.5, y+.5)); interpolate(sd, sdf, scale*Point2(x+.5, y+.5));
*output(x, y) = distVal(median(sd[0], sd[1], sd[2]), pxRange, midValue); *output(x, y) = float(median(sd[0], sd[1], sd[2]) >= sdThreshold);
}
} }
} else {
sdfPxRange *= (double) (output.width+output.height)/(sdf.width+sdf.height);
DistanceMapping distanceMapping = DistanceMapping::inverse(sdfPxRange);
float sdBias = .5f-sdThreshold;
for (int y = 0; y < output.height; ++y) {
for (int x = 0; x < output.width; ++x) {
float sd[4];
interpolate(sd, sdf, scale*Point2(x+.5, y+.5));
*output(x, y) = distVal(median(sd[0], sd[1], sd[2])+sdBias, distanceMapping);
}
}
}
} }
void renderSDF(const BitmapRef<float, 4> &output, const BitmapConstRef<float, 4> &sdf, double pxRange, float midValue) { void renderSDF(const BitmapRef<float, 4> &output, const BitmapConstRef<float, 4> &sdf, Range sdfPxRange, float sdThreshold) {
Vector2 scale((double) sdf.width/output.width, (double) sdf.height/output.height); Vector2 scale((double) sdf.width/output.width, (double) sdf.height/output.height);
pxRange *= (double) (output.width+output.height)/(sdf.width+sdf.height); if (sdfPxRange.lower == sdfPxRange.upper) {
for (int y = 0; y < output.height; ++y) for (int y = 0; y < output.height; ++y) {
for (int x = 0; x < output.width; ++x) { for (int x = 0; x < output.width; ++x) {
float sd[4]; float sd[4];
interpolate(sd, sdf, scale*Point2(x+.5, y+.5)); interpolate(sd, sdf, scale*Point2(x+.5, y+.5));
output(x, y)[0] = distVal(sd[0], pxRange, midValue); output(x, y)[0] = float(sd[0] >= sdThreshold);
output(x, y)[1] = distVal(sd[1], pxRange, midValue); output(x, y)[1] = float(sd[1] >= sdThreshold);
output(x, y)[2] = distVal(sd[2], pxRange, midValue); output(x, y)[2] = float(sd[2] >= sdThreshold);
output(x, y)[3] = distVal(sd[3], pxRange, midValue); output(x, y)[3] = float(sd[3] >= sdThreshold);
}
} }
} else {
sdfPxRange *= (double) (output.width+output.height)/(sdf.width+sdf.height);
DistanceMapping distanceMapping = DistanceMapping::inverse(sdfPxRange);
float sdBias = .5f-sdThreshold;
for (int y = 0; y < output.height; ++y) {
for (int x = 0; x < output.width; ++x) {
float sd[4];
interpolate(sd, sdf, scale*Point2(x+.5, y+.5));
output(x, y)[0] = distVal(sd[0]+sdBias, distanceMapping);
output(x, y)[1] = distVal(sd[1]+sdBias, distanceMapping);
output(x, y)[2] = distVal(sd[2]+sdBias, distanceMapping);
output(x, y)[3] = distVal(sd[3]+sdBias, distanceMapping);
}
}
}
} }
void simulate8bit(const BitmapRef<float, 1> &bitmap) { void simulate8bit(const BitmapRef<float, 1> &bitmap) {

View File

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

121
main.cpp
View File

@ -388,6 +388,10 @@ static const char *const helpText =
"OPTIONS\n" "OPTIONS\n"
" -angle <angle>\n" " -angle <angle>\n"
"\tSpecifies the minimum angle between adjacent edges to be considered a corner. Append D for degrees.\n" "\tSpecifies the minimum angle between adjacent edges to be considered a corner. Append D for degrees.\n"
" -apxrange <outermost distance> <innermost distance>\n"
"\tSpecifies the outermost (negative) and innermost representable distance in pixels.\n"
" -arange <outermost distance> <innermost distance>\n"
"\tSpecifies the outermost (negative) and innermost representable distance in shape units.\n"
" -ascale <x scale> <y scale>\n" " -ascale <x scale> <y scale>\n"
"\tSets the scale used to convert shape units to pixels asymmetrically.\n" "\tSets the scale used to convert shape units to pixels asymmetrically.\n"
" -autoframe\n" " -autoframe\n"
@ -396,8 +400,6 @@ static const char *const helpText =
"\tSelects the strategy of the edge coloring heuristic.\n" "\tSelects the strategy of the edge coloring heuristic.\n"
" -dimensions <width> <height>\n" " -dimensions <width> <height>\n"
"\tSets the dimensions of the output image.\n" "\tSets the dimensions of the output image.\n"
" -distanceshift <shift>\n"
"\tShifts all normalized distances in the output distance field by this value.\n"
" -edgecolors <sequence>\n" " -edgecolors <sequence>\n"
"\tOverrides automatic edge coloring with the specified color sequence.\n" "\tOverrides automatic edge coloring with the specified color sequence.\n"
" -errorcorrection <mode>\n" " -errorcorrection <mode>\n"
@ -555,8 +557,8 @@ int main(int argc, const char *const *argv) {
RANGE_UNIT, RANGE_UNIT,
RANGE_PX RANGE_PX
} rangeMode = RANGE_PX; } rangeMode = RANGE_PX;
double range = 1; Range range(1);
double pxRange = 2; Range pxRange(2);
Vector2 translate; Vector2 translate;
Vector2 scale = 1; Vector2 scale = 1;
bool scaleSpecified = false; bool scaleSpecified = false;
@ -740,18 +742,42 @@ int main(int argc, const char *const *argv) {
} }
ARG_CASE("-range" ARG_CASE_OR "-unitrange", 1) { ARG_CASE("-range" ARG_CASE_OR "-unitrange", 1) {
double r; double r;
if (!(parseDouble(r, argv[argPos++]) && r > 0)) if (!parseDouble(r, argv[argPos++]))
ABORT("Invalid range argument. Use -range <range> with a positive real number."); ABORT("Invalid range argument. Use -range <range> with a real number.");
if (r == 0)
ABORT("Range must be non-zero.");
rangeMode = RANGE_UNIT; rangeMode = RANGE_UNIT;
range = r; range = Range(r);
continue; continue;
} }
ARG_CASE("-pxrange", 1) { ARG_CASE("-pxrange", 1) {
double r; double r;
if (!(parseDouble(r, argv[argPos++]) && r > 0)) if (!parseDouble(r, argv[argPos++]))
ABORT("Invalid range argument. Use -pxrange <range> with a positive real number."); ABORT("Invalid range argument. Use -pxrange <range> with a real number.");
if (r == 0)
ABORT("Range must be non-zero.");
rangeMode = RANGE_PX; rangeMode = RANGE_PX;
pxRange = r; pxRange = Range(r);
continue;
}
ARG_CASE("-arange" ARG_CASE_OR "-aunitrange", 2) {
double r0, r1;
if (!(parseDouble(r0, argv[argPos++]) && parseDouble(r1, argv[argPos++])))
ABORT("Invalid range arguments. Use -arange <minimum> <maximum> with two real numbers.");
if (r0 == r1)
ABORT("Range must be non-empty.");
rangeMode = RANGE_UNIT;
range = Range(r0, r1);
continue;
}
ARG_CASE("-apxrange", 2) {
double r0, r1;
if (!(parseDouble(r0, argv[argPos++]) && parseDouble(r1, argv[argPos++])))
ABORT("Invalid range arguments. Use -apxrange <minimum> <maximum> with two real numbers.");
if (r0 == r1)
ABORT("Range must be non-empty.");
rangeMode = RANGE_PX;
pxRange = Range(r0, r1);
continue; continue;
} }
ARG_CASE("-scale", 1) { ARG_CASE("-scale", 1) {
@ -1039,16 +1065,22 @@ int main(int argc, const char *const *argv) {
if (autoFrame || mode == METRICS || printMetrics || orientation == GUESS) if (autoFrame || mode == METRICS || printMetrics || orientation == GUESS)
bounds = shape.getBounds(); bounds = shape.getBounds();
if (outputDistanceShift) {
Range &rangeRef = rangeMode == RANGE_PX ? pxRange : range;
double rangeShift = -outputDistanceShift*(rangeRef.upper-rangeRef.lower);
rangeRef.lower += rangeShift;
rangeRef.upper += rangeShift;
}
// Auto-frame // Auto-frame
if (autoFrame) { if (autoFrame) {
double l = bounds.l, b = bounds.b, r = bounds.r, t = bounds.t; double l = bounds.l, b = bounds.b, r = bounds.r, t = bounds.t;
Vector2 frame(width, height); Vector2 frame(width, height);
double m = .5+(double) outputDistanceShift;
if (!scaleSpecified) { if (!scaleSpecified) {
if (rangeMode == RANGE_UNIT) if (rangeMode == RANGE_UNIT)
l -= m*range, b -= m*range, r += m*range, t += m*range; l += range.lower, b += range.lower, r -= range.lower, t -= range.lower;
else else
frame -= 2*m*pxRange; frame += 2*pxRange.lower;
} }
if (l >= r || b >= t) if (l >= r || b >= t)
l = 0, b = 0, r = 1, t = 1; l = 0, b = 0, r = 1, t = 1;
@ -1067,7 +1099,7 @@ int main(int argc, const char *const *argv) {
} }
} }
if (rangeMode == RANGE_PX && !scaleSpecified) if (rangeMode == RANGE_PX && !scaleSpecified)
translate += m*pxRange/scale; translate -= pxRange.lower/scale;
} }
if (rangeMode == RANGE_PX) if (rangeMode == RANGE_PX)
@ -1094,13 +1126,13 @@ int main(int argc, const char *const *argv) {
fprintf(out, "translate = %.17g, %.17g\n", translate.x, translate.y); fprintf(out, "translate = %.17g, %.17g\n", translate.x, translate.y);
} }
if (rangeMode == RANGE_PX) if (rangeMode == RANGE_PX)
fprintf(out, "range = %.17g\n", range); fprintf(out, "range %.17g to %.17g\n", range.lower, range.upper);
if (mode == METRICS && outputSpecified) if (mode == METRICS && outputSpecified)
fclose(out); fclose(out);
} }
// Compute output // Compute output
Projection projection(scale, translate); SDFTransformation transformation(Projection(scale, translate), range);
Bitmap<float, 1> sdf; Bitmap<float, 1> sdf;
Bitmap<float, 3> msdf; Bitmap<float, 3> msdf;
Bitmap<float, 4> mtsdf; Bitmap<float, 4> mtsdf;
@ -1125,7 +1157,7 @@ int main(int argc, const char *const *argv) {
if (legacyMode) if (legacyMode)
generateSDF_legacy(sdf, shape, range, scale, translate); generateSDF_legacy(sdf, shape, range, scale, translate);
else else
generateSDF(sdf, shape, projection, range, generatorConfig); generateSDF(sdf, shape, transformation, generatorConfig);
break; break;
} }
case PERPENDICULAR: { case PERPENDICULAR: {
@ -1133,7 +1165,7 @@ int main(int argc, const char *const *argv) {
if (legacyMode) if (legacyMode)
generatePSDF_legacy(sdf, shape, range, scale, translate); generatePSDF_legacy(sdf, shape, range, scale, translate);
else else
generatePSDF(sdf, shape, projection, range, generatorConfig); generatePSDF(sdf, shape, transformation, generatorConfig);
break; break;
} }
case MULTI: { case MULTI: {
@ -1145,7 +1177,7 @@ int main(int argc, const char *const *argv) {
if (legacyMode) if (legacyMode)
generateMSDF_legacy(msdf, shape, range, scale, translate, generatorConfig.errorCorrection); generateMSDF_legacy(msdf, shape, range, scale, translate, generatorConfig.errorCorrection);
else else
generateMSDF(msdf, shape, projection, range, generatorConfig); generateMSDF(msdf, shape, transformation, generatorConfig);
break; break;
} }
case MULTI_AND_TRUE: { case MULTI_AND_TRUE: {
@ -1157,7 +1189,7 @@ int main(int argc, const char *const *argv) {
if (legacyMode) if (legacyMode)
generateMTSDF_legacy(mtsdf, shape, range, scale, translate, generatorConfig.errorCorrection); generateMTSDF_legacy(mtsdf, shape, range, scale, translate, generatorConfig.errorCorrection);
else else
generateMTSDF(mtsdf, shape, projection, range, generatorConfig); generateMTSDF(mtsdf, shape, transformation, generatorConfig);
break; break;
} }
default:; default:;
@ -1188,40 +1220,19 @@ int main(int argc, const char *const *argv) {
switch (mode) { switch (mode) {
case SINGLE: case SINGLE:
case PERPENDICULAR: case PERPENDICULAR:
distanceSignCorrection(sdf, shape, projection, fillRule); distanceSignCorrection(sdf, shape, transformation, fillRule);
break; break;
case MULTI: case MULTI:
distanceSignCorrection(msdf, shape, projection, fillRule); distanceSignCorrection(msdf, shape, transformation, fillRule);
msdfErrorCorrection(msdf, shape, projection, range, postErrorCorrectionConfig); msdfErrorCorrection(msdf, shape, transformation, postErrorCorrectionConfig);
break; break;
case MULTI_AND_TRUE: case MULTI_AND_TRUE:
distanceSignCorrection(mtsdf, shape, projection, fillRule); distanceSignCorrection(mtsdf, shape, transformation, fillRule);
msdfErrorCorrection(msdf, shape, projection, range, postErrorCorrectionConfig); msdfErrorCorrection(msdf, shape, transformation, postErrorCorrectionConfig);
break; break;
default:; default:;
} }
} }
if (outputDistanceShift) {
float *pixel = NULL, *pixelsEnd = NULL;
switch (mode) {
case SINGLE:
case PERPENDICULAR:
pixel = (float *) sdf;
pixelsEnd = pixel+1*sdf.width()*sdf.height();
break;
case MULTI:
pixel = (float *) msdf;
pixelsEnd = pixel+3*msdf.width()*msdf.height();
break;
case MULTI_AND_TRUE:
pixel = (float *) mtsdf;
pixelsEnd = pixel+4*mtsdf.width()*mtsdf.height();
break;
default:;
}
while (pixel < pixelsEnd)
*pixel++ += outputDistanceShift;
}
// Save output // Save output
if (shapeExport) { if (shapeExport) {
@ -1243,18 +1254,18 @@ 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, projection, SDF_ERROR_ESTIMATE_PRECISION, fillRule); double sdfError = estimateSDFError(sdf, shape, transformation, SDF_ERROR_ESTIMATE_PRECISION, fillRule);
printf("SDF error ~ %e\n", sdfError); printf("SDF error ~ %e\n", sdfError);
} }
if (testRenderMulti) { if (testRenderMulti) {
Bitmap<float, 3> render(testWidthM, testHeightM); Bitmap<float, 3> render(testWidthM, testHeightM);
renderSDF(render, sdf, avgScale*range, .5f+outputDistanceShift); renderSDF(render, sdf, avgScale*range);
if (!SAVE_DEFAULT_IMAGE_FORMAT(render, testRenderMulti)) if (!SAVE_DEFAULT_IMAGE_FORMAT(render, testRenderMulti))
fputs("Failed to write test render file.\n", stderr); fputs("Failed to write test render file.\n", stderr);
} }
if (testRender) { if (testRender) {
Bitmap<float, 1> render(testWidth, testHeight); Bitmap<float, 1> render(testWidth, testHeight);
renderSDF(render, sdf, avgScale*range, .5f+outputDistanceShift); renderSDF(render, sdf, avgScale*range);
if (!SAVE_DEFAULT_IMAGE_FORMAT(render, testRender)) if (!SAVE_DEFAULT_IMAGE_FORMAT(render, testRender))
fputs("Failed to write test render file.\n", stderr); fputs("Failed to write test render file.\n", stderr);
} }
@ -1267,18 +1278,18 @@ 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, projection, SDF_ERROR_ESTIMATE_PRECISION, fillRule); double sdfError = estimateSDFError(msdf, shape, transformation, SDF_ERROR_ESTIMATE_PRECISION, fillRule);
printf("SDF error ~ %e\n", sdfError); printf("SDF error ~ %e\n", sdfError);
} }
if (testRenderMulti) { if (testRenderMulti) {
Bitmap<float, 3> render(testWidthM, testHeightM); Bitmap<float, 3> render(testWidthM, testHeightM);
renderSDF(render, msdf, avgScale*range, .5f+outputDistanceShift); renderSDF(render, msdf, avgScale*range);
if (!SAVE_DEFAULT_IMAGE_FORMAT(render, testRenderMulti)) if (!SAVE_DEFAULT_IMAGE_FORMAT(render, testRenderMulti))
fputs("Failed to write test render file.\n", stderr); fputs("Failed to write test render file.\n", stderr);
} }
if (testRender) { if (testRender) {
Bitmap<float, 1> render(testWidth, testHeight); Bitmap<float, 1> render(testWidth, testHeight);
renderSDF(render, msdf, avgScale*range, .5f+outputDistanceShift); renderSDF(render, msdf, avgScale*range);
if (!SAVE_DEFAULT_IMAGE_FORMAT(render, testRender)) if (!SAVE_DEFAULT_IMAGE_FORMAT(render, testRender))
fputs("Failed to write test render file.\n", stderr); fputs("Failed to write test render file.\n", stderr);
} }
@ -1291,18 +1302,18 @@ 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, projection, SDF_ERROR_ESTIMATE_PRECISION, fillRule); double sdfError = estimateSDFError(mtsdf, shape, transformation, SDF_ERROR_ESTIMATE_PRECISION, fillRule);
printf("SDF error ~ %e\n", sdfError); printf("SDF error ~ %e\n", sdfError);
} }
if (testRenderMulti) { if (testRenderMulti) {
Bitmap<float, 4> render(testWidthM, testHeightM); Bitmap<float, 4> render(testWidthM, testHeightM);
renderSDF(render, mtsdf, avgScale*range, .5f+outputDistanceShift); renderSDF(render, mtsdf, avgScale*range);
if (!SAVE_DEFAULT_IMAGE_FORMAT(render, testRenderMulti)) if (!SAVE_DEFAULT_IMAGE_FORMAT(render, testRenderMulti))
fputs("Failed to write test render file.\n", stderr); fputs("Failed to write test render file.\n", stderr);
} }
if (testRender) { if (testRender) {
Bitmap<float, 1> render(testWidth, testHeight); Bitmap<float, 1> render(testWidth, testHeight);
renderSDF(render, mtsdf, avgScale*range, .5f+outputDistanceShift); renderSDF(render, mtsdf, avgScale*range);
if (!SAVE_DEFAULT_IMAGE_FORMAT(render, testRender)) if (!SAVE_DEFAULT_IMAGE_FORMAT(render, testRender))
fputs("Failed to write test render file.\n", stderr); fputs("Failed to write test render file.\n", stderr);
} }

View File

@ -18,7 +18,10 @@
#include "core/base.h" #include "core/base.h"
#include "core/arithmetics.hpp" #include "core/arithmetics.hpp"
#include "core/Vector2.hpp" #include "core/Vector2.hpp"
#include "core/Range.hpp"
#include "core/Projection.h" #include "core/Projection.h"
#include "core/DistanceMapping.h"
#include "core/SDFTransformation.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"
@ -38,31 +41,35 @@
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, const Projection &projection, double range, const GeneratorConfig &config = GeneratorConfig()); void generateSDF(const BitmapRef<float, 1> &output, const Shape &shape, const SDFTransformation &transformation, const GeneratorConfig &config = GeneratorConfig());
/// Generates a single-channel signed perpendicular distance field. /// Generates a single-channel signed perpendicular distance field.
void generatePSDF(const BitmapRef<float, 1> &output, const Shape &shape, const Projection &projection, double range, const GeneratorConfig &config = GeneratorConfig()); void generatePSDF(const BitmapRef<float, 1> &output, const Shape &shape, const SDFTransformation &transformation, 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, const Projection &projection, double range, const MSDFGeneratorConfig &config = MSDFGeneratorConfig()); void generateMSDF(const BitmapRef<float, 3> &output, const Shape &shape, const SDFTransformation &transformation, const MSDFGeneratorConfig &config = MSDFGeneratorConfig());
/// 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, const Projection &projection, double range, const MSDFGeneratorConfig &config = MSDFGeneratorConfig()); void generateMTSDF(const BitmapRef<float, 4> &output, const Shape &shape, const SDFTransformation &transformation, const MSDFGeneratorConfig &config = MSDFGeneratorConfig());
// Old version of the function API's kept for backwards compatibility // Old version of the function API's kept for backwards compatibility
void generatePseudoSDF(const BitmapRef<float, 1> &output, const Shape &shape, const Projection &projection, double range, const GeneratorConfig &config = GeneratorConfig()); void generateSDF(const BitmapRef<float, 1> &output, const Shape &shape, const Projection &projection, Range range, const GeneratorConfig &config = GeneratorConfig());
void generatePSDF(const BitmapRef<float, 1> &output, const Shape &shape, const Projection &projection, Range range, const GeneratorConfig &config = GeneratorConfig());
void generatePseudoSDF(const BitmapRef<float, 1> &output, const Shape &shape, const Projection &projection, Range range, const GeneratorConfig &config = GeneratorConfig());
void generateMSDF(const BitmapRef<float, 3> &output, const Shape &shape, const Projection &projection, Range range, const MSDFGeneratorConfig &config = MSDFGeneratorConfig());
void generateMTSDF(const BitmapRef<float, 4> &output, const Shape &shape, const Projection &projection, Range range, const MSDFGeneratorConfig &config = MSDFGeneratorConfig());
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, Range range, const Vector2 &scale, const Vector2 &translate, bool overlapSupport = true);
void generatePSDF(const BitmapRef<float, 1> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, bool overlapSupport = true); void generatePSDF(const BitmapRef<float, 1> &output, const Shape &shape, Range 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 generatePseudoSDF(const BitmapRef<float, 1> &output, const Shape &shape, Range range, const Vector2 &scale, const Vector2 &translate, bool overlapSupport = true);
void generateMSDF(const BitmapRef<float, 3> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, const ErrorCorrectionConfig &errorCorrectionConfig = ErrorCorrectionConfig(), bool overlapSupport = true); void generateMSDF(const BitmapRef<float, 3> &output, const Shape &shape, Range range, const Vector2 &scale, const Vector2 &translate, const ErrorCorrectionConfig &errorCorrectionConfig = ErrorCorrectionConfig(), bool overlapSupport = true);
void generateMTSDF(const BitmapRef<float, 4> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, const ErrorCorrectionConfig &errorCorrectionConfig = ErrorCorrectionConfig(), bool overlapSupport = true); void generateMTSDF(const BitmapRef<float, 4> &output, const Shape &shape, Range range, const Vector2 &scale, const Vector2 &translate, const ErrorCorrectionConfig &errorCorrectionConfig = ErrorCorrectionConfig(), bool overlapSupport = true);
// Original simpler versions of the previous functions, which work well under normal circumstances, but cannot deal with overlapping contours. // 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, Range range, const Vector2 &scale, const Vector2 &translate);
void generatePSDF_legacy(const BitmapRef<float, 1> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate); void generatePSDF_legacy(const BitmapRef<float, 1> &output, const Shape &shape, Range range, const Vector2 &scale, const Vector2 &translate);
void generatePseudoSDF_legacy(const BitmapRef<float, 1> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate); void generatePseudoSDF_legacy(const BitmapRef<float, 1> &output, const Shape &shape, Range range, const Vector2 &scale, const Vector2 &translate);
void generateMSDF_legacy(const BitmapRef<float, 3> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, ErrorCorrectionConfig errorCorrectionConfig = ErrorCorrectionConfig()); void generateMSDF_legacy(const BitmapRef<float, 3> &output, const Shape &shape, Range range, const Vector2 &scale, const Vector2 &translate, ErrorCorrectionConfig errorCorrectionConfig = ErrorCorrectionConfig());
void generateMTSDF_legacy(const BitmapRef<float, 4> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, ErrorCorrectionConfig errorCorrectionConfig = ErrorCorrectionConfig()); void generateMTSDF_legacy(const BitmapRef<float, 4> &output, const Shape &shape, Range range, const Vector2 &scale, const Vector2 &translate, ErrorCorrectionConfig errorCorrectionConfig = ErrorCorrectionConfig());
} }