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();
// max. angle
edgeColoringSimple(shape, 3.0);
// image width, height
// output width, height
Bitmap<float, 3> msdf(32, 32);
// range, scale, translation
generateMSDF(msdf, shape, 4.0, 1.0, Vector2(4.0, 4.0));
// scale, translation
SDFTransformation t(Projection(1.0, Vector2(4.0, 4.0)), Range(4.0));
generateMSDF(msdf, shape, t);
savePng(msdf, "output.png");
}
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.
float oldPSD = median(oldMSD[0], oldMSD[1], oldMSD[2]);
float newPSD = median(newMSD[0], newMSD[1], newMSD[2]);
float refPSD = float(parent->invRange*parent->distanceFinder.distance(parent->shapeCoord+tVector*parent->texelSize)+.5);
float refPSD = float(parent->distanceMapping(parent->distanceFinder.distance(parent->shapeCoord+tVector*parent->texelSize)));
// Compare the differences of the exact distance and the before and after distances.
return parent->minImproveRatio*fabsf(newPSD-refPSD) < double(fabsf(oldPSD-refPSD));
}
@ -87,7 +87,7 @@ public:
Point2 shapeCoord, sdfCoord;
const float *msd;
bool protectedFlag;
inline ShapeDistanceChecker(const BitmapConstRef<float, N> &sdf, const Shape &shape, const Projection &projection, double invRange, double minImproveRatio) : distanceFinder(shape), sdf(sdf), invRange(invRange), minImproveRatio(minImproveRatio) {
inline ShapeDistanceChecker(const BitmapConstRef<float, N> &sdf, const Shape &shape, const Projection &projection, DistanceMapping distanceMapping, double minImproveRatio) : distanceFinder(shape), sdf(sdf), distanceMapping(distanceMapping), minImproveRatio(minImproveRatio) {
texelSize = projection.unprojectVector(Vector2(1));
}
inline ArtifactClassifier classifier(const Vector2 &direction, double span) {
@ -96,15 +96,14 @@ public:
private:
ShapeDistanceFinder<ContourCombiner<PerpendicularDistanceSelector> > distanceFinder;
BitmapConstRef<float, N> sdf;
double invRange;
DistanceMapping distanceMapping;
Vector2 texelSize;
double minImproveRatio;
};
MSDFErrorCorrection::MSDFErrorCorrection() { }
MSDFErrorCorrection::MSDFErrorCorrection(const BitmapRef<byte, 1> &stencil, const Projection &projection, double range) : stencil(stencil), projection(projection) {
invRange = 1/range;
MSDFErrorCorrection::MSDFErrorCorrection(const BitmapRef<byte, 1> &stencil, const SDFTransformation &transformation) : stencil(stencil), transformation(transformation) {
minDeviationRatio = ErrorCorrectionConfig::defaultMinDeviationRatio;
minImproveRatio = ErrorCorrectionConfig::defaultMinImproveRatio;
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 (!(commonColor&(commonColor-1))) {
// 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)
p.y = stencil.height-p.y;
int l = (int) floor(p.x-.5);
@ -191,7 +190,7 @@ template <int N>
void MSDFErrorCorrection::protectEdges(const BitmapConstRef<float, N> &sdf) {
float radius;
// 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) {
const float *left = sdf(0, y);
const float *right = sdf(1, y);
@ -207,7 +206,7 @@ void MSDFErrorCorrection::protectEdges(const BitmapConstRef<float, N> &sdf) {
}
}
// 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) {
const float *bottom = sdf(0, y);
const float *top = sdf(0, y+1);
@ -223,7 +222,7 @@ void MSDFErrorCorrection::protectEdges(const BitmapConstRef<float, N> &sdf) {
}
}
// 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) {
const float *lb = sdf(0, y);
const float *rb = sdf(1, y);
@ -391,9 +390,9 @@ static bool hasDiagonalArtifact(const ArtifactClassifier &artifactClassifier, fl
template <int N>
void MSDFErrorCorrection::findErrors(const BitmapConstRef<float, N> &sdf) {
// Compute the expected deltas between values of horizontally, vertically, and diagonally adjacent texels.
double hSpan = minDeviationRatio*projection.unprojectVector(Vector2(invRange, 0)).length();
double vSpan = minDeviationRatio*projection.unprojectVector(Vector2(0, invRange)).length();
double dSpan = minDeviationRatio*projection.unprojectVector(Vector2(invRange)).length();
double hSpan = minDeviationRatio*transformation.unprojectVector(Vector2(transformation.distanceMapping(DistanceMapping::Delta(1)), 0)).length();
double vSpan = minDeviationRatio*transformation.unprojectVector(Vector2(0, transformation.distanceMapping(DistanceMapping::Delta(1)))).length();
double dSpan = minDeviationRatio*transformation.unprojectVector(Vector2(transformation.distanceMapping(DistanceMapping::Delta(1)))).length();
// Inspect all texels.
for (int y = 0; y < sdf.height; ++y) {
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>
void MSDFErrorCorrection::findErrors(const BitmapConstRef<float, N> &sdf, const Shape &shape) {
// Compute the expected deltas between values of horizontally, vertically, and diagonally adjacent texels.
double hSpan = minDeviationRatio*projection.unprojectVector(Vector2(invRange, 0)).length();
double vSpan = minDeviationRatio*projection.unprojectVector(Vector2(0, invRange)).length();
double dSpan = minDeviationRatio*projection.unprojectVector(Vector2(invRange)).length();
double hSpan = minDeviationRatio*transformation.unprojectVector(Vector2(transformation.distanceMapping(DistanceMapping::Delta(1)), 0)).length();
double vSpan = minDeviationRatio*transformation.unprojectVector(Vector2(0, transformation.distanceMapping(DistanceMapping::Delta(1)))).length();
double dSpan = minDeviationRatio*transformation.unprojectVector(Vector2(transformation.distanceMapping(DistanceMapping::Delta(1)))).length();
#ifdef MSDFGEN_USE_OPENMP
#pragma omp parallel
#endif
{
ShapeDistanceChecker<ContourCombiner, N> shapeDistanceChecker(sdf, shape, projection, invRange, minImproveRatio);
ShapeDistanceChecker<ContourCombiner, N> shapeDistanceChecker(sdf, shape, transformation, transformation.distanceMapping, minImproveRatio);
bool rightToLeft = false;
// Inspect all texels.
#ifdef MSDFGEN_USE_OPENMP
@ -439,7 +438,7 @@ void MSDFErrorCorrection::findErrors(const BitmapConstRef<float, N> &sdf, const
if ((*stencil(x, row)&ERROR))
continue;
const float *c = sdf(x, row);
shapeDistanceChecker.shapeCoord = projection.unproject(Point2(x+.5, y+.5));
shapeDistanceChecker.shapeCoord = transformation.unproject(Point2(x+.5, y+.5));
shapeDistanceChecker.sdfCoord = Point2(x+.5, row+.5);
shapeDistanceChecker.msd = c;
shapeDistanceChecker.protectedFlag = (*stencil(x, row)&PROTECTED) != 0;

View File

@ -1,7 +1,7 @@
#pragma once
#include "Projection.h"
#include "SDFTransformation.h"
#include "Shape.h"
#include "BitmapRef.hpp"
@ -20,7 +20,7 @@ public:
};
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.
void setMinDeviationRatio(double minDeviationRatio);
/// Sets the minimum ratio between the pre-correction distance error and the post-correction distance error.
@ -46,8 +46,7 @@ public:
private:
BitmapRef<byte, 1> stencil;
Projection projection;
double invRange;
SDFTransformation transformation;
double minDeviationRatio;
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 {
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)
return;
Bitmap<byte, 1> stencilBuffer;
@ -19,7 +19,7 @@ static void msdfErrorCorrectionInner(const BitmapRef<float, N> &sdf, const Shape
BitmapRef<byte, 1> stencil;
stencil.pixels = config.errorCorrection.buffer ? config.errorCorrection.buffer : (byte *) stencilBuffer;
stencil.width = sdf.width, stencil.height = sdf.height;
MSDFErrorCorrection ec(stencil, projection, range);
MSDFErrorCorrection ec(stencil, transformation);
ec.setMinDeviationRatio(config.errorCorrection.minDeviationRatio);
ec.setMinImproveRatio(config.errorCorrection.minImproveRatio);
switch (config.errorCorrection.mode) {
@ -49,9 +49,9 @@ static void msdfErrorCorrectionInner(const BitmapRef<float, N> &sdf, const Shape
}
template <int N>
static void msdfErrorCorrectionShapeless(const BitmapRef<float, N> &sdf, const Projection &projection, double range, double minDeviationRatio, bool protectAll) {
static void msdfErrorCorrectionShapeless(const BitmapRef<float, N> &sdf, const SDFTransformation &transformation, double minDeviationRatio, bool protectAll) {
Bitmap<byte, 1> stencilBuffer(sdf.width, sdf.height);
MSDFErrorCorrection ec(stencilBuffer, projection, range);
MSDFErrorCorrection ec(stencilBuffer, transformation);
ec.setMinDeviationRatio(minDeviationRatio);
if (protectAll)
ec.protectAll();
@ -59,25 +59,43 @@ static void msdfErrorCorrectionShapeless(const BitmapRef<float, N> &sdf, const P
ec.apply(sdf);
}
void msdfErrorCorrection(const BitmapRef<float, 3> &sdf, const Shape &shape, const Projection &projection, double range, const MSDFGeneratorConfig &config) {
msdfErrorCorrectionInner(sdf, shape, projection, range, config);
void msdfErrorCorrection(const BitmapRef<float, 3> &sdf, const Shape &shape, const SDFTransformation &transformation, const MSDFGeneratorConfig &config) {
msdfErrorCorrectionInner(sdf, shape, transformation, config);
}
void msdfErrorCorrection(const BitmapRef<float, 4> &sdf, const Shape &shape, const Projection &projection, double range, const MSDFGeneratorConfig &config) {
msdfErrorCorrectionInner(sdf, shape, projection, range, config);
void msdfErrorCorrection(const BitmapRef<float, 4> &sdf, const Shape &shape, const SDFTransformation &transformation, const MSDFGeneratorConfig &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) {
msdfErrorCorrectionShapeless(sdf, projection, range, minDeviationRatio, false);
void msdfFastDistanceErrorCorrection(const BitmapRef<float, 3> &sdf, const SDFTransformation &transformation, double minDeviationRatio) {
msdfErrorCorrectionShapeless(sdf, transformation, minDeviationRatio, false);
}
void msdfFastDistanceErrorCorrection(const BitmapRef<float, 4> &sdf, const Projection &projection, double range, double minDeviationRatio) {
msdfErrorCorrectionShapeless(sdf, projection, range, minDeviationRatio, false);
void msdfFastDistanceErrorCorrection(const BitmapRef<float, 4> &sdf, const SDFTransformation &transformation, double minDeviationRatio) {
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) {
msdfErrorCorrectionShapeless(sdf, projection, range, minDeviationRatio, true);
void msdfFastEdgeErrorCorrection(const BitmapRef<float, 3> &sdf, const SDFTransformation &transformation, double minDeviationRatio) {
msdfErrorCorrectionShapeless(sdf, transformation, minDeviationRatio, true);
}
void msdfFastEdgeErrorCorrection(const BitmapRef<float, 4> &sdf, const Projection &projection, double range, double minDeviationRatio) {
msdfErrorCorrectionShapeless(sdf, projection, range, minDeviationRatio, true);
void msdfFastEdgeErrorCorrection(const BitmapRef<float, 4> &sdf, const SDFTransformation &transformation, double minDeviationRatio) {
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
#include "Vector2.hpp"
#include "Range.hpp"
#include "Projection.h"
#include "SDFTransformation.h"
#include "Shape.h"
#include "BitmapRef.hpp"
#include "generator-config.h"
@ -10,16 +12,22 @@
namespace msdfgen {
/// Predicts potential artifacts caused by the interpolation of the MSDF and corrects them by converting nearby texels to single-channel.
void msdfErrorCorrection(const BitmapRef<float, 3> &sdf, const Shape &shape, const Projection &projection, double range, const MSDFGeneratorConfig &config = MSDFGeneratorConfig());
void msdfErrorCorrection(const BitmapRef<float, 4> &sdf, const Shape &shape, const Projection &projection, double range, const MSDFGeneratorConfig &config = MSDFGeneratorConfig());
void msdfErrorCorrection(const BitmapRef<float, 3> &sdf, const Shape &shape, const SDFTransformation &transformation, 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.
void msdfFastDistanceErrorCorrection(const BitmapRef<float, 3> &sdf, const Projection &projection, double range, double minDeviationRatio = ErrorCorrectionConfig::defaultMinDeviationRatio);
void msdfFastDistanceErrorCorrection(const BitmapRef<float, 4> &sdf, const Projection &projection, double range, double minDeviationRatio = ErrorCorrectionConfig::defaultMinDeviationRatio);
void msdfFastDistanceErrorCorrection(const BitmapRef<float, 3> &sdf, const SDFTransformation &transformation, 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.
void msdfFastEdgeErrorCorrection(const BitmapRef<float, 3> &sdf, const Projection &projection, double range, double minDeviationRatio = ErrorCorrectionConfig::defaultMinDeviationRatio);
void msdfFastEdgeErrorCorrection(const BitmapRef<float, 4> &sdf, const Projection &projection, double range, double minDeviationRatio = ErrorCorrectionConfig::defaultMinDeviationRatio);
void msdfFastEdgeErrorCorrection(const BitmapRef<float, 3> &sdf, const SDFTransformation &transformation, 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.
void msdfErrorCorrection_legacy(const BitmapRef<float, 3> &output, const Vector2 &threshold);

View File

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

View File

@ -2,89 +2,175 @@
#include "render-sdf.h"
#include "arithmetics.hpp"
#include "DistanceMapping.h"
#include "pixel-conversion.hpp"
#include "bitmap-interpolation.hpp"
namespace msdfgen {
static float distVal(float dist, double pxRange, float midValue) {
if (!pxRange)
return (float) (dist > midValue);
return (float) clamp((dist-midValue)*pxRange+.5);
static float distVal(float dist, DistanceMapping mapping) {
return (float) clamp(mapping(dist)+.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);
pxRange *= (double) (output.width+output.height)/(sdf.width+sdf.height);
for (int y = 0; y < output.height; ++y)
for (int x = 0; x < output.width; ++x) {
float sd;
interpolate(&sd, sdf, scale*Point2(x+.5, y+.5));
*output(x, y) = distVal(sd, pxRange, midValue);
if (sdfPxRange.lower == sdfPxRange.upper) {
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) = 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);
pxRange *= (double) (output.width+output.height)/(sdf.width+sdf.height);
for (int y = 0; y < output.height; ++y)
for (int x = 0; x < output.width; ++x) {
float sd;
interpolate(&sd, sdf, scale*Point2(x+.5, y+.5));
float v = distVal(sd, pxRange, midValue);
output(x, y)[0] = v;
output(x, y)[1] = v;
output(x, y)[2] = v;
if (sdfPxRange.lower == sdfPxRange.upper) {
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 = float(sd >= sdThreshold);
output(x, y)[0] = v;
output(x, y)[1] = 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);
pxRange *= (double) (output.width+output.height)/(sdf.width+sdf.height);
for (int y = 0; y < output.height; ++y)
for (int x = 0; x < output.width; ++x) {
float sd[3];
interpolate(sd, sdf, scale*Point2(x+.5, y+.5));
*output(x, y) = distVal(median(sd[0], sd[1], sd[2]), pxRange, midValue);
if (sdfPxRange.lower == sdfPxRange.upper) {
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) = 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);
pxRange *= (double) (output.width+output.height)/(sdf.width+sdf.height);
for (int y = 0; y < output.height; ++y)
for (int x = 0; x < output.width; ++x) {
float sd[3];
interpolate(sd, sdf, scale*Point2(x+.5, y+.5));
output(x, y)[0] = distVal(sd[0], pxRange, midValue);
output(x, y)[1] = distVal(sd[1], pxRange, midValue);
output(x, y)[2] = distVal(sd[2], pxRange, midValue);
if (sdfPxRange.lower == sdfPxRange.upper) {
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] = float(sd[0] >= sdThreshold);
output(x, y)[1] = float(sd[1] >= sdThreshold);
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);
pxRange *= (double) (output.width+output.height)/(sdf.width+sdf.height);
for (int y = 0; y < output.height; ++y)
for (int x = 0; x < output.width; ++x) {
float sd[4];
interpolate(sd, sdf, scale*Point2(x+.5, y+.5));
*output(x, y) = distVal(median(sd[0], sd[1], sd[2]), pxRange, midValue);
if (sdfPxRange.lower == sdfPxRange.upper) {
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) = 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);
pxRange *= (double) (output.width+output.height)/(sdf.width+sdf.height);
for (int y = 0; y < output.height; ++y)
for (int x = 0; x < output.width; ++x) {
float sd[4];
interpolate(sd, sdf, scale*Point2(x+.5, y+.5));
output(x, y)[0] = distVal(sd[0], pxRange, midValue);
output(x, y)[1] = distVal(sd[1], pxRange, midValue);
output(x, y)[2] = distVal(sd[2], pxRange, midValue);
output(x, y)[3] = distVal(sd[3], pxRange, midValue);
if (sdfPxRange.lower == sdfPxRange.upper) {
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] = float(sd[0] >= sdThreshold);
output(x, y)[1] = float(sd[1] >= sdThreshold);
output(x, y)[2] = float(sd[2] >= sdThreshold);
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) {

View File

@ -2,17 +2,18 @@
#pragma once
#include "Vector2.hpp"
#include "Range.hpp"
#include "BitmapRef.hpp"
namespace msdfgen {
/// Reconstructs the shape's appearance into output from the distance field sdf.
void renderSDF(const BitmapRef<float, 1> &output, const BitmapConstRef<float, 1> &sdf, double pxRange = 0, float midValue = .5f);
void renderSDF(const BitmapRef<float, 3> &output, const BitmapConstRef<float, 1> &sdf, double pxRange = 0, float midValue = .5f);
void renderSDF(const BitmapRef<float, 1> &output, const BitmapConstRef<float, 3> &sdf, double pxRange = 0, float midValue = .5f);
void renderSDF(const BitmapRef<float, 3> &output, const BitmapConstRef<float, 3> &sdf, double pxRange = 0, float midValue = .5f);
void renderSDF(const BitmapRef<float, 1> &output, const BitmapConstRef<float, 4> &sdf, double pxRange = 0, float midValue = .5f);
void renderSDF(const BitmapRef<float, 4> &output, const BitmapConstRef<float, 4> &sdf, double pxRange = 0, float midValue = .5f);
void renderSDF(const BitmapRef<float, 1> &output, const BitmapConstRef<float, 1> &sdf, Range sdfPxRange = 0, float sdThreshold = .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, Range sdfPxRange = 0, float sdThreshold = .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, Range sdfPxRange = 0, float sdThreshold = .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.
void simulate8bit(const BitmapRef<float, 1> &bitmap);

121
main.cpp
View File

@ -388,6 +388,10 @@ static const char *const helpText =
"OPTIONS\n"
" -angle <angle>\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"
"\tSets the scale used to convert shape units to pixels asymmetrically.\n"
" -autoframe\n"
@ -396,8 +400,6 @@ static const char *const helpText =
"\tSelects the strategy of the edge coloring heuristic.\n"
" -dimensions <width> <height>\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"
"\tOverrides automatic edge coloring with the specified color sequence.\n"
" -errorcorrection <mode>\n"
@ -555,8 +557,8 @@ int main(int argc, const char *const *argv) {
RANGE_UNIT,
RANGE_PX
} rangeMode = RANGE_PX;
double range = 1;
double pxRange = 2;
Range range(1);
Range pxRange(2);
Vector2 translate;
Vector2 scale = 1;
bool scaleSpecified = false;
@ -740,18 +742,42 @@ int main(int argc, const char *const *argv) {
}
ARG_CASE("-range" ARG_CASE_OR "-unitrange", 1) {
double r;
if (!(parseDouble(r, argv[argPos++]) && r > 0))
ABORT("Invalid range argument. Use -range <range> with a positive real number.");
if (!parseDouble(r, argv[argPos++]))
ABORT("Invalid range argument. Use -range <range> with a real number.");
if (r == 0)
ABORT("Range must be non-zero.");
rangeMode = RANGE_UNIT;
range = r;
range = Range(r);
continue;
}
ARG_CASE("-pxrange", 1) {
double r;
if (!(parseDouble(r, argv[argPos++]) && r > 0))
ABORT("Invalid range argument. Use -pxrange <range> with a positive real number.");
if (!parseDouble(r, argv[argPos++]))
ABORT("Invalid range argument. Use -pxrange <range> with a real number.");
if (r == 0)
ABORT("Range must be non-zero.");
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;
}
ARG_CASE("-scale", 1) {
@ -1039,16 +1065,22 @@ int main(int argc, const char *const *argv) {
if (autoFrame || mode == METRICS || printMetrics || orientation == GUESS)
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
if (autoFrame) {
double l = bounds.l, b = bounds.b, r = bounds.r, t = bounds.t;
Vector2 frame(width, height);
double m = .5+(double) outputDistanceShift;
if (!scaleSpecified) {
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
frame -= 2*m*pxRange;
frame += 2*pxRange.lower;
}
if (l >= r || b >= t)
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)
translate += m*pxRange/scale;
translate -= pxRange.lower/scale;
}
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);
}
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)
fclose(out);
}
// Compute output
Projection projection(scale, translate);
SDFTransformation transformation(Projection(scale, translate), range);
Bitmap<float, 1> sdf;
Bitmap<float, 3> msdf;
Bitmap<float, 4> mtsdf;
@ -1125,7 +1157,7 @@ int main(int argc, const char *const *argv) {
if (legacyMode)
generateSDF_legacy(sdf, shape, range, scale, translate);
else
generateSDF(sdf, shape, projection, range, generatorConfig);
generateSDF(sdf, shape, transformation, generatorConfig);
break;
}
case PERPENDICULAR: {
@ -1133,7 +1165,7 @@ int main(int argc, const char *const *argv) {
if (legacyMode)
generatePSDF_legacy(sdf, shape, range, scale, translate);
else
generatePSDF(sdf, shape, projection, range, generatorConfig);
generatePSDF(sdf, shape, transformation, generatorConfig);
break;
}
case MULTI: {
@ -1145,7 +1177,7 @@ int main(int argc, const char *const *argv) {
if (legacyMode)
generateMSDF_legacy(msdf, shape, range, scale, translate, generatorConfig.errorCorrection);
else
generateMSDF(msdf, shape, projection, range, generatorConfig);
generateMSDF(msdf, shape, transformation, generatorConfig);
break;
}
case MULTI_AND_TRUE: {
@ -1157,7 +1189,7 @@ int main(int argc, const char *const *argv) {
if (legacyMode)
generateMTSDF_legacy(mtsdf, shape, range, scale, translate, generatorConfig.errorCorrection);
else
generateMTSDF(mtsdf, shape, projection, range, generatorConfig);
generateMTSDF(mtsdf, shape, transformation, generatorConfig);
break;
}
default:;
@ -1188,40 +1220,19 @@ int main(int argc, const char *const *argv) {
switch (mode) {
case SINGLE:
case PERPENDICULAR:
distanceSignCorrection(sdf, shape, projection, fillRule);
distanceSignCorrection(sdf, shape, transformation, fillRule);
break;
case MULTI:
distanceSignCorrection(msdf, shape, projection, fillRule);
msdfErrorCorrection(msdf, shape, projection, range, postErrorCorrectionConfig);
distanceSignCorrection(msdf, shape, transformation, fillRule);
msdfErrorCorrection(msdf, shape, transformation, postErrorCorrectionConfig);
break;
case MULTI_AND_TRUE:
distanceSignCorrection(mtsdf, shape, projection, fillRule);
msdfErrorCorrection(msdf, shape, projection, range, postErrorCorrectionConfig);
distanceSignCorrection(mtsdf, shape, transformation, fillRule);
msdfErrorCorrection(msdf, shape, transformation, postErrorCorrectionConfig);
break;
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
if (shapeExport) {
@ -1243,18 +1254,18 @@ int main(int argc, const char *const *argv) {
if (is8bitFormat(format) && (testRenderMulti || testRender || estimateError))
simulate8bit(sdf);
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);
}
if (testRenderMulti) {
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))
fputs("Failed to write test render file.\n", stderr);
}
if (testRender) {
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))
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))
simulate8bit(msdf);
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);
}
if (testRenderMulti) {
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))
fputs("Failed to write test render file.\n", stderr);
}
if (testRender) {
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))
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))
simulate8bit(mtsdf);
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);
}
if (testRenderMulti) {
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))
fputs("Failed to write test render file.\n", stderr);
}
if (testRender) {
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))
fputs("Failed to write test render file.\n", stderr);
}

View File

@ -18,7 +18,10 @@
#include "core/base.h"
#include "core/arithmetics.hpp"
#include "core/Vector2.hpp"
#include "core/Range.hpp"
#include "core/Projection.h"
#include "core/DistanceMapping.h"
#include "core/SDFTransformation.h"
#include "core/Scanline.h"
#include "core/Shape.h"
#include "core/BitmapRef.hpp"
@ -38,31 +41,35 @@
namespace msdfgen {
/// Generates a conventional single-channel signed distance field.
void generateSDF(const BitmapRef<float, 1> &output, const Shape &shape, const Projection &projection, double range, const GeneratorConfig &config = GeneratorConfig());
void generateSDF(const BitmapRef<float, 1> &output, const Shape &shape, const SDFTransformation &transformation, const GeneratorConfig &config = GeneratorConfig());
/// 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)
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.
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
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 generatePSDF(const BitmapRef<float, 1> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, bool overlapSupport = true);
void generatePseudoSDF(const BitmapRef<float, 1> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, bool overlapSupport = true);
void generateMSDF(const BitmapRef<float, 3> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, const ErrorCorrectionConfig &errorCorrectionConfig = ErrorCorrectionConfig(), bool overlapSupport = true);
void generateMTSDF(const BitmapRef<float, 4> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, const ErrorCorrectionConfig &errorCorrectionConfig = ErrorCorrectionConfig(), bool overlapSupport = true);
void generateSDF(const BitmapRef<float, 1> &output, const Shape &shape, Range 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, Range range, const Vector2 &scale, const Vector2 &translate, 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, 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.
void generateSDF_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, double range, const Vector2 &scale, const Vector2 &translate);
void generatePseudoSDF_legacy(const BitmapRef<float, 1> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate);
void generateMSDF_legacy(const BitmapRef<float, 3> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, ErrorCorrectionConfig errorCorrectionConfig = ErrorCorrectionConfig());
void generateMTSDF_legacy(const BitmapRef<float, 4> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, ErrorCorrectionConfig errorCorrectionConfig = ErrorCorrectionConfig());
void generateSDF_legacy(const BitmapRef<float, 1> &output, const Shape &shape, Range 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, Range range, const Vector2 &scale, const Vector2 &translate);
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, Range range, const Vector2 &scale, const Vector2 &translate, ErrorCorrectionConfig errorCorrectionConfig = ErrorCorrectionConfig());
}