mirror of https://github.com/Chlumsky/msdfgen.git
Renamed pseudo-distance to perpendicular distance
This commit is contained in:
parent
35f92541c4
commit
64c3e24829
11
README.md
11
README.md
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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>;
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 ¶m) 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.
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
31
main.cpp
31
main.cpp
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
|
|
|||
Loading…
Reference in New Issue