Renamed pseudo-distance to perpendicular distance

This commit is contained in:
Chlumsky 2024-03-10 10:30:59 +01:00
parent 35f92541c4
commit 64c3e24829
10 changed files with 129 additions and 111 deletions

View File

@ -4,7 +4,7 @@ This is a utility for generating signed distance fields from vector shapes and f
which serve as a texture representation that can be used in real-time graphics to efficiently reproduce said shapes.
Although it can also be used to generate conventional signed distance fields best known from
[this Valve paper](https://steamcdn-a.akamaihd.net/apps/valve/2007/SIGGRAPH2007_AlphaTestedMagnification.pdf)
and pseudo-distance fields, its primary purpose is to generate multi-channel distance fields,
and perpendicular distance fields, its primary purpose is to generate multi-channel distance fields,
using a method I have developed. Unlike monochrome distance fields, they have the ability
to reproduce sharp corners almost perfectly by utilizing all three color channels.
@ -52,7 +52,7 @@ where only the input specification is required.
Mode can be one of:
- **sdf** – generates a conventional monochrome (true) signed distance field.
- **psdf** – generates a monochrome signed pseudo-distance field.
- **psdf** – generates a monochrome signed perpendicular distance field.
- **msdf** (default) – generates a multi-channel signed distance field using my new method.
- **mtsdf** – generates a combined multi-channel and true signed distance field in the alpha channel.
@ -104,10 +104,9 @@ in order to generate a distance field. Please note that all classes and function
It consists of closed contours, which in turn consist of edges. An edge is represented by a `LinearEdge`, `QuadraticEdge`,
or `CubicEdge`. You can construct them from two endpoints and 0 to 2 Bézier control points.
- Normalize the shape using its `normalize` method and assign colors to edges if you need a multi-channel SDF.
This can be performed automatically using the `edgeColoringSimple` heuristic, or manually by setting each edge's
`color` member. Keep in mind that at least two color channels must be turned on in each edge, and iff two edges meet
at a sharp corner, they must only have one channel in common.
- Call `generateSDF`, `generatePseudoSDF`, or `generateMSDF` to generate a distance field into a floating point
This can be performed automatically using the `edgeColoringSimple` (or other) heuristic, or manually by setting each edge's
`color` member. Keep in mind that at least two color channels must be turned on in each edge.
- Call `generateSDF`, `generatePSDF`, `generateMSDF`, or `generateMTSDF` to generate a distance field into a floating point
`Bitmap` object. This can then be worked with further or saved to a file using `saveBmp`, `savePng`, or `saveTiff`.
- You may also render an image from the distance field using `renderSDF`. Consider calling `simulate8bit`
on the distance field beforehand to simulate the standard 8 bits/channel image format.

View File

@ -94,7 +94,7 @@ public:
return ArtifactClassifier(this, direction, span);
}
private:
ShapeDistanceFinder<ContourCombiner<PseudoDistanceSelector> > distanceFinder;
ShapeDistanceFinder<ContourCombiner<PerpendicularDistanceSelector> > distanceFinder;
BitmapConstRef<float, N> sdf;
double invRange;
Vector2 texelSize;

View File

@ -50,7 +50,7 @@ typename SimpleContourCombiner<EdgeSelector>::DistanceType SimpleContourCombiner
}
template class SimpleContourCombiner<TrueDistanceSelector>;
template class SimpleContourCombiner<PseudoDistanceSelector>;
template class SimpleContourCombiner<PerpendicularDistanceSelector>;
template class SimpleContourCombiner<MultiDistanceSelector>;
template class SimpleContourCombiner<MultiAndTrueDistanceSelector>;
@ -134,7 +134,7 @@ typename OverlappingContourCombiner<EdgeSelector>::DistanceType OverlappingConto
}
template class OverlappingContourCombiner<TrueDistanceSelector>;
template class OverlappingContourCombiner<PseudoDistanceSelector>;
template class OverlappingContourCombiner<PerpendicularDistanceSelector>;
template class OverlappingContourCombiner<MultiDistanceSelector>;
template class OverlappingContourCombiner<MultiAndTrueDistanceSelector>;

View File

@ -25,15 +25,15 @@ EdgeSegment *EdgeSegment::create(Point2 p0, Point2 p1, Point2 p2, Point2 p3, Edg
return new CubicSegment(p0, p1, p2, p3, edgeColor);
}
void EdgeSegment::distanceToPseudoDistance(SignedDistance &distance, Point2 origin, double param) const {
void EdgeSegment::distanceToPerpendicularDistance(SignedDistance &distance, Point2 origin, double param) const {
if (param < 0) {
Vector2 dir = direction(0).normalize();
Vector2 aq = origin-point(0);
double ts = dotProduct(aq, dir);
if (ts < 0) {
double pseudoDistance = crossProduct(aq, dir);
if (fabs(pseudoDistance) <= fabs(distance.distance)) {
distance.distance = pseudoDistance;
double perpendicularDistance = crossProduct(aq, dir);
if (fabs(perpendicularDistance) <= fabs(distance.distance)) {
distance.distance = perpendicularDistance;
distance.dot = 0;
}
}
@ -42,9 +42,9 @@ void EdgeSegment::distanceToPseudoDistance(SignedDistance &distance, Point2 orig
Vector2 bq = origin-point(1);
double ts = dotProduct(bq, dir);
if (ts > 0) {
double pseudoDistance = crossProduct(bq, dir);
if (fabs(pseudoDistance) <= fabs(distance.distance)) {
distance.distance = pseudoDistance;
double perpendicularDistance = crossProduct(bq, dir);
if (fabs(perpendicularDistance) <= fabs(distance.distance)) {
distance.distance = perpendicularDistance;
distance.dot = 0;
}
}

View File

@ -37,8 +37,8 @@ public:
virtual Vector2 directionChange(double param) const = 0;
/// Returns the minimum signed distance between origin and the edge.
virtual SignedDistance signedDistance(Point2 origin, double &param) const = 0;
/// Converts a previously retrieved signed distance from origin to pseudo-distance.
virtual void distanceToPseudoDistance(SignedDistance &distance, Point2 origin, double param) const;
/// Converts a previously retrieved signed distance from origin to perpendicular distance.
virtual void distanceToPerpendicularDistance(SignedDistance &distance, Point2 origin, double param) const;
/// Outputs a list of (at most three) intersections (their X coordinates) with an infinite horizontal scanline at y and returns how many there are.
virtual int scanlineIntersections(double x[3], int dy[3], double y) const = 0;
/// Adjusts the bounding box to fit the edge segment.

View File

@ -36,48 +36,48 @@ TrueDistanceSelector::DistanceType TrueDistanceSelector::distance() const {
return minDistance.distance;
}
PseudoDistanceSelectorBase::EdgeCache::EdgeCache() : absDistance(0), aDomainDistance(0), bDomainDistance(0), aPseudoDistance(0), bPseudoDistance(0) { }
PerpendicularDistanceSelectorBase::EdgeCache::EdgeCache() : absDistance(0), aDomainDistance(0), bDomainDistance(0), aPerpendicularDistance(0), bPerpendicularDistance(0) { }
bool PseudoDistanceSelectorBase::getPseudoDistance(double &distance, const Vector2 &ep, const Vector2 &edgeDir) {
bool PerpendicularDistanceSelectorBase::getPerpendicularDistance(double &distance, const Vector2 &ep, const Vector2 &edgeDir) {
double ts = dotProduct(ep, edgeDir);
if (ts > 0) {
double pseudoDistance = crossProduct(ep, edgeDir);
if (fabs(pseudoDistance) < fabs(distance)) {
distance = pseudoDistance;
double perpendicularDistance = crossProduct(ep, edgeDir);
if (fabs(perpendicularDistance) < fabs(distance)) {
distance = perpendicularDistance;
return true;
}
}
return false;
}
PseudoDistanceSelectorBase::PseudoDistanceSelectorBase() : minNegativePseudoDistance(-fabs(minTrueDistance.distance)), minPositivePseudoDistance(fabs(minTrueDistance.distance)), nearEdge(NULL), nearEdgeParam(0) { }
PerpendicularDistanceSelectorBase::PerpendicularDistanceSelectorBase() : minNegativePerpendicularDistance(-fabs(minTrueDistance.distance)), minPositivePerpendicularDistance(fabs(minTrueDistance.distance)), nearEdge(NULL), nearEdgeParam(0) { }
void PseudoDistanceSelectorBase::reset(double delta) {
void PerpendicularDistanceSelectorBase::reset(double delta) {
minTrueDistance.distance += nonZeroSign(minTrueDistance.distance)*delta;
minNegativePseudoDistance = -fabs(minTrueDistance.distance);
minPositivePseudoDistance = fabs(minTrueDistance.distance);
minNegativePerpendicularDistance = -fabs(minTrueDistance.distance);
minPositivePerpendicularDistance = fabs(minTrueDistance.distance);
nearEdge = NULL;
nearEdgeParam = 0;
}
bool PseudoDistanceSelectorBase::isEdgeRelevant(const EdgeCache &cache, const EdgeSegment *edge, const Point2 &p) const {
bool PerpendicularDistanceSelectorBase::isEdgeRelevant(const EdgeCache &cache, const EdgeSegment *edge, const Point2 &p) const {
double delta = DISTANCE_DELTA_FACTOR*(p-cache.point).length();
return (
cache.absDistance-delta <= fabs(minTrueDistance.distance) ||
fabs(cache.aDomainDistance) < delta ||
fabs(cache.bDomainDistance) < delta ||
(cache.aDomainDistance > 0 && (cache.aPseudoDistance < 0 ?
cache.aPseudoDistance+delta >= minNegativePseudoDistance :
cache.aPseudoDistance-delta <= minPositivePseudoDistance
(cache.aDomainDistance > 0 && (cache.aPerpendicularDistance < 0 ?
cache.aPerpendicularDistance+delta >= minNegativePerpendicularDistance :
cache.aPerpendicularDistance-delta <= minPositivePerpendicularDistance
)) ||
(cache.bDomainDistance > 0 && (cache.bPseudoDistance < 0 ?
cache.bPseudoDistance+delta >= minNegativePseudoDistance :
cache.bPseudoDistance-delta <= minPositivePseudoDistance
(cache.bDomainDistance > 0 && (cache.bPerpendicularDistance < 0 ?
cache.bPerpendicularDistance+delta >= minNegativePerpendicularDistance :
cache.bPerpendicularDistance-delta <= minPositivePerpendicularDistance
))
);
}
void PseudoDistanceSelectorBase::addEdgeTrueDistance(const EdgeSegment *edge, const SignedDistance &distance, double param) {
void PerpendicularDistanceSelectorBase::addEdgeTrueDistance(const EdgeSegment *edge, const SignedDistance &distance, double param) {
if (distance < minTrueDistance) {
minTrueDistance = distance;
nearEdge = edge;
@ -85,47 +85,47 @@ void PseudoDistanceSelectorBase::addEdgeTrueDistance(const EdgeSegment *edge, co
}
}
void PseudoDistanceSelectorBase::addEdgePseudoDistance(double distance) {
if (distance <= 0 && distance > minNegativePseudoDistance)
minNegativePseudoDistance = distance;
if (distance >= 0 && distance < minPositivePseudoDistance)
minPositivePseudoDistance = distance;
void PerpendicularDistanceSelectorBase::addEdgePerpendicularDistance(double distance) {
if (distance <= 0 && distance > minNegativePerpendicularDistance)
minNegativePerpendicularDistance = distance;
if (distance >= 0 && distance < minPositivePerpendicularDistance)
minPositivePerpendicularDistance = distance;
}
void PseudoDistanceSelectorBase::merge(const PseudoDistanceSelectorBase &other) {
void PerpendicularDistanceSelectorBase::merge(const PerpendicularDistanceSelectorBase &other) {
if (other.minTrueDistance < minTrueDistance) {
minTrueDistance = other.minTrueDistance;
nearEdge = other.nearEdge;
nearEdgeParam = other.nearEdgeParam;
}
if (other.minNegativePseudoDistance > minNegativePseudoDistance)
minNegativePseudoDistance = other.minNegativePseudoDistance;
if (other.minPositivePseudoDistance < minPositivePseudoDistance)
minPositivePseudoDistance = other.minPositivePseudoDistance;
if (other.minNegativePerpendicularDistance > minNegativePerpendicularDistance)
minNegativePerpendicularDistance = other.minNegativePerpendicularDistance;
if (other.minPositivePerpendicularDistance < minPositivePerpendicularDistance)
minPositivePerpendicularDistance = other.minPositivePerpendicularDistance;
}
double PseudoDistanceSelectorBase::computeDistance(const Point2 &p) const {
double minDistance = minTrueDistance.distance < 0 ? minNegativePseudoDistance : minPositivePseudoDistance;
double PerpendicularDistanceSelectorBase::computeDistance(const Point2 &p) const {
double minDistance = minTrueDistance.distance < 0 ? minNegativePerpendicularDistance : minPositivePerpendicularDistance;
if (nearEdge) {
SignedDistance distance = minTrueDistance;
nearEdge->distanceToPseudoDistance(distance, p, nearEdgeParam);
nearEdge->distanceToPerpendicularDistance(distance, p, nearEdgeParam);
if (fabs(distance.distance) < fabs(minDistance))
minDistance = distance.distance;
}
return minDistance;
}
SignedDistance PseudoDistanceSelectorBase::trueDistance() const {
SignedDistance PerpendicularDistanceSelectorBase::trueDistance() const {
return minTrueDistance;
}
void PseudoDistanceSelector::reset(const Point2 &p) {
void PerpendicularDistanceSelector::reset(const Point2 &p) {
double delta = DISTANCE_DELTA_FACTOR*(p-this->p).length();
PseudoDistanceSelectorBase::reset(delta);
PerpendicularDistanceSelectorBase::reset(delta);
this->p = p;
}
void PseudoDistanceSelector::addEdge(EdgeCache &cache, const EdgeSegment *prevEdge, const EdgeSegment *edge, const EdgeSegment *nextEdge) {
void PerpendicularDistanceSelector::addEdge(EdgeCache &cache, const EdgeSegment *prevEdge, const EdgeSegment *edge, const EdgeSegment *nextEdge) {
if (isEdgeRelevant(cache, edge, p)) {
double param;
SignedDistance distance = edge->signedDistance(p, param);
@ -143,22 +143,22 @@ void PseudoDistanceSelector::addEdge(EdgeCache &cache, const EdgeSegment *prevEd
double bdd = -dotProduct(bp, (bDir+nextDir).normalize(true));
if (add > 0) {
double pd = distance.distance;
if (getPseudoDistance(pd, ap, -aDir))
addEdgePseudoDistance(pd = -pd);
cache.aPseudoDistance = pd;
if (getPerpendicularDistance(pd, ap, -aDir))
addEdgePerpendicularDistance(pd = -pd);
cache.aPerpendicularDistance = pd;
}
if (bdd > 0) {
double pd = distance.distance;
if (getPseudoDistance(pd, bp, bDir))
addEdgePseudoDistance(pd);
cache.bPseudoDistance = pd;
if (getPerpendicularDistance(pd, bp, bDir))
addEdgePerpendicularDistance(pd);
cache.bPerpendicularDistance = pd;
}
cache.aDomainDistance = add;
cache.bDomainDistance = bdd;
}
}
PseudoDistanceSelector::DistanceType PseudoDistanceSelector::distance() const {
PerpendicularDistanceSelector::DistanceType PerpendicularDistanceSelector::distance() const {
return computeDistance(p);
}
@ -197,28 +197,28 @@ void MultiDistanceSelector::addEdge(EdgeCache &cache, const EdgeSegment *prevEdg
double bdd = -dotProduct(bp, (bDir+nextDir).normalize(true));
if (add > 0) {
double pd = distance.distance;
if (PseudoDistanceSelectorBase::getPseudoDistance(pd, ap, -aDir)) {
if (PerpendicularDistanceSelectorBase::getPerpendicularDistance(pd, ap, -aDir)) {
pd = -pd;
if (edge->color&RED)
r.addEdgePseudoDistance(pd);
r.addEdgePerpendicularDistance(pd);
if (edge->color&GREEN)
g.addEdgePseudoDistance(pd);
g.addEdgePerpendicularDistance(pd);
if (edge->color&BLUE)
b.addEdgePseudoDistance(pd);
b.addEdgePerpendicularDistance(pd);
}
cache.aPseudoDistance = pd;
cache.aPerpendicularDistance = pd;
}
if (bdd > 0) {
double pd = distance.distance;
if (PseudoDistanceSelectorBase::getPseudoDistance(pd, bp, bDir)) {
if (PerpendicularDistanceSelectorBase::getPerpendicularDistance(pd, bp, bDir)) {
if (edge->color&RED)
r.addEdgePseudoDistance(pd);
r.addEdgePerpendicularDistance(pd);
if (edge->color&GREEN)
g.addEdgePseudoDistance(pd);
g.addEdgePerpendicularDistance(pd);
if (edge->color&BLUE)
b.addEdgePseudoDistance(pd);
b.addEdgePerpendicularDistance(pd);
}
cache.bPseudoDistance = pd;
cache.bPerpendicularDistance = pd;
}
cache.aDomainDistance = add;
cache.bDomainDistance = bdd;

View File

@ -38,40 +38,40 @@ private:
};
class PseudoDistanceSelectorBase {
class PerpendicularDistanceSelectorBase {
public:
struct EdgeCache {
Point2 point;
double absDistance;
double aDomainDistance, bDomainDistance;
double aPseudoDistance, bPseudoDistance;
double aPerpendicularDistance, bPerpendicularDistance;
EdgeCache();
};
static bool getPseudoDistance(double &distance, const Vector2 &ep, const Vector2 &edgeDir);
static bool getPerpendicularDistance(double &distance, const Vector2 &ep, const Vector2 &edgeDir);
PseudoDistanceSelectorBase();
PerpendicularDistanceSelectorBase();
void reset(double delta);
bool isEdgeRelevant(const EdgeCache &cache, const EdgeSegment *edge, const Point2 &p) const;
void addEdgeTrueDistance(const EdgeSegment *edge, const SignedDistance &distance, double param);
void addEdgePseudoDistance(double distance);
void merge(const PseudoDistanceSelectorBase &other);
void addEdgePerpendicularDistance(double distance);
void merge(const PerpendicularDistanceSelectorBase &other);
double computeDistance(const Point2 &p) const;
SignedDistance trueDistance() const;
private:
SignedDistance minTrueDistance;
double minNegativePseudoDistance;
double minPositivePseudoDistance;
double minNegativePerpendicularDistance;
double minPositivePerpendicularDistance;
const EdgeSegment *nearEdge;
double nearEdgeParam;
};
/// Selects the nearest edge by its pseudo-distance.
class PseudoDistanceSelector : public PseudoDistanceSelectorBase {
/// Selects the nearest edge by its perpendicular distance.
class PerpendicularDistanceSelector : public PerpendicularDistanceSelectorBase {
public:
typedef double DistanceType;
@ -85,12 +85,12 @@ private:
};
/// Selects the nearest edge for each of the three channels by its pseudo-distance.
/// Selects the nearest edge for each of the three channels by its perpendicular distance.
class MultiDistanceSelector {
public:
typedef MultiDistance DistanceType;
typedef PseudoDistanceSelectorBase::EdgeCache EdgeCache;
typedef PerpendicularDistanceSelectorBase::EdgeCache EdgeCache;
void reset(const Point2 &p);
void addEdge(EdgeCache &cache, const EdgeSegment *prevEdge, const EdgeSegment *edge, const EdgeSegment *nextEdge);
@ -100,11 +100,11 @@ public:
private:
Point2 p;
PseudoDistanceSelectorBase r, g, b;
PerpendicularDistanceSelectorBase r, g, b;
};
/// Selects the nearest edge for each of the three color channels by its pseudo-distance and by true distance for the alpha channel.
/// Selects the nearest edge for each of the three color channels by its perpendicular distance and by true distance for the alpha channel.
class MultiAndTrueDistanceSelector : public MultiDistanceSelector {
public:

View File

@ -81,11 +81,11 @@ void generateSDF(const BitmapRef<float, 1> &output, const Shape &shape, const Pr
generateDistanceField<SimpleContourCombiner<TrueDistanceSelector> >(output, shape, projection, range);
}
void generatePseudoSDF(const BitmapRef<float, 1> &output, const Shape &shape, const Projection &projection, double range, const GeneratorConfig &config) {
void generatePSDF(const BitmapRef<float, 1> &output, const Shape &shape, const Projection &projection, double range, const GeneratorConfig &config) {
if (config.overlapSupport)
generateDistanceField<OverlappingContourCombiner<PseudoDistanceSelector> >(output, shape, projection, range);
generateDistanceField<OverlappingContourCombiner<PerpendicularDistanceSelector> >(output, shape, projection, range);
else
generateDistanceField<SimpleContourCombiner<PseudoDistanceSelector> >(output, shape, projection, range);
generateDistanceField<SimpleContourCombiner<PerpendicularDistanceSelector> >(output, shape, projection, range);
}
void generateMSDF(const BitmapRef<float, 3> &output, const Shape &shape, const Projection &projection, double range, const MSDFGeneratorConfig &config) {
@ -106,12 +106,20 @@ void generateMTSDF(const BitmapRef<float, 4> &output, const Shape &shape, const
// 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 generateSDF(const BitmapRef<float, 1> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, bool overlapSupport) {
generateSDF(output, shape, Projection(scale, translate), range, GeneratorConfig(overlapSupport));
}
void generatePSDF(const BitmapRef<float, 1> &output, const Shape &shape, double 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) {
generatePseudoSDF(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) {
@ -145,7 +153,7 @@ void generateSDF_legacy(const BitmapRef<float, 1> &output, const Shape &shape, d
}
}
void generatePseudoSDF_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) {
#ifdef MSDFGEN_USE_OPENMP
#pragma omp parallel for
#endif
@ -167,12 +175,16 @@ void generatePseudoSDF_legacy(const BitmapRef<float, 1> &output, const Shape &sh
}
}
if (nearEdge)
(*nearEdge)->distanceToPseudoDistance(minDistance, p, nearParam);
(*nearEdge)->distanceToPerpendicularDistance(minDistance, p, nearParam);
*output(x, row) = float(minDistance.distance/range+.5);
}
}
}
void generatePseudoSDF_legacy(const BitmapRef<float, 1> &output, const Shape &shape, double 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) {
#ifdef MSDFGEN_USE_OPENMP
#pragma omp parallel for
@ -212,11 +224,11 @@ void generateMSDF_legacy(const BitmapRef<float, 3> &output, const Shape &shape,
}
if (r.nearEdge)
(*r.nearEdge)->distanceToPseudoDistance(r.minDistance, p, r.nearParam);
(*r.nearEdge)->distanceToPerpendicularDistance(r.minDistance, p, r.nearParam);
if (g.nearEdge)
(*g.nearEdge)->distanceToPseudoDistance(g.minDistance, p, g.nearParam);
(*g.nearEdge)->distanceToPerpendicularDistance(g.minDistance, p, g.nearParam);
if (b.nearEdge)
(*b.nearEdge)->distanceToPseudoDistance(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)[1] = float(g.minDistance.distance/range+.5);
output(x, row)[2] = float(b.minDistance.distance/range+.5);
@ -269,11 +281,11 @@ void generateMTSDF_legacy(const BitmapRef<float, 4> &output, const Shape &shape,
}
if (r.nearEdge)
(*r.nearEdge)->distanceToPseudoDistance(r.minDistance, p, r.nearParam);
(*r.nearEdge)->distanceToPerpendicularDistance(r.minDistance, p, r.nearParam);
if (g.nearEdge)
(*g.nearEdge)->distanceToPseudoDistance(g.minDistance, p, g.nearParam);
(*g.nearEdge)->distanceToPerpendicularDistance(g.minDistance, p, g.nearParam);
if (b.nearEdge)
(*b.nearEdge)->distanceToPseudoDistance(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)[1] = float(g.minDistance.distance/range+.5);
output(x, row)[2] = float(b.minDistance.distance/range+.5);

View File

@ -358,7 +358,7 @@ static const char *const helpText =
"\n"
"MODES\n"
" sdf - Generate conventional monochrome (true) signed distance field.\n"
" psdf - Generate monochrome signed pseudo-distance field.\n"
" psdf - Generate monochrome signed perpendicular distance field.\n"
" msdf - Generate multi-channel signed distance field. This is used by default if no mode is specified.\n"
" mtsdf - Generate combined multi-channel and true signed distance field in the alpha channel.\n"
" metrics - Report shape metrics only.\n"
@ -513,7 +513,7 @@ int main(int argc, const char *const *argv) {
} inputType = NONE;
enum {
SINGLE,
PSEUDO,
PERPENDICULAR,
MULTI,
MULTI_AND_TRUE,
METRICS
@ -573,7 +573,7 @@ int main(int argc, const char *const *argv) {
GUESS
} orientation = KEEP;
unsigned long long coloringSeed = 0;
void (*edgeColoring)(Shape &, double, unsigned long long) = edgeColoringSimple;
void (*edgeColoring)(Shape &, double, unsigned long long) = &edgeColoringSimple;
bool explicitErrorCorrectionMode = false;
int argPos = 1;
@ -589,7 +589,7 @@ int main(int argc, const char *const *argv) {
++arg;
ARG_MODE("sdf", SINGLE)
ARG_MODE("psdf", PSEUDO)
ARG_MODE("psdf", PERPENDICULAR)
ARG_MODE("msdf", MULTI)
ARG_MODE("mtsdf", MULTI_AND_TRUE)
ARG_MODE("metrics", METRICS)
@ -856,9 +856,12 @@ int main(int argc, const char *const *argv) {
continue;
}
ARG_CASE("-coloringstrategy", 1) {
if (!strcmp(argv[argPos+1], "simple")) edgeColoring = edgeColoringSimple;
else if (!strcmp(argv[argPos+1], "inktrap")) edgeColoring = edgeColoringInkTrap;
else if (!strcmp(argv[argPos+1], "distance")) edgeColoring = edgeColoringByDistance;
if (!strcmp(argv[argPos+1], "simple"))
edgeColoring = &edgeColoringSimple;
else if (!strcmp(argv[argPos+1], "inktrap"))
edgeColoring = &edgeColoringInkTrap;
else if (!strcmp(argv[argPos+1], "distance"))
edgeColoring = &edgeColoringByDistance;
else
fputs("Unknown coloring strategy specified.\n", stderr);
argPos += 2;
@ -1164,12 +1167,12 @@ int main(int argc, const char *const *argv) {
generateSDF(sdf, shape, projection, range, generatorConfig);
break;
}
case PSEUDO: {
case PERPENDICULAR: {
sdf = Bitmap<float, 1>(width, height);
if (legacyMode)
generatePseudoSDF_legacy(sdf, shape, range, scale, translate);
generatePSDF_legacy(sdf, shape, range, scale, translate);
else
generatePseudoSDF(sdf, shape, projection, range, generatorConfig);
generatePSDF(sdf, shape, projection, range, generatorConfig);
break;
}
case MULTI: {
@ -1208,7 +1211,7 @@ int main(int argc, const char *const *argv) {
if (orientation == REVERSE) {
switch (mode) {
case SINGLE:
case PSEUDO:
case PERPENDICULAR:
invertColor<1>(sdf);
break;
case MULTI:
@ -1223,7 +1226,7 @@ int main(int argc, const char *const *argv) {
if (scanlinePass) {
switch (mode) {
case SINGLE:
case PSEUDO:
case PERPENDICULAR:
distanceSignCorrection(sdf, shape, projection, fillRule);
break;
case MULTI:
@ -1241,7 +1244,7 @@ int main(int argc, const char *const *argv) {
float *pixel = NULL, *pixelsEnd = NULL;
switch (mode) {
case SINGLE:
case PSEUDO:
case PERPENDICULAR:
pixel = (float *) sdf;
pixelsEnd = pixel+1*sdf.width()*sdf.height();
break;
@ -1271,7 +1274,7 @@ int main(int argc, const char *const *argv) {
const char *error = NULL;
switch (mode) {
case SINGLE:
case PSEUDO:
case PERPENDICULAR:
if ((error = writeOutput<1>(sdf, output, format))) {
fprintf(stderr, "%s\n", error);
return 1;

View File

@ -40,8 +40,8 @@ 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());
/// Generates a single-channel signed pseudo-distance field.
void generatePseudoSDF(const BitmapRef<float, 1> &output, const Shape &shape, const Projection &projection, double range, const GeneratorConfig &config = GeneratorConfig());
/// 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());
/// 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());
@ -50,13 +50,17 @@ void generateMSDF(const BitmapRef<float, 3> &output, const Shape &shape, const P
void generateMTSDF(const BitmapRef<float, 4> &output, const Shape &shape, const Projection &projection, double range, 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, 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);
// 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());