mirror of https://github.com/Chlumsky/msdfgen.git
Replaced double by configurable real type
This commit is contained in:
parent
3e8774abde
commit
901b099386
|
|
@ -2,11 +2,10 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include "types.h"
|
||||
|
||||
namespace msdfgen {
|
||||
|
||||
typedef unsigned char byte;
|
||||
|
||||
/// Reference to a 2D image bitmap or a buffer acting as one. Pixel storage not owned or managed by the object.
|
||||
template <typename T, int N = 1>
|
||||
struct BitmapRef {
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
namespace msdfgen {
|
||||
|
||||
static double shoelace(const Point2 &a, const Point2 &b) {
|
||||
static real shoelace(const Point2 &a, const Point2 &b) {
|
||||
return (b.x-a.x)*(a.y+b.y);
|
||||
}
|
||||
|
||||
|
|
@ -24,29 +24,29 @@ EdgeHolder &Contour::addEdge() {
|
|||
return edges.back();
|
||||
}
|
||||
|
||||
static void boundPoint(double &l, double &b, double &r, double &t, Point2 p) {
|
||||
static void boundPoint(real &l, real &b, real &r, real &t, Point2 p) {
|
||||
if (p.x < l) l = p.x;
|
||||
if (p.y < b) b = p.y;
|
||||
if (p.x > r) r = p.x;
|
||||
if (p.y > t) t = p.y;
|
||||
}
|
||||
|
||||
void Contour::bound(double &l, double &b, double &r, double &t) const {
|
||||
void Contour::bound(real &l, real &b, real &r, real &t) const {
|
||||
for (std::vector<EdgeHolder>::const_iterator edge = edges.begin(); edge != edges.end(); ++edge)
|
||||
(*edge)->bound(l, b, r, t);
|
||||
}
|
||||
|
||||
void Contour::boundMiters(double &l, double &b, double &r, double &t, double border, double miterLimit, int polarity) const {
|
||||
void Contour::boundMiters(real &l, real &b, real &r, real &t, real border, real miterLimit, int polarity) const {
|
||||
if (edges.empty())
|
||||
return;
|
||||
Vector2 prevDir = edges.back()->direction(1).normalize(true);
|
||||
for (std::vector<EdgeHolder>::const_iterator edge = edges.begin(); edge != edges.end(); ++edge) {
|
||||
Vector2 dir = -(*edge)->direction(0).normalize(true);
|
||||
if (polarity*crossProduct(prevDir, dir) >= 0) {
|
||||
double miterLength = miterLimit;
|
||||
double q = .5*(1-dotProduct(prevDir, dir));
|
||||
if (q > 0)
|
||||
miterLength = min(1/sqrt(q), miterLimit);
|
||||
real miterLength = miterLimit;
|
||||
real q = real(.5)*(real(1)-dotProduct(prevDir, dir));
|
||||
if (q > real(0))
|
||||
miterLength = min(real(1)/real(sqrt(q)), miterLimit);
|
||||
Point2 miter = (*edge)->point(0)+border*miterLength*(prevDir+dir).normalize(true);
|
||||
boundPoint(l, b, r, t, miter);
|
||||
}
|
||||
|
|
@ -59,12 +59,12 @@ int Contour::winding() const {
|
|||
return 0;
|
||||
double total = 0;
|
||||
if (edges.size() == 1) {
|
||||
Point2 a = edges[0]->point(0), b = edges[0]->point(1/3.), c = edges[0]->point(2/3.);
|
||||
Point2 a = edges[0]->point(0), b = edges[0]->point(real(1)/real(3)), c = edges[0]->point(real(2)/real(3));
|
||||
total += shoelace(a, b);
|
||||
total += shoelace(b, c);
|
||||
total += shoelace(c, a);
|
||||
} else if (edges.size() == 2) {
|
||||
Point2 a = edges[0]->point(0), b = edges[0]->point(.5), c = edges[1]->point(0), d = edges[1]->point(.5);
|
||||
Point2 a = edges[0]->point(0), b = edges[0]->point(real(.5)), c = edges[1]->point(0), d = edges[1]->point(real(.5));
|
||||
total += shoelace(a, b);
|
||||
total += shoelace(b, c);
|
||||
total += shoelace(c, d);
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include "types.h"
|
||||
#include "EdgeHolder.h"
|
||||
|
||||
namespace msdfgen {
|
||||
|
|
@ -21,9 +22,9 @@ public:
|
|||
/// Creates a new edge in the contour and returns its reference.
|
||||
EdgeHolder &addEdge();
|
||||
/// Adjusts the bounding box to fit the contour.
|
||||
void bound(double &l, double &b, double &r, double &t) const;
|
||||
void bound(real &l, real &b, real &r, real &t) const;
|
||||
/// Adjusts the bounding box to fit the contour border's mitered corners.
|
||||
void boundMiters(double &l, double &b, double &r, double &t, double border, double miterLimit, int polarity) const;
|
||||
void boundMiters(real &l, real &b, real &r, real &t, real border, real miterLimit, int polarity) const;
|
||||
/// Computes the winding of the contour. Returns 1 if positive, -1 if negative.
|
||||
int winding() const;
|
||||
/// Reverses the sequence of edges on the contour.
|
||||
|
|
|
|||
|
|
@ -13,24 +13,24 @@
|
|||
|
||||
namespace msdfgen {
|
||||
|
||||
#define ARTIFACT_T_EPSILON .01
|
||||
#define PROTECTION_RADIUS_TOLERANCE 1.001
|
||||
#define ARTIFACT_T_EPSILON ::msdfgen::real(.01)
|
||||
#define PROTECTION_RADIUS_TOLERANCE ::msdfgen::real(1.001)
|
||||
|
||||
#define CLASSIFIER_FLAG_CANDIDATE 0x01
|
||||
#define CLASSIFIER_FLAG_ARTIFACT 0x02
|
||||
|
||||
MSDFGEN_PUBLIC const double ErrorCorrectionConfig::defaultMinDeviationRatio = 1.11111111111111111;
|
||||
MSDFGEN_PUBLIC const double ErrorCorrectionConfig::defaultMinImproveRatio = 1.11111111111111111;
|
||||
MSDFGEN_PUBLIC const real ErrorCorrectionConfig::defaultMinDeviationRatio = 1.11111111111111111;
|
||||
MSDFGEN_PUBLIC const real ErrorCorrectionConfig::defaultMinImproveRatio = 1.11111111111111111;
|
||||
|
||||
/// The base artifact classifier recognizes artifacts based on the contents of the SDF alone.
|
||||
class BaseArtifactClassifier {
|
||||
public:
|
||||
inline BaseArtifactClassifier(double span, bool protectedFlag) : span(span), protectedFlag(protectedFlag) { }
|
||||
inline BaseArtifactClassifier(real span, bool protectedFlag) : span(span), protectedFlag(protectedFlag) { }
|
||||
/// Evaluates if the median value xm interpolated at xt in the range between am at at and bm at bt indicates an artifact.
|
||||
inline int rangeTest(double at, double bt, double xt, float am, float bm, float xm) const {
|
||||
inline int rangeTest(real at, real bt, real xt, float am, float bm, float xm) const {
|
||||
// For protected texels, only consider inversion artifacts (interpolated median has different sign than boundaries). For the rest, it is sufficient that the interpolated median is outside its boundaries.
|
||||
if ((am > .5f && bm > .5f && xm <= .5f) || (am < .5f && bm < .5f && xm >= .5f) || (!protectedFlag && median(am, bm, xm) != xm)) {
|
||||
double axSpan = (xt-at)*span, bxSpan = (bt-xt)*span;
|
||||
real axSpan = (xt-at)*span, bxSpan = (bt-xt)*span;
|
||||
// Check if the interpolated median's value is in the expected range based on its distance (span) from boundaries a, b.
|
||||
if (!(xm >= am-axSpan && xm <= am+axSpan && xm >= bm-bxSpan && xm <= bm+bxSpan))
|
||||
return CLASSIFIER_FLAG_CANDIDATE|CLASSIFIER_FLAG_ARTIFACT;
|
||||
|
|
@ -39,11 +39,11 @@ public:
|
|||
return 0;
|
||||
}
|
||||
/// Returns true if the combined results of the tests performed on the median value m interpolated at t indicate an artifact.
|
||||
inline bool evaluate(double t, float m, int flags) const {
|
||||
return (flags&2) != 0;
|
||||
inline bool evaluate(real t, float m, int flags) const {
|
||||
return (flags&CLASSIFIER_FLAG_ARTIFACT) != 0;
|
||||
}
|
||||
private:
|
||||
double span;
|
||||
real span;
|
||||
bool protectedFlag;
|
||||
};
|
||||
|
||||
|
|
@ -53,9 +53,9 @@ class ShapeDistanceChecker {
|
|||
public:
|
||||
class ArtifactClassifier : public BaseArtifactClassifier {
|
||||
public:
|
||||
inline ArtifactClassifier(ShapeDistanceChecker *parent, const Vector2 &direction, double span) : BaseArtifactClassifier(span, parent->protectedFlag), parent(parent), direction(direction) { }
|
||||
inline ArtifactClassifier(ShapeDistanceChecker *parent, const Vector2 &direction, real span) : BaseArtifactClassifier(span, parent->protectedFlag), parent(parent), direction(direction) { }
|
||||
/// Returns true if the combined results of the tests performed on the median value m interpolated at t indicate an artifact.
|
||||
inline bool evaluate(double t, float m, int flags) const {
|
||||
inline bool evaluate(real t, float m, int flags) const {
|
||||
if (flags&CLASSIFIER_FLAG_CANDIDATE) {
|
||||
// Skip expensive distance evaluation if the point has already been classified as an artifact by the base classifier.
|
||||
if (flags&CLASSIFIER_FLAG_ARTIFACT)
|
||||
|
|
@ -66,7 +66,7 @@ public:
|
|||
Point2 sdfCoord = parent->sdfCoord+tVector;
|
||||
interpolate(oldMSD, parent->sdf, sdfCoord);
|
||||
// Compute the color that would be interpolated at the artifact candidate's position if error correction was applied on the current texel.
|
||||
double aWeight = (1-fabs(tVector.x))*(1-fabs(tVector.y));
|
||||
real aWeight = (real(1)-fabs(tVector.x))*(real(1)-fabs(tVector.y));
|
||||
float aPSD = median(parent->msd[0], parent->msd[1], parent->msd[2]);
|
||||
newMSD[0] = float(oldMSD[0]+aWeight*(aPSD-parent->msd[0]));
|
||||
newMSD[1] = float(oldMSD[1]+aWeight*(aPSD-parent->msd[1]));
|
||||
|
|
@ -74,9 +74,9 @@ 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->invRange*parent->distanceFinder.distance(parent->shapeCoord+tVector*parent->texelSize)+real(.5));
|
||||
// Compare the differences of the exact distance and the before and after distances.
|
||||
return parent->minImproveRatio*fabsf(newPSD-refPSD) < double(fabsf(oldPSD-refPSD));
|
||||
return parent->minImproveRatio*fabsf(newPSD-refPSD) < real(fabsf(oldPSD-refPSD));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
@ -87,34 +87,34 @@ 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, real invRange, real minImproveRatio) : distanceFinder(shape), sdf(sdf), invRange(invRange), minImproveRatio(minImproveRatio) {
|
||||
texelSize = projection.unprojectVector(Vector2(1));
|
||||
}
|
||||
inline ArtifactClassifier classifier(const Vector2 &direction, double span) {
|
||||
inline ArtifactClassifier classifier(const Vector2 &direction, real span) {
|
||||
return ArtifactClassifier(this, direction, span);
|
||||
}
|
||||
private:
|
||||
ShapeDistanceFinder<ContourCombiner<PseudoDistanceSelector> > distanceFinder;
|
||||
BitmapConstRef<float, N> sdf;
|
||||
double invRange;
|
||||
real invRange;
|
||||
Vector2 texelSize;
|
||||
double minImproveRatio;
|
||||
real 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 Projection &projection, real range) : stencil(stencil), projection(projection) {
|
||||
invRange = real(1)/range;
|
||||
minDeviationRatio = ErrorCorrectionConfig::defaultMinDeviationRatio;
|
||||
minImproveRatio = ErrorCorrectionConfig::defaultMinImproveRatio;
|
||||
memset(stencil.pixels, 0, sizeof(byte)*stencil.width*stencil.height);
|
||||
}
|
||||
|
||||
void MSDFErrorCorrection::setMinDeviationRatio(double minDeviationRatio) {
|
||||
void MSDFErrorCorrection::setMinDeviationRatio(real minDeviationRatio) {
|
||||
this->minDeviationRatio = minDeviationRatio;
|
||||
}
|
||||
|
||||
void MSDFErrorCorrection::setMinImproveRatio(double minImproveRatio) {
|
||||
void MSDFErrorCorrection::setMinImproveRatio(real minImproveRatio) {
|
||||
this->minImproveRatio = minImproveRatio;
|
||||
}
|
||||
|
||||
|
|
@ -130,8 +130,8 @@ void MSDFErrorCorrection::protectCorners(const Shape &shape) {
|
|||
Point2 p = projection.project((*edge)->point(0));
|
||||
if (shape.inverseYAxis)
|
||||
p.y = stencil.height-p.y;
|
||||
int l = (int) floor(p.x-.5);
|
||||
int b = (int) floor(p.y-.5);
|
||||
int l = (int) floor(p.x-real(.5));
|
||||
int b = (int) floor(p.y-real(.5));
|
||||
int r = l+1;
|
||||
int t = b+1;
|
||||
// Check that the positions are within bounds.
|
||||
|
|
@ -154,7 +154,7 @@ void MSDFErrorCorrection::protectCorners(const Shape &shape) {
|
|||
/// Determines if the channel contributes to an edge between the two texels a, b.
|
||||
static bool edgeBetweenTexelsChannel(const float *a, const float *b, int channel) {
|
||||
// Find interpolation ratio t (0 < t < 1) where an edge is expected (mix(a[channel], b[channel], t) == 0.5).
|
||||
double t = (a[channel]-.5)/(a[channel]-b[channel]);
|
||||
real t = (a[channel]-real(.5))/(a[channel]-b[channel]);
|
||||
if (t > 0 && t < 1) {
|
||||
// Interpolate channel values at t.
|
||||
float c[3] = {
|
||||
|
|
@ -256,7 +256,7 @@ void MSDFErrorCorrection::protectAll() {
|
|||
}
|
||||
|
||||
/// Returns the median of the linear interpolation of texels a, b at t.
|
||||
static float interpolatedMedian(const float *a, const float *b, double t) {
|
||||
static float interpolatedMedian(const float *a, const float *b, real t) {
|
||||
return median(
|
||||
mix(a[0], b[0], t),
|
||||
mix(a[1], b[1], t),
|
||||
|
|
@ -264,7 +264,7 @@ static float interpolatedMedian(const float *a, const float *b, double t) {
|
|||
);
|
||||
}
|
||||
/// Returns the median of the bilinear interpolation with the given constant, linear, and quadratic terms at t.
|
||||
static float interpolatedMedian(const float *a, const float *l, const float *q, double t) {
|
||||
static float interpolatedMedian(const float *a, const float *l, const float *q, real t) {
|
||||
return float(median(
|
||||
t*(t*q[0]+l[0])+a[0],
|
||||
t*(t*q[1]+l[1])+a[1],
|
||||
|
|
@ -273,7 +273,7 @@ static float interpolatedMedian(const float *a, const float *l, const float *q,
|
|||
}
|
||||
|
||||
/// Determines if the interpolated median xm is an artifact.
|
||||
static bool isArtifact(bool isProtected, double axSpan, double bxSpan, float am, float bm, float xm) {
|
||||
static bool isArtifact(bool isProtected, real axSpan, real bxSpan, float am, float bm, float xm) {
|
||||
return (
|
||||
// For protected texels, only report an artifact if it would cause fill inversion (change between positive and negative distance).
|
||||
(!isProtected || (am > .5f && bm > .5f && xm <= .5f) || (am < .5f && bm < .5f && xm >= .5f)) &&
|
||||
|
|
@ -286,8 +286,8 @@ static bool isArtifact(bool isProtected, double axSpan, double bxSpan, float am,
|
|||
template <class ArtifactClassifier>
|
||||
static bool hasLinearArtifactInner(const ArtifactClassifier &artifactClassifier, float am, float bm, const float *a, const float *b, float dA, float dB) {
|
||||
// Find interpolation ratio t (0 < t < 1) where two color channels are equal (mix(dA, dB, t) == 0).
|
||||
double t = (double) dA/(dA-dB);
|
||||
if (t > ARTIFACT_T_EPSILON && t < 1-ARTIFACT_T_EPSILON) {
|
||||
real t = (real) dA/(dA-dB);
|
||||
if (t > ARTIFACT_T_EPSILON && t < real(1)-ARTIFACT_T_EPSILON) {
|
||||
// Interpolate median at t and let the classifier decide if its value indicates an artifact.
|
||||
float xm = interpolatedMedian(a, b, t);
|
||||
return artifactClassifier.evaluate(t, xm, artifactClassifier.rangeTest(0, 1, t, am, bm, xm));
|
||||
|
|
@ -297,22 +297,22 @@ static bool hasLinearArtifactInner(const ArtifactClassifier &artifactClassifier,
|
|||
|
||||
/// Checks if a bilinear interpolation artifact will occur at a point where two specific color channels are equal - such points have extreme median values.
|
||||
template <class ArtifactClassifier>
|
||||
static bool hasDiagonalArtifactInner(const ArtifactClassifier &artifactClassifier, float am, float dm, const float *a, const float *l, const float *q, float dA, float dBC, float dD, double tEx0, double tEx1) {
|
||||
static bool hasDiagonalArtifactInner(const ArtifactClassifier &artifactClassifier, float am, float dm, const float *a, const float *l, const float *q, float dA, float dBC, float dD, real tEx0, real tEx1) {
|
||||
// Find interpolation ratios t (0 < t[i] < 1) where two color channels are equal.
|
||||
double t[2];
|
||||
real t[2];
|
||||
int solutions = solveQuadratic(t, dD-dBC+dA, dBC-dA-dA, dA);
|
||||
for (int i = 0; i < solutions; ++i) {
|
||||
// Solutions t[i] == 0 and t[i] == 1 are singularities and occur very often because two channels are usually equal at texels.
|
||||
if (t[i] > ARTIFACT_T_EPSILON && t[i] < 1-ARTIFACT_T_EPSILON) {
|
||||
if (t[i] > ARTIFACT_T_EPSILON && t[i] < real(1)-ARTIFACT_T_EPSILON) {
|
||||
// Interpolate median xm at t.
|
||||
float xm = interpolatedMedian(a, l, q, t[i]);
|
||||
// Determine if xm deviates too much from medians of a, d.
|
||||
int rangeFlags = artifactClassifier.rangeTest(0, 1, t[i], am, dm, xm);
|
||||
// Additionally, check xm against the interpolated medians at the local extremes tEx0, tEx1.
|
||||
double tEnd[2];
|
||||
real tEnd[2];
|
||||
float em[2];
|
||||
// tEx0
|
||||
if (tEx0 > 0 && tEx0 < 1) {
|
||||
if (tEx0 > real(0) && tEx0 < real(1)) {
|
||||
tEnd[0] = 0, tEnd[1] = 1;
|
||||
em[0] = am, em[1] = dm;
|
||||
tEnd[tEx0 > t[i]] = tEx0;
|
||||
|
|
@ -320,7 +320,7 @@ static bool hasDiagonalArtifactInner(const ArtifactClassifier &artifactClassifie
|
|||
rangeFlags |= artifactClassifier.rangeTest(tEnd[0], tEnd[1], t[i], em[0], em[1], xm);
|
||||
}
|
||||
// tEx1
|
||||
if (tEx1 > 0 && tEx1 < 1) {
|
||||
if (tEx1 > real(0) && tEx1 < real(1)) {
|
||||
tEnd[0] = 0, tEnd[1] = 1;
|
||||
em[0] = am, em[1] = dm;
|
||||
tEnd[tEx1 > t[i]] = tEx1;
|
||||
|
|
@ -373,10 +373,10 @@ static bool hasDiagonalArtifact(const ArtifactClassifier &artifactClassifier, fl
|
|||
d[2]+abc[2]
|
||||
};
|
||||
// Compute interpolation ratios tEx (0 < tEx[i] < 1) for the local extremes of each color channel (the derivative 2*q[i]*tEx[i]+l[i] == 0).
|
||||
double tEx[3] = {
|
||||
-.5*l[0]/q[0],
|
||||
-.5*l[1]/q[1],
|
||||
-.5*l[2]/q[2]
|
||||
real tEx[3] = {
|
||||
real(-.5)*l[0]/q[0],
|
||||
real(-.5)*l[1]/q[1],
|
||||
real(-.5)*l[2]/q[2]
|
||||
};
|
||||
// Check points where each pair of color channels meets.
|
||||
return (
|
||||
|
|
@ -391,9 +391,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();
|
||||
real hSpan = minDeviationRatio*projection.unprojectVector(Vector2(invRange, 0)).length();
|
||||
real vSpan = minDeviationRatio*projection.unprojectVector(Vector2(0, invRange)).length();
|
||||
real dSpan = minDeviationRatio*projection.unprojectVector(Vector2(invRange)).length();
|
||||
// Inspect all texels.
|
||||
for (int y = 0; y < sdf.height; ++y) {
|
||||
for (int x = 0; x < sdf.width; ++x) {
|
||||
|
|
@ -419,9 +419,9 @@ 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();
|
||||
real hSpan = minDeviationRatio*projection.unprojectVector(Vector2(invRange, 0)).length();
|
||||
real vSpan = minDeviationRatio*projection.unprojectVector(Vector2(0, invRange)).length();
|
||||
real dSpan = minDeviationRatio*projection.unprojectVector(Vector2(invRange)).length();
|
||||
#ifdef MSDFGEN_USE_OPENMP
|
||||
#pragma omp parallel
|
||||
#endif
|
||||
|
|
@ -439,8 +439,8 @@ 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.sdfCoord = Point2(x+.5, row+.5);
|
||||
shapeDistanceChecker.shapeCoord = projection.unproject(Point2(real(x)+real(.5), real(y)+real(.5)));
|
||||
shapeDistanceChecker.sdfCoord = Point2(real(x)+real(.5), real(row)+real(.5));
|
||||
shapeDistanceChecker.msd = c;
|
||||
shapeDistanceChecker.protectedFlag = (*stencil(x, row)&PROTECTED) != 0;
|
||||
float cm = median(c[0], c[1], c[2]);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
#include "Projection.h"
|
||||
#include "Shape.h"
|
||||
#include "BitmapRef.hpp"
|
||||
|
|
@ -20,11 +21,11 @@ public:
|
|||
};
|
||||
|
||||
MSDFErrorCorrection();
|
||||
explicit MSDFErrorCorrection(const BitmapRef<byte, 1> &stencil, const Projection &projection, double range);
|
||||
explicit MSDFErrorCorrection(const BitmapRef<byte, 1> &stencil, const Projection &projection, real range);
|
||||
/// Sets the minimum ratio between the actual and maximum expected distance delta to be considered an error.
|
||||
void setMinDeviationRatio(double minDeviationRatio);
|
||||
void setMinDeviationRatio(real minDeviationRatio);
|
||||
/// Sets the minimum ratio between the pre-correction distance error and the post-correction distance error.
|
||||
void setMinImproveRatio(double minImproveRatio);
|
||||
void setMinImproveRatio(real minImproveRatio);
|
||||
/// Flags all texels that are interpolated at corners as protected.
|
||||
void protectCorners(const Shape &shape);
|
||||
/// Flags all texels that contribute to edges as protected.
|
||||
|
|
@ -47,9 +48,9 @@ public:
|
|||
private:
|
||||
BitmapRef<byte, 1> stencil;
|
||||
Projection projection;
|
||||
double invRange;
|
||||
double minDeviationRatio;
|
||||
double minImproveRatio;
|
||||
real invRange;
|
||||
real minDeviationRatio;
|
||||
real minImproveRatio;
|
||||
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -23,19 +23,19 @@ Vector2 Projection::unprojectVector(const Vector2 &vector) const {
|
|||
return vector/scale;
|
||||
}
|
||||
|
||||
double Projection::projectX(double x) const {
|
||||
real Projection::projectX(real x) const {
|
||||
return scale.x*(x+translate.x);
|
||||
}
|
||||
|
||||
double Projection::projectY(double y) const {
|
||||
real Projection::projectY(real y) const {
|
||||
return scale.y*(y+translate.y);
|
||||
}
|
||||
|
||||
double Projection::unprojectX(double x) const {
|
||||
real Projection::unprojectX(real x) const {
|
||||
return x/scale.x-translate.x;
|
||||
}
|
||||
|
||||
double Projection::unprojectY(double y) const {
|
||||
real Projection::unprojectY(real y) const {
|
||||
return y/scale.y-translate.y;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
#include "Vector2.hpp"
|
||||
|
||||
namespace msdfgen {
|
||||
|
|
@ -20,13 +21,13 @@ public:
|
|||
/// Converts the vector from pixel coordinate space.
|
||||
Vector2 unprojectVector(const Vector2 &vector) const;
|
||||
/// Converts the X-coordinate from shape to pixel coordinate space.
|
||||
double projectX(double x) const;
|
||||
real projectX(real x) const;
|
||||
/// Converts the Y-coordinate from shape to pixel coordinate space.
|
||||
double projectY(double y) const;
|
||||
real projectY(real y) const;
|
||||
/// Converts the X-coordinate from pixel to shape coordinate space.
|
||||
double unprojectX(double x) const;
|
||||
real unprojectX(real x) const;
|
||||
/// Converts the Y-coordinate from pixel to shape coordinate space.
|
||||
double unprojectY(double y) const;
|
||||
real unprojectY(real y) const;
|
||||
|
||||
private:
|
||||
Vector2 scale;
|
||||
|
|
|
|||
|
|
@ -24,14 +24,14 @@ bool interpretFillRule(int intersections, FillRule fillRule) {
|
|||
return false;
|
||||
}
|
||||
|
||||
double Scanline::overlap(const Scanline &a, const Scanline &b, double xFrom, double xTo, FillRule fillRule) {
|
||||
double total = 0;
|
||||
real Scanline::overlap(const Scanline &a, const Scanline &b, real xFrom, real xTo, FillRule fillRule) {
|
||||
real total = 0;
|
||||
bool aInside = false, bInside = false;
|
||||
int ai = 0, bi = 0;
|
||||
double ax = !a.intersections.empty() ? a.intersections[ai].x : xTo;
|
||||
double bx = !b.intersections.empty() ? b.intersections[bi].x : xTo;
|
||||
real ax = !a.intersections.empty() ? a.intersections[ai].x : xTo;
|
||||
real bx = !b.intersections.empty() ? b.intersections[bi].x : xTo;
|
||||
while (ax < xFrom || bx < xFrom) {
|
||||
double xNext = min(ax, bx);
|
||||
real xNext = min(ax, bx);
|
||||
if (ax == xNext && ai < (int) a.intersections.size()) {
|
||||
aInside = interpretFillRule(a.intersections[ai].direction, fillRule);
|
||||
ax = ++ai < (int) a.intersections.size() ? a.intersections[ai].x : xTo;
|
||||
|
|
@ -41,9 +41,9 @@ double Scanline::overlap(const Scanline &a, const Scanline &b, double xFrom, dou
|
|||
bx = ++bi < (int) b.intersections.size() ? b.intersections[bi].x : xTo;
|
||||
}
|
||||
}
|
||||
double x = xFrom;
|
||||
real x = xFrom;
|
||||
while (ax < xTo || bx < xTo) {
|
||||
double xNext = min(ax, bx);
|
||||
real xNext = min(ax, bx);
|
||||
if (aInside == bInside)
|
||||
total += xNext-x;
|
||||
if (ax == xNext && ai < (int) a.intersections.size()) {
|
||||
|
|
@ -87,7 +87,7 @@ void Scanline::setIntersections(std::vector<Intersection> &&intersections) {
|
|||
}
|
||||
#endif
|
||||
|
||||
int Scanline::moveTo(double x) const {
|
||||
int Scanline::moveTo(real x) const {
|
||||
if (intersections.empty())
|
||||
return -1;
|
||||
int index = lastIndex;
|
||||
|
|
@ -107,18 +107,18 @@ int Scanline::moveTo(double x) const {
|
|||
return index;
|
||||
}
|
||||
|
||||
int Scanline::countIntersections(double x) const {
|
||||
int Scanline::countIntersections(real x) const {
|
||||
return moveTo(x)+1;
|
||||
}
|
||||
|
||||
int Scanline::sumIntersections(double x) const {
|
||||
int Scanline::sumIntersections(real x) const {
|
||||
int index = moveTo(x);
|
||||
if (index >= 0)
|
||||
return intersections[index].direction;
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool Scanline::filled(double x, FillRule fillRule) const {
|
||||
bool Scanline::filled(real x, FillRule fillRule) const {
|
||||
return interpretFillRule(sumIntersections(x), fillRule);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include "types.h"
|
||||
|
||||
namespace msdfgen {
|
||||
|
||||
|
|
@ -23,12 +24,12 @@ public:
|
|||
/// An intersection with the scanline.
|
||||
struct Intersection {
|
||||
/// X coordinate.
|
||||
double x;
|
||||
real x;
|
||||
/// Normalized Y direction of the oriented edge at the point of intersection.
|
||||
int direction;
|
||||
};
|
||||
|
||||
static double overlap(const Scanline &a, const Scanline &b, double xFrom, double xTo, FillRule fillRule);
|
||||
static real overlap(const Scanline &a, const Scanline &b, real xFrom, real xTo, FillRule fillRule);
|
||||
|
||||
Scanline();
|
||||
/// Populates the intersection list.
|
||||
|
|
@ -37,18 +38,18 @@ public:
|
|||
void setIntersections(std::vector<Intersection> &&intersections);
|
||||
#endif
|
||||
/// Returns the number of intersections left of x.
|
||||
int countIntersections(double x) const;
|
||||
int countIntersections(real x) const;
|
||||
/// Returns the total sign of intersections left of x.
|
||||
int sumIntersections(double x) const;
|
||||
int sumIntersections(real x) const;
|
||||
/// Decides whether the scanline is filled at x based on fill rule.
|
||||
bool filled(double x, FillRule fillRule) const;
|
||||
bool filled(real x, FillRule fillRule) const;
|
||||
|
||||
private:
|
||||
std::vector<Intersection> intersections;
|
||||
mutable int lastIndex;
|
||||
|
||||
void preprocess();
|
||||
int moveTo(double x) const;
|
||||
int moveTo(real x) const;
|
||||
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
#include "Shape.h"
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cfloat>
|
||||
#include "arithmetics.hpp"
|
||||
|
||||
namespace msdfgen {
|
||||
|
|
@ -63,7 +64,7 @@ void Shape::normalize() {
|
|||
for (std::vector<EdgeHolder>::iterator edge = contour->edges.begin(); edge != contour->edges.end(); ++edge) {
|
||||
Vector2 prevDir = (*prevEdge)->direction(1).normalize();
|
||||
Vector2 curDir = (*edge)->direction(0).normalize();
|
||||
if (dotProduct(prevDir, curDir) < MSDFGEN_CORNER_DOT_EPSILON-1) {
|
||||
if (dotProduct(prevDir, curDir) < MSDFGEN_CORNER_DOT_EPSILON-real(1)) {
|
||||
deconvergeEdge(*prevEdge, 1);
|
||||
deconvergeEdge(*edge, 0);
|
||||
}
|
||||
|
|
@ -73,32 +74,31 @@ void Shape::normalize() {
|
|||
}
|
||||
}
|
||||
|
||||
void Shape::bound(double &l, double &b, double &r, double &t) const {
|
||||
void Shape::bound(real &l, real &b, real &r, real &t) const {
|
||||
for (std::vector<Contour>::const_iterator contour = contours.begin(); contour != contours.end(); ++contour)
|
||||
contour->bound(l, b, r, t);
|
||||
}
|
||||
|
||||
void Shape::boundMiters(double &l, double &b, double &r, double &t, double border, double miterLimit, int polarity) const {
|
||||
void Shape::boundMiters(real &l, real &b, real &r, real &t, real border, real miterLimit, int polarity) const {
|
||||
for (std::vector<Contour>::const_iterator contour = contours.begin(); contour != contours.end(); ++contour)
|
||||
contour->boundMiters(l, b, r, t, border, miterLimit, polarity);
|
||||
}
|
||||
|
||||
Shape::Bounds Shape::getBounds(double border, double miterLimit, int polarity) const {
|
||||
static const double LARGE_VALUE = 1e240;
|
||||
Shape::Bounds bounds = { +LARGE_VALUE, +LARGE_VALUE, -LARGE_VALUE, -LARGE_VALUE };
|
||||
Shape::Bounds Shape::getBounds(real border, real miterLimit, int polarity) const {
|
||||
Shape::Bounds bounds = { +FLT_MAX, +FLT_MAX, -FLT_MAX, -FLT_MAX };
|
||||
bound(bounds.l, bounds.b, bounds.r, bounds.t);
|
||||
if (border > 0) {
|
||||
if (border > real(0)) {
|
||||
bounds.l -= border, bounds.b -= border;
|
||||
bounds.r += border, bounds.t += border;
|
||||
if (miterLimit > 0)
|
||||
if (miterLimit > real(0))
|
||||
boundMiters(bounds.l, bounds.b, bounds.r, bounds.t, border, miterLimit, polarity);
|
||||
}
|
||||
return bounds;
|
||||
}
|
||||
|
||||
void Shape::scanline(Scanline &line, double y) const {
|
||||
void Shape::scanline(Scanline &line, real y) const {
|
||||
std::vector<Scanline::Intersection> intersections;
|
||||
double x[3];
|
||||
real x[3];
|
||||
int dy[3];
|
||||
for (std::vector<Contour>::const_iterator contour = contours.begin(); contour != contours.end(); ++contour) {
|
||||
for (std::vector<EdgeHolder>::const_iterator edge = contour->edges.begin(); edge != contour->edges.end(); ++edge) {
|
||||
|
|
@ -125,7 +125,7 @@ int Shape::edgeCount() const {
|
|||
|
||||
void Shape::orientContours() {
|
||||
struct Intersection {
|
||||
double x;
|
||||
real x;
|
||||
int direction;
|
||||
int contourIndex;
|
||||
|
||||
|
|
@ -134,21 +134,21 @@ void Shape::orientContours() {
|
|||
}
|
||||
};
|
||||
|
||||
const double ratio = .5*(sqrt(5)-1); // an irrational number to minimize chance of intersecting a corner or other point of interest
|
||||
const real ratio = real(.5)*(sqrt(5)-real(1)); // an irrational number to minimize chance of intersecting a corner or other point of interest
|
||||
std::vector<int> orientations(contours.size());
|
||||
std::vector<Intersection> intersections;
|
||||
for (int i = 0; i < (int) contours.size(); ++i) {
|
||||
if (!orientations[i] && !contours[i].edges.empty()) {
|
||||
// Find an Y that crosses the contour
|
||||
double y0 = contours[i].edges.front()->point(0).y;
|
||||
double y1 = y0;
|
||||
real y0 = contours[i].edges.front()->point(0).y;
|
||||
real y1 = y0;
|
||||
for (std::vector<EdgeHolder>::const_iterator edge = contours[i].edges.begin(); edge != contours[i].edges.end() && y0 == y1; ++edge)
|
||||
y1 = (*edge)->point(1).y;
|
||||
for (std::vector<EdgeHolder>::const_iterator edge = contours[i].edges.begin(); edge != contours[i].edges.end() && y0 == y1; ++edge)
|
||||
y1 = (*edge)->point(ratio).y; // in case all endpoints are in a horizontal line
|
||||
double y = mix(y0, y1, ratio);
|
||||
real y = mix(y0, y1, ratio);
|
||||
// Scanline through whole shape at Y
|
||||
double x[3];
|
||||
real x[3];
|
||||
int dy[3];
|
||||
for (int j = 0; j < (int) contours.size(); ++j) {
|
||||
for (std::vector<EdgeHolder>::const_iterator edge = contours[j].edges.begin(); edge != contours[j].edges.end(); ++edge) {
|
||||
|
|
|
|||
15
core/Shape.h
15
core/Shape.h
|
|
@ -2,22 +2,23 @@
|
|||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include "types.h"
|
||||
#include "Contour.h"
|
||||
#include "Scanline.h"
|
||||
|
||||
namespace msdfgen {
|
||||
|
||||
// Threshold of the dot product of adjacent edge directions to be considered convergent.
|
||||
#define MSDFGEN_CORNER_DOT_EPSILON .000001
|
||||
#define MSDFGEN_CORNER_DOT_EPSILON ::msdfgen::real(.000001)
|
||||
// The proportional amount by which a curve's control point will be adjusted to eliminate convergent corners.
|
||||
#define MSDFGEN_DECONVERGENCE_FACTOR .000001
|
||||
#define MSDFGEN_DECONVERGENCE_FACTOR ::msdfgen::real(.000001)
|
||||
|
||||
/// Vector shape representation.
|
||||
class Shape {
|
||||
|
||||
public:
|
||||
struct Bounds {
|
||||
double l, b, r, t;
|
||||
real l, b, r, t;
|
||||
};
|
||||
|
||||
/// The list of contours the shape consists of.
|
||||
|
|
@ -38,13 +39,13 @@ public:
|
|||
/// Performs basic checks to determine if the object represents a valid shape.
|
||||
bool validate() const;
|
||||
/// Adjusts the bounding box to fit the shape.
|
||||
void bound(double &l, double &b, double &r, double &t) const;
|
||||
void bound(real &l, real &b, real &r, real &t) const;
|
||||
/// Adjusts the bounding box to fit the shape border's mitered corners.
|
||||
void boundMiters(double &l, double &b, double &r, double &t, double border, double miterLimit, int polarity) const;
|
||||
void boundMiters(real &l, real &b, real &r, real &t, real border, real miterLimit, int polarity) const;
|
||||
/// Computes the minimum bounding box that fits the shape, optionally with a (mitered) border.
|
||||
Bounds getBounds(double border = 0, double miterLimit = 0, int polarity = 0) const;
|
||||
Bounds getBounds(real border = 0, real miterLimit = 0, int polarity = 0) const;
|
||||
/// Outputs the scanline that intersects the shape at y.
|
||||
void scanline(Scanline &line, double y) const;
|
||||
void scanline(Scanline &line, real y) const;
|
||||
/// Returns the total number of edge segments
|
||||
int edgeCount() const;
|
||||
/// Assumes its contours are unoriented (even-odd fill rule). Attempts to orient them to conform to the non-zero winding rule.
|
||||
|
|
|
|||
|
|
@ -10,11 +10,11 @@ namespace msdfgen {
|
|||
class SignedDistance {
|
||||
|
||||
public:
|
||||
double distance;
|
||||
double dot;
|
||||
real distance;
|
||||
real dot;
|
||||
|
||||
inline SignedDistance() : distance(-DBL_MAX), dot(0) { }
|
||||
inline SignedDistance(double dist, double d) : distance(dist), dot(d) { }
|
||||
inline SignedDistance() : distance(-FLT_MAX), dot(0) { }
|
||||
inline SignedDistance(real dist, real d) : distance(dist), dot(d) { }
|
||||
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include <cstddef>
|
||||
#include <cmath>
|
||||
#include "types.h"
|
||||
|
||||
namespace msdfgen {
|
||||
|
||||
|
|
@ -12,11 +13,11 @@ namespace msdfgen {
|
|||
*/
|
||||
struct Vector2 {
|
||||
|
||||
double x, y;
|
||||
real x, y;
|
||||
|
||||
inline Vector2(double val = 0) : x(val), y(val) { }
|
||||
inline Vector2(real val = 0) : x(val), y(val) { }
|
||||
|
||||
inline Vector2(double x, double y) : x(x), y(y) { }
|
||||
inline Vector2(real x, real y) : x(x), y(y) { }
|
||||
|
||||
/// Sets the vector to zero.
|
||||
inline void reset() {
|
||||
|
|
@ -24,23 +25,23 @@ struct Vector2 {
|
|||
}
|
||||
|
||||
/// Sets individual elements of the vector.
|
||||
inline void set(double x, double y) {
|
||||
inline void set(real x, real y) {
|
||||
this->x = x, this->y = y;
|
||||
}
|
||||
|
||||
/// Returns the vector's squared length.
|
||||
inline double squaredLength() const {
|
||||
inline real squaredLength() const {
|
||||
return x*x+y*y;
|
||||
}
|
||||
|
||||
/// Returns the vector's length.
|
||||
inline double length() const {
|
||||
inline real length() const {
|
||||
return sqrt(x*x+y*y);
|
||||
}
|
||||
|
||||
/// Returns the normalized vector - one that has the same direction but unit length.
|
||||
inline Vector2 normalize(bool allowZero = false) const {
|
||||
if (double len = length())
|
||||
if (real len = length())
|
||||
return Vector2(x/len, y/len);
|
||||
return Vector2(0, !allowZero);
|
||||
}
|
||||
|
|
@ -52,7 +53,7 @@ struct Vector2 {
|
|||
|
||||
/// Returns a vector with unit length that is orthogonal to this one.
|
||||
inline Vector2 getOrthonormal(bool polarity = true, bool allowZero = false) const {
|
||||
if (double len = length())
|
||||
if (real len = length())
|
||||
return polarity ? Vector2(-y/len, x/len) : Vector2(y/len, -x/len);
|
||||
return polarity ? Vector2(0, !allowZero) : Vector2(0, -!allowZero);
|
||||
}
|
||||
|
|
@ -87,12 +88,12 @@ struct Vector2 {
|
|||
return *this;
|
||||
}
|
||||
|
||||
inline Vector2 &operator*=(double value) {
|
||||
inline Vector2 &operator*=(real value) {
|
||||
x *= value, y *= value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline Vector2 &operator/=(double value) {
|
||||
inline Vector2 &operator/=(real value) {
|
||||
x /= value, y /= value;
|
||||
return *this;
|
||||
}
|
||||
|
|
@ -103,12 +104,12 @@ struct Vector2 {
|
|||
typedef Vector2 Point2;
|
||||
|
||||
/// Dot product of two vectors.
|
||||
inline double dotProduct(const Vector2 a, const Vector2 b) {
|
||||
inline real dotProduct(const Vector2 a, const Vector2 b) {
|
||||
return a.x*b.x+a.y*b.y;
|
||||
}
|
||||
|
||||
/// A special version of the cross product for 2D vectors (returns scalar value).
|
||||
inline double crossProduct(const Vector2 a, const Vector2 b) {
|
||||
inline real crossProduct(const Vector2 a, const Vector2 b) {
|
||||
return a.x*b.y-a.y*b.x;
|
||||
}
|
||||
|
||||
|
|
@ -148,19 +149,19 @@ inline Vector2 operator/(const Vector2 a, const Vector2 b) {
|
|||
return Vector2(a.x/b.x, a.y/b.y);
|
||||
}
|
||||
|
||||
inline Vector2 operator*(double a, const Vector2 b) {
|
||||
inline Vector2 operator*(real a, const Vector2 b) {
|
||||
return Vector2(a*b.x, a*b.y);
|
||||
}
|
||||
|
||||
inline Vector2 operator/(double a, const Vector2 b) {
|
||||
inline Vector2 operator/(real a, const Vector2 b) {
|
||||
return Vector2(a/b.x, a/b.y);
|
||||
}
|
||||
|
||||
inline Vector2 operator*(const Vector2 a, double b) {
|
||||
inline Vector2 operator*(const Vector2 a, real b) {
|
||||
return Vector2(a.x*b, a.y*b);
|
||||
}
|
||||
|
||||
inline Vector2 operator/(const Vector2 a, double b) {
|
||||
inline Vector2 operator/(const Vector2 a, real b) {
|
||||
return Vector2(a.x/b, a.y/b);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,16 +2,17 @@
|
|||
#pragma once
|
||||
|
||||
#include <cmath>
|
||||
#include "types.h"
|
||||
#include "Vector2.hpp"
|
||||
|
||||
// Parameters for iterative search of closest point on a cubic Bezier curve. Increase for higher precision.
|
||||
#define MSDFGEN_CUBIC_SEARCH_STARTS 4
|
||||
#define MSDFGEN_CUBIC_SEARCH_STEPS 4
|
||||
|
||||
#define MSDFGEN_QUADRATIC_RATIO_LIMIT 1e8
|
||||
#define MSDFGEN_QUADRATIC_RATIO_LIMIT ::msdfgen::real(1e8)
|
||||
|
||||
#ifndef MSDFGEN_CUBE_ROOT
|
||||
#define MSDFGEN_CUBE_ROOT(x) pow((x), 1/3.)
|
||||
#define MSDFGEN_CUBE_ROOT(x) pow((x), ::msdfgen::real(1)/::msdfgen::real(3))
|
||||
#endif
|
||||
|
||||
namespace msdfgen {
|
||||
|
|
@ -22,37 +23,37 @@ namespace msdfgen {
|
|||
* q = 2*P1-2*P0
|
||||
* r = P2-2*P1+P0
|
||||
*/
|
||||
inline double quadraticNearPoint(const Vector2 p, const Vector2 q, const Vector2 r) {
|
||||
double qq = q.squaredLength();
|
||||
double rr = r.squaredLength();
|
||||
inline real quadraticNearPoint(const Vector2 p, const Vector2 q, const Vector2 r) {
|
||||
real qq = q.squaredLength();
|
||||
real rr = r.squaredLength();
|
||||
if (qq >= MSDFGEN_QUADRATIC_RATIO_LIMIT*rr)
|
||||
return dotProduct(p, q)/qq;
|
||||
double norm = .5/rr;
|
||||
double a = 3*norm*dotProduct(q, r);
|
||||
double b = norm*(qq-2*dotProduct(p, r));
|
||||
double c = norm*dotProduct(p, q);
|
||||
double aa = a*a;
|
||||
double g = 1/9.*(aa-3*b);
|
||||
double h = 1/54.*(a*(aa+aa-9*b)-27*c);
|
||||
double hh = h*h;
|
||||
double ggg = g*g*g;
|
||||
a *= 1/3.;
|
||||
real norm = real(.5)/rr;
|
||||
real a = real(3)*norm*dotProduct(q, r);
|
||||
real b = norm*(qq-real(2)*dotProduct(p, r));
|
||||
real c = norm*dotProduct(p, q);
|
||||
real aa = a*a;
|
||||
real g = real(1)/real(9)*(aa-real(3)*b);
|
||||
real h = real(1)/real(54)*(a*(aa+aa-real(9)*b)-real(27)*c);
|
||||
real hh = h*h;
|
||||
real ggg = g*g*g;
|
||||
a *= real(1)/real(3);
|
||||
if (hh < ggg) {
|
||||
double u = 1/3.*acos(h/sqrt(ggg));
|
||||
g = -2*sqrt(g);
|
||||
if (h >= 0) {
|
||||
double t = g*cos(u)-a;
|
||||
if (t >= 0)
|
||||
real u = real(1)/real(3)*acos(h/sqrt(ggg));
|
||||
g = real(-2)*sqrt(g);
|
||||
if (h >= real(0)) {
|
||||
real t = g*cos(u)-a;
|
||||
if (t >= real(0))
|
||||
return t;
|
||||
return g*cos(u+2.0943951023931954923)-a; // 2.094 = PI*2/3
|
||||
return g*cos(u+real(2.0943951023931954923))-a; // 2.094 = PI*2/3
|
||||
} else {
|
||||
double t = g*cos(u+2.0943951023931954923)-a;
|
||||
if (t <= 1)
|
||||
real t = g*cos(u+real(2.0943951023931954923))-a;
|
||||
if (t <= real(1))
|
||||
return t;
|
||||
return g*cos(u)-a;
|
||||
}
|
||||
}
|
||||
double s = (h < 0 ? 1. : -1.)*MSDFGEN_CUBE_ROOT(fabs(h)+sqrt(hh-ggg));
|
||||
real s = (h < real(0) ? real(1) : real(-1))*MSDFGEN_CUBE_ROOT(fabs(h)+sqrt(hh-ggg));
|
||||
return s+g/s-a;
|
||||
}
|
||||
|
||||
|
|
@ -63,20 +64,20 @@ inline double quadraticNearPoint(const Vector2 p, const Vector2 q, const Vector2
|
|||
* r = 3*P2-6*P1+3*P0
|
||||
* s = P3-3*P2+3*P1-P0
|
||||
*/
|
||||
inline double cubicNearPoint(const Vector2 p, const Vector2 q, const Vector2 r, const Vector2 s, double &squaredDistance) {
|
||||
inline real cubicNearPoint(const Vector2 p, const Vector2 q, const Vector2 r, const Vector2 s, real &squaredDistance) {
|
||||
squaredDistance = p.squaredLength();
|
||||
double bestT = 0;
|
||||
real bestT = 0;
|
||||
for (int i = 0; i <= MSDFGEN_CUBIC_SEARCH_STARTS; ++i) {
|
||||
double t = 1./MSDFGEN_CUBIC_SEARCH_STARTS*i;
|
||||
real t = real(1)/real(MSDFGEN_CUBIC_SEARCH_STARTS)*real(i);
|
||||
Vector2 curP = p-(q+(r+s*t)*t)*t;
|
||||
for (int step = 0; step < MSDFGEN_CUBIC_SEARCH_STEPS; ++step) {
|
||||
Vector2 d0 = q+(r+r+3*s*t)*t;
|
||||
Vector2 d1 = r+r+6*s*t;
|
||||
Vector2 d0 = q+(r+r+real(3)*s*t)*t;
|
||||
Vector2 d1 = r+r+real(6)*s*t;
|
||||
t += dotProduct(curP, d0)/(d0.squaredLength()-dotProduct(curP, d1));
|
||||
if (t <= 0 || t >= 1)
|
||||
if (t <= real(0) || t >= real(1))
|
||||
break;
|
||||
curP = p-(q+(r+s*t)*t)*t;
|
||||
double curSquaredDistance = curP.squaredLength();
|
||||
real curSquaredDistance = curP.squaredLength();
|
||||
if (curSquaredDistance < squaredDistance) {
|
||||
squaredDistance = curSquaredDistance;
|
||||
bestT = t;
|
||||
|
|
@ -86,8 +87,8 @@ inline double cubicNearPoint(const Vector2 p, const Vector2 q, const Vector2 r,
|
|||
return bestT;
|
||||
}
|
||||
|
||||
inline double cubicNearPoint(const Vector2 p, const Vector2 q, const Vector2 r, const Vector2 s) {
|
||||
double squaredDistance;
|
||||
inline real cubicNearPoint(const Vector2 p, const Vector2 q, const Vector2 r, const Vector2 s) {
|
||||
real squaredDistance;
|
||||
return cubicNearPoint(p, q, r, s, squaredDistance);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
#include "arithmetics.hpp"
|
||||
#include "Vector2.hpp"
|
||||
#include "BitmapRef.hpp"
|
||||
|
|
@ -9,13 +10,13 @@ namespace msdfgen {
|
|||
|
||||
template <typename T, int N>
|
||||
static void interpolate(T *output, const BitmapConstRef<T, N> &bitmap, Point2 pos) {
|
||||
pos -= .5;
|
||||
pos -= real(.5);
|
||||
int l = (int) floor(pos.x);
|
||||
int b = (int) floor(pos.y);
|
||||
int r = l+1;
|
||||
int t = b+1;
|
||||
double lr = pos.x-l;
|
||||
double bt = pos.y-b;
|
||||
real lr = pos.x-real(l);
|
||||
real bt = pos.y-real(b);
|
||||
l = clamp(l, bitmap.width-1), r = clamp(r, bitmap.width-1);
|
||||
b = clamp(b, bitmap.height-1), t = clamp(t, bitmap.height-1);
|
||||
for (int i = 0; i < N; ++i)
|
||||
|
|
|
|||
|
|
@ -6,21 +6,21 @@
|
|||
|
||||
namespace msdfgen {
|
||||
|
||||
static void initDistance(double &distance) {
|
||||
distance = -DBL_MAX;
|
||||
static void initDistance(real &distance) {
|
||||
distance = -FLT_MAX;
|
||||
}
|
||||
|
||||
static void initDistance(MultiDistance &distance) {
|
||||
distance.r = -DBL_MAX;
|
||||
distance.g = -DBL_MAX;
|
||||
distance.b = -DBL_MAX;
|
||||
distance.r = -FLT_MAX;
|
||||
distance.g = -FLT_MAX;
|
||||
distance.b = -FLT_MAX;
|
||||
}
|
||||
|
||||
static double resolveDistance(double distance) {
|
||||
static real resolveDistance(real distance) {
|
||||
return distance;
|
||||
}
|
||||
|
||||
static double resolveDistance(const MultiDistance &distance) {
|
||||
static real resolveDistance(const MultiDistance &distance) {
|
||||
return median(distance.r, distance.g, distance.b);
|
||||
}
|
||||
|
||||
|
|
@ -79,22 +79,22 @@ typename OverlappingContourCombiner<EdgeSelector>::DistanceType OverlappingConto
|
|||
for (int i = 0; i < contourCount; ++i) {
|
||||
DistanceType edgeDistance = edgeSelectors[i].distance();
|
||||
shapeEdgeSelector.merge(edgeSelectors[i]);
|
||||
if (windings[i] > 0 && resolveDistance(edgeDistance) >= 0)
|
||||
if (windings[i] > 0 && resolveDistance(edgeDistance) >= real(0))
|
||||
innerEdgeSelector.merge(edgeSelectors[i]);
|
||||
if (windings[i] < 0 && resolveDistance(edgeDistance) <= 0)
|
||||
if (windings[i] < 0 && resolveDistance(edgeDistance) <= real(0))
|
||||
outerEdgeSelector.merge(edgeSelectors[i]);
|
||||
}
|
||||
|
||||
DistanceType shapeDistance = shapeEdgeSelector.distance();
|
||||
DistanceType innerDistance = innerEdgeSelector.distance();
|
||||
DistanceType outerDistance = outerEdgeSelector.distance();
|
||||
double innerScalarDistance = resolveDistance(innerDistance);
|
||||
double outerScalarDistance = resolveDistance(outerDistance);
|
||||
real innerScalarDistance = resolveDistance(innerDistance);
|
||||
real outerScalarDistance = resolveDistance(outerDistance);
|
||||
DistanceType distance;
|
||||
initDistance(distance);
|
||||
|
||||
int winding = 0;
|
||||
if (innerScalarDistance >= 0 && fabs(innerScalarDistance) <= fabs(outerScalarDistance)) {
|
||||
if (innerScalarDistance >= real(0) && fabs(innerScalarDistance) <= fabs(outerScalarDistance)) {
|
||||
distance = innerDistance;
|
||||
winding = 1;
|
||||
for (int i = 0; i < contourCount; ++i)
|
||||
|
|
@ -103,7 +103,7 @@ typename OverlappingContourCombiner<EdgeSelector>::DistanceType OverlappingConto
|
|||
if (fabs(resolveDistance(contourDistance)) < fabs(outerScalarDistance) && resolveDistance(contourDistance) > resolveDistance(distance))
|
||||
distance = contourDistance;
|
||||
}
|
||||
} else if (outerScalarDistance <= 0 && fabs(outerScalarDistance) < fabs(innerScalarDistance)) {
|
||||
} else if (outerScalarDistance <= real(0) && fabs(outerScalarDistance) < fabs(innerScalarDistance)) {
|
||||
distance = outerDistance;
|
||||
winding = -1;
|
||||
for (int i = 0; i < contourCount; ++i)
|
||||
|
|
@ -118,7 +118,7 @@ typename OverlappingContourCombiner<EdgeSelector>::DistanceType OverlappingConto
|
|||
for (int i = 0; i < contourCount; ++i)
|
||||
if (windings[i] != winding) {
|
||||
DistanceType contourDistance = edgeSelectors[i].distance();
|
||||
if (resolveDistance(contourDistance)*resolveDistance(distance) >= 0 && fabs(resolveDistance(contourDistance)) < fabs(resolveDistance(distance)))
|
||||
if (resolveDistance(contourDistance)*resolveDistance(distance) >= real(0) && fabs(resolveDistance(contourDistance)) < fabs(resolveDistance(distance)))
|
||||
distance = contourDistance;
|
||||
}
|
||||
if (resolveDistance(distance) == resolveDistance(shapeDistance))
|
||||
|
|
|
|||
|
|
@ -11,15 +11,15 @@
|
|||
|
||||
namespace msdfgen {
|
||||
|
||||
static bool isCorner(const Vector2 &aDir, const Vector2 &bDir, double crossThreshold) {
|
||||
static bool isCorner(const Vector2 &aDir, const Vector2 &bDir, real crossThreshold) {
|
||||
return dotProduct(aDir, bDir) <= 0 || fabs(crossProduct(aDir, bDir)) > crossThreshold;
|
||||
}
|
||||
|
||||
static double estimateEdgeLength(const EdgeSegment *edge) {
|
||||
double len = 0;
|
||||
static real estimateEdgeLength(const EdgeSegment *edge) {
|
||||
real len = 0;
|
||||
Point2 prev = edge->point(0);
|
||||
for (int i = 1; i <= MSDFGEN_EDGE_LENGTH_PRECISION; ++i) {
|
||||
Point2 cur = edge->point(1./MSDFGEN_EDGE_LENGTH_PRECISION*i);
|
||||
Point2 cur = edge->point(real(1)/real(MSDFGEN_EDGE_LENGTH_PRECISION)*real(i));
|
||||
len += (cur-prev).length();
|
||||
prev = cur;
|
||||
}
|
||||
|
|
@ -43,8 +43,8 @@ static void switchColor(EdgeColor &color, unsigned long long &seed, EdgeColor ba
|
|||
seed >>= 1;
|
||||
}
|
||||
|
||||
void edgeColoringSimple(Shape &shape, double angleThreshold, unsigned long long seed) {
|
||||
double crossThreshold = sin(angleThreshold);
|
||||
void edgeColoringSimple(Shape &shape, real angleThreshold, unsigned long long seed) {
|
||||
real crossThreshold = sin(angleThreshold);
|
||||
std::vector<int> corners;
|
||||
for (std::vector<Contour>::iterator contour = shape.contours.begin(); contour != shape.contours.end(); ++contour) {
|
||||
// Identify corners
|
||||
|
|
@ -115,18 +115,18 @@ void edgeColoringSimple(Shape &shape, double angleThreshold, unsigned long long
|
|||
|
||||
struct EdgeColoringInkTrapCorner {
|
||||
int index;
|
||||
double prevEdgeLengthEstimate;
|
||||
real prevEdgeLengthEstimate;
|
||||
bool minor;
|
||||
EdgeColor color;
|
||||
};
|
||||
|
||||
void edgeColoringInkTrap(Shape &shape, double angleThreshold, unsigned long long seed) {
|
||||
void edgeColoringInkTrap(Shape &shape, real angleThreshold, unsigned long long seed) {
|
||||
typedef EdgeColoringInkTrapCorner Corner;
|
||||
double crossThreshold = sin(angleThreshold);
|
||||
real crossThreshold = sin(angleThreshold);
|
||||
std::vector<Corner> corners;
|
||||
for (std::vector<Contour>::iterator contour = shape.contours.begin(); contour != shape.contours.end(); ++contour) {
|
||||
// Identify corners
|
||||
double splineLength = 0;
|
||||
real splineLength = 0;
|
||||
corners.clear();
|
||||
if (!contour->edges.empty()) {
|
||||
Vector2 prevDirection = contour->edges.back()->direction(1);
|
||||
|
|
@ -227,29 +227,29 @@ void edgeColoringInkTrap(Shape &shape, double angleThreshold, unsigned long long
|
|||
#define MAX_RECOLOR_STEPS 16
|
||||
#define EDGE_DISTANCE_PRECISION 16
|
||||
|
||||
static double edgeToEdgeDistance(const EdgeSegment &a, const EdgeSegment &b, int precision) {
|
||||
static real edgeToEdgeDistance(const EdgeSegment &a, const EdgeSegment &b, int precision) {
|
||||
if (a.point(0) == b.point(0) || a.point(0) == b.point(1) || a.point(1) == b.point(0) || a.point(1) == b.point(1))
|
||||
return 0;
|
||||
double iFac = 1./precision;
|
||||
double minDistance = (b.point(0)-a.point(0)).length();
|
||||
real iFac = real(1)/real(precision);
|
||||
real minDistance = (b.point(0)-a.point(0)).length();
|
||||
for (int i = 0; i <= precision; ++i) {
|
||||
double t = iFac*i;
|
||||
double d = fabs(a.signedDistance(b.point(t), t).distance);
|
||||
real t = iFac*i;
|
||||
real d = fabs(a.signedDistance(b.point(t), t).distance);
|
||||
minDistance = min(minDistance, d);
|
||||
}
|
||||
for (int i = 0; i <= precision; ++i) {
|
||||
double t = iFac*i;
|
||||
double d = fabs(b.signedDistance(a.point(t), t).distance);
|
||||
real t = iFac*i;
|
||||
real d = fabs(b.signedDistance(a.point(t), t).distance);
|
||||
minDistance = min(minDistance, d);
|
||||
}
|
||||
return minDistance;
|
||||
}
|
||||
|
||||
static double splineToSplineDistance(EdgeSegment *const *edgeSegments, int aStart, int aEnd, int bStart, int bEnd, int precision) {
|
||||
double minDistance = DBL_MAX;
|
||||
static real splineToSplineDistance(EdgeSegment *const *edgeSegments, int aStart, int aEnd, int bStart, int bEnd, int precision) {
|
||||
real minDistance = FLT_MAX;
|
||||
for (int ai = aStart; ai < aEnd; ++ai)
|
||||
for (int bi = bStart; bi < bEnd && minDistance; ++bi) {
|
||||
double d = edgeToEdgeDistance(*edgeSegments[ai], *edgeSegments[bi], precision);
|
||||
real d = edgeToEdgeDistance(*edgeSegments[ai], *edgeSegments[bi], precision);
|
||||
minDistance = min(minDistance, d);
|
||||
}
|
||||
return minDistance;
|
||||
|
|
@ -358,16 +358,16 @@ static bool tryAddEdge(int *coloring, int *const *edgeMatrix, int vertexCount, i
|
|||
return true;
|
||||
}
|
||||
|
||||
static int cmpDoublePtr(const void *a, const void *b) {
|
||||
return sign(**reinterpret_cast<const double *const *>(a)-**reinterpret_cast<const double *const *>(b));
|
||||
static int cmpRealPtr(const void *a, const void *b) {
|
||||
return sign(**reinterpret_cast<const real *const *>(a)-**reinterpret_cast<const real *const *>(b));
|
||||
}
|
||||
|
||||
void edgeColoringByDistance(Shape &shape, double angleThreshold, unsigned long long seed) {
|
||||
void edgeColoringByDistance(Shape &shape, real angleThreshold, unsigned long long seed) {
|
||||
|
||||
std::vector<EdgeSegment *> edgeSegments;
|
||||
std::vector<int> splineStarts;
|
||||
|
||||
double crossThreshold = sin(angleThreshold);
|
||||
real crossThreshold = sin(angleThreshold);
|
||||
std::vector<int> corners;
|
||||
for (std::vector<Contour>::iterator contour = shape.contours.begin(); contour != shape.contours.end(); ++contour)
|
||||
if (!contour->edges.empty()) {
|
||||
|
|
@ -445,29 +445,29 @@ void edgeColoringByDistance(Shape &shape, double angleThreshold, unsigned long l
|
|||
if (!splineCount)
|
||||
return;
|
||||
|
||||
std::vector<double> distanceMatrixStorage(splineCount*splineCount);
|
||||
std::vector<double *> distanceMatrix(splineCount);
|
||||
std::vector<real> distanceMatrixStorage(splineCount*splineCount);
|
||||
std::vector<real *> distanceMatrix(splineCount);
|
||||
for (int i = 0; i < splineCount; ++i)
|
||||
distanceMatrix[i] = &distanceMatrixStorage[i*splineCount];
|
||||
const double *distanceMatrixBase = &distanceMatrixStorage[0];
|
||||
const real *distanceMatrixBase = &distanceMatrixStorage[0];
|
||||
|
||||
for (int i = 0; i < splineCount; ++i) {
|
||||
distanceMatrix[i][i] = -1;
|
||||
for (int j = i+1; j < splineCount; ++j) {
|
||||
double dist = splineToSplineDistance(&edgeSegments[0], splineStarts[i], splineStarts[i+1], splineStarts[j], splineStarts[j+1], EDGE_DISTANCE_PRECISION);
|
||||
real dist = splineToSplineDistance(&edgeSegments[0], splineStarts[i], splineStarts[i+1], splineStarts[j], splineStarts[j+1], EDGE_DISTANCE_PRECISION);
|
||||
distanceMatrix[i][j] = dist;
|
||||
distanceMatrix[j][i] = dist;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<const double *> graphEdgeDistances;
|
||||
std::vector<const real *> graphEdgeDistances;
|
||||
graphEdgeDistances.reserve(splineCount*(splineCount-1)/2);
|
||||
for (int i = 0; i < splineCount; ++i)
|
||||
for (int j = i+1; j < splineCount; ++j)
|
||||
graphEdgeDistances.push_back(&distanceMatrix[i][j]);
|
||||
int graphEdgeCount = (int) graphEdgeDistances.size();
|
||||
if (!graphEdgeDistances.empty())
|
||||
qsort(&graphEdgeDistances[0], graphEdgeDistances.size(), sizeof(const double *), &cmpDoublePtr);
|
||||
qsort(&graphEdgeDistances[0], graphEdgeDistances.size(), sizeof(const real *), &cmpRealPtr);
|
||||
|
||||
std::vector<int> edgeMatrixStorage(splineCount*splineCount);
|
||||
std::vector<int *> edgeMatrix(splineCount);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
#include "Shape.h"
|
||||
|
||||
#define MSDFGEN_EDGE_LENGTH_PRECISION 4
|
||||
|
|
@ -12,18 +13,18 @@ namespace msdfgen {
|
|||
* angleThreshold specifies the maximum angle (in radians) to be considered a corner, for example 3 (~172 degrees).
|
||||
* Values below 1/2 PI will be treated as the external angle.
|
||||
*/
|
||||
void edgeColoringSimple(Shape &shape, double angleThreshold, unsigned long long seed = 0);
|
||||
void edgeColoringSimple(Shape &shape, real angleThreshold, unsigned long long seed = 0);
|
||||
|
||||
/** The alternative "ink trap" coloring strategy is designed for better results with typefaces
|
||||
* that use ink traps as a design feature. It guarantees that even if all edges that are shorter than
|
||||
* both their neighboring edges are removed, the coloring remains consistent with the established rules.
|
||||
*/
|
||||
void edgeColoringInkTrap(Shape &shape, double angleThreshold, unsigned long long seed = 0);
|
||||
void edgeColoringInkTrap(Shape &shape, real angleThreshold, unsigned long long seed = 0);
|
||||
|
||||
/** The alternative coloring by distance tries to use different colors for edges that are close together.
|
||||
* This should theoretically be the best strategy on average. However, since it needs to compute the distance
|
||||
* between all pairs of edges, and perform a graph optimization task, it is much slower than the rest.
|
||||
*/
|
||||
void edgeColoringByDistance(Shape &shape, double angleThreshold, unsigned long long seed = 0);
|
||||
void edgeColoringByDistance(Shape &shape, real angleThreshold, unsigned long long seed = 0);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,24 +9,24 @@
|
|||
|
||||
namespace msdfgen {
|
||||
|
||||
void EdgeSegment::distanceToPseudoDistance(SignedDistance &distance, Point2 origin, double param) const {
|
||||
if (param < 0) {
|
||||
void EdgeSegment::distanceToPseudoDistance(SignedDistance &distance, Point2 origin, real param) const {
|
||||
if (param < real(0)) {
|
||||
Vector2 dir = direction(0).normalize();
|
||||
Vector2 aq = origin-point(0);
|
||||
double ts = dotProduct(aq, dir);
|
||||
if (ts < 0) {
|
||||
double pseudoDistance = crossProduct(aq, dir);
|
||||
real ts = dotProduct(aq, dir);
|
||||
if (ts < real(0)) {
|
||||
real pseudoDistance = crossProduct(aq, dir);
|
||||
if (fabs(pseudoDistance) <= fabs(distance.distance)) {
|
||||
distance.distance = pseudoDistance;
|
||||
distance.dot = 0;
|
||||
}
|
||||
}
|
||||
} else if (param > 1) {
|
||||
} else if (param > real(1)) {
|
||||
Vector2 dir = direction(1).normalize();
|
||||
Vector2 bq = origin-point(1);
|
||||
double ts = dotProduct(bq, dir);
|
||||
if (ts > 0) {
|
||||
double pseudoDistance = crossProduct(bq, dir);
|
||||
real ts = dotProduct(bq, dir);
|
||||
if (ts > real(0)) {
|
||||
real pseudoDistance = crossProduct(bq, dir);
|
||||
if (fabs(pseudoDistance) <= fabs(distance.distance)) {
|
||||
distance.distance = pseudoDistance;
|
||||
distance.dot = 0;
|
||||
|
|
@ -42,7 +42,7 @@ LinearSegment::LinearSegment(Point2 p0, Point2 p1, EdgeColor edgeColor) : EdgeSe
|
|||
|
||||
QuadraticSegment::QuadraticSegment(Point2 p0, Point2 p1, Point2 p2, EdgeColor edgeColor) : EdgeSegment(edgeColor) {
|
||||
if (p1 == p0 || p1 == p2)
|
||||
p1 = 0.5*(p0+p2);
|
||||
p1 = real(.5)*(p0+p2);
|
||||
p[0] = p0;
|
||||
p[1] = p1;
|
||||
p[2] = p2;
|
||||
|
|
@ -50,8 +50,8 @@ QuadraticSegment::QuadraticSegment(Point2 p0, Point2 p1, Point2 p2, EdgeColor ed
|
|||
|
||||
CubicSegment::CubicSegment(Point2 p0, Point2 p1, Point2 p2, Point2 p3, EdgeColor edgeColor) : EdgeSegment(edgeColor) {
|
||||
if ((p1 == p0 || p1 == p3) && (p2 == p0 || p2 == p3)) {
|
||||
p1 = mix(p0, p3, 1/3.);
|
||||
p2 = mix(p0, p3, 2/3.);
|
||||
p1 = mix(p0, p3, real(1)/real(3));
|
||||
p2 = mix(p0, p3, real(2)/real(3));
|
||||
}
|
||||
p[0] = p0;
|
||||
p[1] = p1;
|
||||
|
|
@ -95,31 +95,31 @@ const Point2 *CubicSegment::controlPoints() const {
|
|||
return p;
|
||||
}
|
||||
|
||||
Point2 LinearSegment::point(double param) const {
|
||||
Point2 LinearSegment::point(real param) const {
|
||||
return mix(p[0], p[1], param);
|
||||
}
|
||||
|
||||
Point2 QuadraticSegment::point(double param) const {
|
||||
Point2 QuadraticSegment::point(real param) const {
|
||||
return mix(mix(p[0], p[1], param), mix(p[1], p[2], param), param);
|
||||
}
|
||||
|
||||
Point2 CubicSegment::point(double param) const {
|
||||
Point2 CubicSegment::point(real param) const {
|
||||
Vector2 p12 = mix(p[1], p[2], param);
|
||||
return mix(mix(mix(p[0], p[1], param), p12, param), mix(p12, mix(p[2], p[3], param), param), param);
|
||||
}
|
||||
|
||||
Vector2 LinearSegment::direction(double param) const {
|
||||
Vector2 LinearSegment::direction(real param) const {
|
||||
return p[1]-p[0];
|
||||
}
|
||||
|
||||
Vector2 QuadraticSegment::direction(double param) const {
|
||||
Vector2 QuadraticSegment::direction(real param) const {
|
||||
Vector2 tangent = mix(p[1]-p[0], p[2]-p[1], param);
|
||||
if (!tangent)
|
||||
return p[2]-p[0];
|
||||
return tangent;
|
||||
}
|
||||
|
||||
Vector2 CubicSegment::direction(double param) const {
|
||||
Vector2 CubicSegment::direction(real param) const {
|
||||
Vector2 tangent = mix(mix(p[1]-p[0], p[2]-p[1], param), mix(p[2]-p[1], p[3]-p[2], param), param);
|
||||
if (!tangent) {
|
||||
if (param == 0) return p[2]-p[0];
|
||||
|
|
@ -128,195 +128,195 @@ Vector2 CubicSegment::direction(double param) const {
|
|||
return tangent;
|
||||
}
|
||||
|
||||
Vector2 LinearSegment::directionChange(double param) const {
|
||||
Vector2 LinearSegment::directionChange(real param) const {
|
||||
return Vector2();
|
||||
}
|
||||
|
||||
Vector2 QuadraticSegment::directionChange(double param) const {
|
||||
Vector2 QuadraticSegment::directionChange(real param) const {
|
||||
return (p[2]-p[1])-(p[1]-p[0]);
|
||||
}
|
||||
|
||||
Vector2 CubicSegment::directionChange(double param) const {
|
||||
Vector2 CubicSegment::directionChange(real param) const {
|
||||
return mix((p[2]-p[1])-(p[1]-p[0]), (p[3]-p[2])-(p[2]-p[1]), param);
|
||||
}
|
||||
|
||||
double LinearSegment::length() const {
|
||||
real LinearSegment::length() const {
|
||||
return (p[1]-p[0]).length();
|
||||
}
|
||||
|
||||
double QuadraticSegment::length() const {
|
||||
real QuadraticSegment::length() const {
|
||||
Vector2 ab = p[1]-p[0];
|
||||
Vector2 br = p[2]-p[1]-ab;
|
||||
double abab = dotProduct(ab, ab);
|
||||
double abbr = dotProduct(ab, br);
|
||||
double brbr = dotProduct(br, br);
|
||||
double abLen = sqrt(abab);
|
||||
double brLen = sqrt(brbr);
|
||||
double crs = crossProduct(ab, br);
|
||||
double h = sqrt(abab+abbr+abbr+brbr);
|
||||
real abab = dotProduct(ab, ab);
|
||||
real abbr = dotProduct(ab, br);
|
||||
real brbr = dotProduct(br, br);
|
||||
real abLen = sqrt(abab);
|
||||
real brLen = sqrt(brbr);
|
||||
real crs = crossProduct(ab, br);
|
||||
real h = sqrt(abab+abbr+abbr+brbr);
|
||||
return (
|
||||
brLen*((abbr+brbr)*h-abbr*abLen)+
|
||||
crs*crs*log((brLen*h+abbr+brbr)/(brLen*abLen+abbr))
|
||||
)/(brbr*brLen);
|
||||
}
|
||||
|
||||
SignedDistance LinearSegment::signedDistance(Point2 origin, double ¶m) const {
|
||||
SignedDistance LinearSegment::signedDistance(Point2 origin, real ¶m) const {
|
||||
Vector2 aq = origin-p[0];
|
||||
Vector2 ab = p[1]-p[0];
|
||||
param = dotProduct(aq, ab)/dotProduct(ab, ab);
|
||||
Vector2 eq = p[param > .5]-origin;
|
||||
double endpointDistance = eq.length();
|
||||
if (param > 0 && param < 1) {
|
||||
double orthoDistance = dotProduct(ab.getOrthonormal(false), aq);
|
||||
Vector2 eq = p[param > real(.5)]-origin;
|
||||
real endpointDistance = eq.length();
|
||||
if (param > real(0) && param < real(1)) {
|
||||
real orthoDistance = dotProduct(ab.getOrthonormal(false), aq);
|
||||
if (fabs(orthoDistance) < endpointDistance)
|
||||
return SignedDistance(orthoDistance, 0);
|
||||
}
|
||||
return SignedDistance(nonZeroSign(crossProduct(aq, ab))*endpointDistance, fabs(dotProduct(ab.normalize(), eq.normalize())));
|
||||
return SignedDistance(real(nonZeroSign(crossProduct(aq, ab)))*endpointDistance, fabs(dotProduct(ab.normalize(), eq.normalize())));
|
||||
}
|
||||
|
||||
#ifdef MSDFGEN_USE_BEZIER_SOLVER
|
||||
|
||||
SignedDistance QuadraticSegment::signedDistance(Point2 origin, double ¶m) const {
|
||||
SignedDistance QuadraticSegment::signedDistance(Point2 origin, real ¶m) const {
|
||||
Vector2 ap = origin-p[0];
|
||||
Vector2 bp = origin-p[2];
|
||||
Vector2 q = 2*(p[1]-p[0]);
|
||||
Vector2 q = real(2)*(p[1]-p[0]);
|
||||
Vector2 r = p[2]-2*p[1]+p[0];
|
||||
double aSqD = ap.squaredLength();
|
||||
double bSqD = bp.squaredLength();
|
||||
double t = quadraticNearPoint(ap, q, r);
|
||||
if (t > 0 && t < 1) {
|
||||
real aSqD = ap.squaredLength();
|
||||
real bSqD = bp.squaredLength();
|
||||
real t = quadraticNearPoint(ap, q, r);
|
||||
if (t > real(0) && t < real(1)) {
|
||||
Vector2 tp = ap-(q+r*t)*t;
|
||||
double tSqD = tp.squaredLength();
|
||||
real tSqD = tp.squaredLength();
|
||||
if (tSqD < aSqD && tSqD < bSqD) {
|
||||
param = t;
|
||||
return SignedDistance(nonZeroSign(crossProduct(tp, q+2*r*t))*sqrt(tSqD), 0);
|
||||
return SignedDistance(real(nonZeroSign(crossProduct(tp, q+real(2)*r*t)))*sqrt(tSqD), 0);
|
||||
}
|
||||
}
|
||||
if (bSqD < aSqD) {
|
||||
Vector2 d = q+r+r;
|
||||
if (!d)
|
||||
d = p[2]-p[0];
|
||||
param = dotProduct(bp, d)/d.squaredLength()+1;
|
||||
return SignedDistance(nonZeroSign(crossProduct(bp, d))*sqrt(bSqD), dotProduct(bp.normalize(), d.normalize()));
|
||||
param = dotProduct(bp, d)/d.squaredLength()+real(1);
|
||||
return SignedDistance(real(nonZeroSign(crossProduct(bp, d)))*sqrt(bSqD), dotProduct(bp.normalize(), d.normalize()));
|
||||
}
|
||||
if (!q)
|
||||
q = p[2]-p[0];
|
||||
param = dotProduct(ap, q)/q.squaredLength();
|
||||
return SignedDistance(nonZeroSign(crossProduct(ap, q))*sqrt(aSqD), -dotProduct(ap.normalize(), q.normalize()));
|
||||
return SignedDistance(real(nonZeroSign(crossProduct(ap, q)))*sqrt(aSqD), -dotProduct(ap.normalize(), q.normalize()));
|
||||
}
|
||||
|
||||
SignedDistance CubicSegment::signedDistance(Point2 origin, double ¶m) const {
|
||||
SignedDistance CubicSegment::signedDistance(Point2 origin, real ¶m) const {
|
||||
Vector2 ap = origin-p[0];
|
||||
Vector2 bp = origin-p[3];
|
||||
Vector2 q = 3*(p[1]-p[0]);
|
||||
Vector2 r = 3*(p[2]-p[1])-q;
|
||||
Vector2 s = p[3]-3*(p[2]-p[1])-p[0];
|
||||
double aSqD = ap.squaredLength();
|
||||
double bSqD = bp.squaredLength();
|
||||
double tSqD;
|
||||
double t = cubicNearPoint(ap, q, r, s, tSqD);
|
||||
if (t > 0 && t < 1) {
|
||||
Vector2 q = real(3)*(p[1]-p[0]);
|
||||
Vector2 r = real(3)*(p[2]-p[1])-q;
|
||||
Vector2 s = p[3]-real(3)*(p[2]-p[1])-p[0];
|
||||
real aSqD = ap.squaredLength();
|
||||
real bSqD = bp.squaredLength();
|
||||
real tSqD;
|
||||
real t = cubicNearPoint(ap, q, r, s, tSqD);
|
||||
if (t > real(0) && t < real(1)) {
|
||||
if (tSqD < aSqD && tSqD < bSqD) {
|
||||
param = t;
|
||||
return SignedDistance(nonZeroSign(crossProduct(ap-(q+(r+s*t)*t)*t, q+(r+r+3*s*t)*t))*sqrt(tSqD), 0);
|
||||
return SignedDistance(real(nonZeroSign(crossProduct(ap-(q+(r+s*t)*t)*t, q+(r+r+real(3)*s*t)*t)))*sqrt(tSqD), 0);
|
||||
}
|
||||
}
|
||||
if (bSqD < aSqD) {
|
||||
Vector2 d = q+r+r+3*s;
|
||||
Vector2 d = q+r+r+real(3)*s;
|
||||
if (!d)
|
||||
d = p[3]-p[1];
|
||||
param = dotProduct(bp, d)/d.squaredLength()+1;
|
||||
return SignedDistance(nonZeroSign(crossProduct(bp, d))*sqrt(bSqD), dotProduct(bp.normalize(), d.normalize()));
|
||||
param = dotProduct(bp, d)/d.squaredLength()+real(1);
|
||||
return SignedDistance(real(nonZeroSign(crossProduct(bp, d)))*sqrt(bSqD), dotProduct(bp.normalize(), d.normalize()));
|
||||
}
|
||||
if (!q)
|
||||
q = p[2]-p[0];
|
||||
param = dotProduct(ap, q)/q.squaredLength();
|
||||
return SignedDistance(nonZeroSign(crossProduct(ap, q))*sqrt(aSqD), -dotProduct(ap.normalize(), q.normalize()));
|
||||
return SignedDistance(real(nonZeroSign(crossProduct(ap, q)))*sqrt(aSqD), -dotProduct(ap.normalize(), q.normalize()));
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
SignedDistance QuadraticSegment::signedDistance(Point2 origin, double ¶m) const {
|
||||
SignedDistance QuadraticSegment::signedDistance(Point2 origin, real ¶m) const {
|
||||
Vector2 qa = p[0]-origin;
|
||||
Vector2 ab = p[1]-p[0];
|
||||
Vector2 br = p[2]-p[1]-ab;
|
||||
double a = dotProduct(br, br);
|
||||
double b = 3*dotProduct(ab, br);
|
||||
double c = 2*dotProduct(ab, ab)+dotProduct(qa, br);
|
||||
double d = dotProduct(qa, ab);
|
||||
double t[3];
|
||||
real a = dotProduct(br, br);
|
||||
real b = real(3)*dotProduct(ab, br);
|
||||
real c = real(2)*dotProduct(ab, ab)+dotProduct(qa, br);
|
||||
real d = dotProduct(qa, ab);
|
||||
real t[3];
|
||||
int solutions = solveCubic(t, a, b, c, d);
|
||||
|
||||
Vector2 epDir = direction(0);
|
||||
double minDistance = nonZeroSign(crossProduct(epDir, qa))*qa.length(); // distance from A
|
||||
real minDistance = real(nonZeroSign(crossProduct(epDir, qa)))*qa.length(); // distance from A
|
||||
param = -dotProduct(qa, epDir)/dotProduct(epDir, epDir);
|
||||
{
|
||||
epDir = direction(1);
|
||||
double distance = (p[2]-origin).length(); // distance from B
|
||||
real distance = (p[2]-origin).length(); // distance from B
|
||||
if (distance < fabs(minDistance)) {
|
||||
minDistance = nonZeroSign(crossProduct(epDir, p[2]-origin))*distance;
|
||||
minDistance = real(nonZeroSign(crossProduct(epDir, p[2]-origin)))*distance;
|
||||
param = dotProduct(origin-p[1], epDir)/dotProduct(epDir, epDir);
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < solutions; ++i) {
|
||||
if (t[i] > 0 && t[i] < 1) {
|
||||
Point2 qe = qa+2*t[i]*ab+t[i]*t[i]*br;
|
||||
double distance = qe.length();
|
||||
if (t[i] > real(0) && t[i] < real(1)) {
|
||||
Point2 qe = qa+real(2)*t[i]*ab+t[i]*t[i]*br;
|
||||
real distance = qe.length();
|
||||
if (distance <= fabs(minDistance)) {
|
||||
minDistance = nonZeroSign(crossProduct(ab+t[i]*br, qe))*distance;
|
||||
minDistance = real(nonZeroSign(crossProduct(ab+t[i]*br, qe)))*distance;
|
||||
param = t[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (param >= 0 && param <= 1)
|
||||
if (param >= real(0) && param <= real(1))
|
||||
return SignedDistance(minDistance, 0);
|
||||
if (param < .5)
|
||||
if (param < real(.5))
|
||||
return SignedDistance(minDistance, fabs(dotProduct(direction(0).normalize(), qa.normalize())));
|
||||
else
|
||||
return SignedDistance(minDistance, fabs(dotProduct(direction(1).normalize(), (p[2]-origin).normalize())));
|
||||
}
|
||||
|
||||
SignedDistance CubicSegment::signedDistance(Point2 origin, double ¶m) const {
|
||||
SignedDistance CubicSegment::signedDistance(Point2 origin, real ¶m) const {
|
||||
Vector2 qa = p[0]-origin;
|
||||
Vector2 ab = p[1]-p[0];
|
||||
Vector2 br = p[2]-p[1]-ab;
|
||||
Vector2 as = (p[3]-p[2])-(p[2]-p[1])-br;
|
||||
|
||||
Vector2 epDir = direction(0);
|
||||
double minDistance = nonZeroSign(crossProduct(epDir, qa))*qa.length(); // distance from A
|
||||
real minDistance = real(nonZeroSign(crossProduct(epDir, qa)))*qa.length(); // distance from A
|
||||
param = -dotProduct(qa, epDir)/dotProduct(epDir, epDir);
|
||||
{
|
||||
epDir = direction(1);
|
||||
double distance = (p[3]-origin).length(); // distance from B
|
||||
real distance = (p[3]-origin).length(); // distance from B
|
||||
if (distance < fabs(minDistance)) {
|
||||
minDistance = nonZeroSign(crossProduct(epDir, p[3]-origin))*distance;
|
||||
minDistance = real(nonZeroSign(crossProduct(epDir, p[3]-origin)))*distance;
|
||||
param = dotProduct(epDir-(p[3]-origin), epDir)/dotProduct(epDir, epDir);
|
||||
}
|
||||
}
|
||||
// Iterative minimum distance search
|
||||
for (int i = 0; i <= MSDFGEN_CUBIC_SEARCH_STARTS; ++i) {
|
||||
double t = (double) i/MSDFGEN_CUBIC_SEARCH_STARTS;
|
||||
Vector2 qe = qa+3*t*ab+3*t*t*br+t*t*t*as;
|
||||
real t = real(1)/real(MSDFGEN_CUBIC_SEARCH_STARTS)*real(i);
|
||||
Vector2 qe = qa+real(3)*t*ab+real(3)*t*t*br+t*t*t*as;
|
||||
for (int step = 0; step < MSDFGEN_CUBIC_SEARCH_STEPS; ++step) {
|
||||
// Improve t
|
||||
Vector2 d1 = 3*ab+6*t*br+3*t*t*as;
|
||||
Vector2 d2 = 6*br+6*t*as;
|
||||
Vector2 d1 = real(3)*ab+real(6)*t*br+real(3)*t*t*as;
|
||||
Vector2 d2 = real(6)*br+real(6)*t*as;
|
||||
t -= dotProduct(qe, d1)/(dotProduct(d1, d1)+dotProduct(qe, d2));
|
||||
if (t <= 0 || t >= 1)
|
||||
if (t <= real(0) || t >= real(1))
|
||||
break;
|
||||
qe = qa+3*t*ab+3*t*t*br+t*t*t*as;
|
||||
double distance = qe.length();
|
||||
qe = qa+real(3)*t*ab+real(3)*t*t*br+t*t*t*as;
|
||||
real distance = qe.length();
|
||||
if (distance < fabs(minDistance)) {
|
||||
minDistance = nonZeroSign(crossProduct(d1, qe))*distance;
|
||||
minDistance = real(nonZeroSign(crossProduct(d1, qe)))*distance;
|
||||
param = t;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (param >= 0 && param <= 1)
|
||||
if (param >= real(0) && param <= real(1))
|
||||
return SignedDistance(minDistance, 0);
|
||||
if (param < .5)
|
||||
if (param < real(.5))
|
||||
return SignedDistance(minDistance, fabs(dotProduct(direction(0).normalize(), qa.normalize())));
|
||||
else
|
||||
return SignedDistance(minDistance, fabs(dotProduct(direction(1).normalize(), (p[3]-origin).normalize())));
|
||||
|
|
@ -324,9 +324,9 @@ SignedDistance CubicSegment::signedDistance(Point2 origin, double ¶m) const
|
|||
|
||||
#endif
|
||||
|
||||
int LinearSegment::scanlineIntersections(double x[3], int dy[3], double y) const {
|
||||
int LinearSegment::scanlineIntersections(real x[3], int dy[3], real y) const {
|
||||
if ((y >= p[0].y && y < p[1].y) || (y >= p[1].y && y < p[0].y)) {
|
||||
double param = (y-p[0].y)/(p[1].y-p[0].y);
|
||||
real param = (y-p[0].y)/(p[1].y-p[0].y);
|
||||
x[0] = mix(p[0].x, p[1].x, param);
|
||||
dy[0] = sign(p[1].y-p[0].y);
|
||||
return 1;
|
||||
|
|
@ -334,7 +334,7 @@ int LinearSegment::scanlineIntersections(double x[3], int dy[3], double y) const
|
|||
return 0;
|
||||
}
|
||||
|
||||
int QuadraticSegment::scanlineIntersections(double x[3], int dy[3], double y) const {
|
||||
int QuadraticSegment::scanlineIntersections(real x[3], int dy[3], real y) const {
|
||||
int total = 0;
|
||||
int nextDY = y > p[0].y ? 1 : -1;
|
||||
x[total] = p[0].x;
|
||||
|
|
@ -347,16 +347,16 @@ int QuadraticSegment::scanlineIntersections(double x[3], int dy[3], double y) co
|
|||
{
|
||||
Vector2 ab = p[1]-p[0];
|
||||
Vector2 br = p[2]-p[1]-ab;
|
||||
double t[2];
|
||||
real t[2];
|
||||
int solutions = solveQuadratic(t, br.y, 2*ab.y, p[0].y-y);
|
||||
// Sort solutions
|
||||
double tmp;
|
||||
real tmp;
|
||||
if (solutions >= 2 && t[0] > t[1])
|
||||
tmp = t[0], t[0] = t[1], t[1] = tmp;
|
||||
for (int i = 0; i < solutions && total < 2; ++i) {
|
||||
if (t[i] >= 0 && t[i] <= 1) {
|
||||
x[total] = p[0].x+2*t[i]*ab.x+t[i]*t[i]*br.x;
|
||||
if (nextDY*(ab.y+t[i]*br.y) >= 0) {
|
||||
if (real(nextDY)*(ab.y+t[i]*br.y) >= real(0)) {
|
||||
dy[total++] = nextDY;
|
||||
nextDY = -nextDY;
|
||||
}
|
||||
|
|
@ -388,7 +388,7 @@ int QuadraticSegment::scanlineIntersections(double x[3], int dy[3], double y) co
|
|||
return total;
|
||||
}
|
||||
|
||||
int CubicSegment::scanlineIntersections(double x[3], int dy[3], double y) const {
|
||||
int CubicSegment::scanlineIntersections(real x[3], int dy[3], real y) const {
|
||||
int total = 0;
|
||||
int nextDY = y > p[0].y ? 1 : -1;
|
||||
x[total] = p[0].x;
|
||||
|
|
@ -402,10 +402,10 @@ int CubicSegment::scanlineIntersections(double x[3], int dy[3], double y) const
|
|||
Vector2 ab = p[1]-p[0];
|
||||
Vector2 br = p[2]-p[1]-ab;
|
||||
Vector2 as = (p[3]-p[2])-(p[2]-p[1])-br;
|
||||
double t[3];
|
||||
real t[3];
|
||||
int solutions = solveCubic(t, as.y, 3*br.y, 3*ab.y, p[0].y-y);
|
||||
// Sort solutions
|
||||
double tmp;
|
||||
real tmp;
|
||||
if (solutions >= 2) {
|
||||
if (t[0] > t[1])
|
||||
tmp = t[0], t[0] = t[1], t[1] = tmp;
|
||||
|
|
@ -417,8 +417,8 @@ int CubicSegment::scanlineIntersections(double x[3], int dy[3], double y) const
|
|||
}
|
||||
for (int i = 0; i < solutions && total < 3; ++i) {
|
||||
if (t[i] >= 0 && t[i] <= 1) {
|
||||
x[total] = p[0].x+3*t[i]*ab.x+3*t[i]*t[i]*br.x+t[i]*t[i]*t[i]*as.x;
|
||||
if (nextDY*(ab.y+2*t[i]*br.y+t[i]*t[i]*as.y) >= 0) {
|
||||
x[total] = p[0].x+real(3)*t[i]*ab.x+real(3)*t[i]*t[i]*br.x+t[i]*t[i]*t[i]*as.x;
|
||||
if (real(nextDY)*(ab.y+real(2)*t[i]*br.y+t[i]*t[i]*as.y) >= real(0)) {
|
||||
dy[total++] = nextDY;
|
||||
nextDY = -nextDY;
|
||||
}
|
||||
|
|
@ -450,49 +450,49 @@ int CubicSegment::scanlineIntersections(double x[3], int dy[3], double y) const
|
|||
return total;
|
||||
}
|
||||
|
||||
static void pointBounds(Point2 p, double &l, double &b, double &r, double &t) {
|
||||
static void pointBounds(Point2 p, real &l, real &b, real &r, real &t) {
|
||||
if (p.x < l) l = p.x;
|
||||
if (p.y < b) b = p.y;
|
||||
if (p.x > r) r = p.x;
|
||||
if (p.y > t) t = p.y;
|
||||
}
|
||||
|
||||
void LinearSegment::bound(double &l, double &b, double &r, double &t) const {
|
||||
void LinearSegment::bound(real &l, real &b, real &r, real &t) const {
|
||||
pointBounds(p[0], l, b, r, t);
|
||||
pointBounds(p[1], l, b, r, t);
|
||||
}
|
||||
|
||||
void QuadraticSegment::bound(double &l, double &b, double &r, double &t) const {
|
||||
void QuadraticSegment::bound(real &l, real &b, real &r, real &t) const {
|
||||
pointBounds(p[0], l, b, r, t);
|
||||
pointBounds(p[2], l, b, r, t);
|
||||
Vector2 bot = (p[1]-p[0])-(p[2]-p[1]);
|
||||
if (bot.x) {
|
||||
double param = (p[1].x-p[0].x)/bot.x;
|
||||
if (param > 0 && param < 1)
|
||||
real param = (p[1].x-p[0].x)/bot.x;
|
||||
if (param > real(0) && param < real(1))
|
||||
pointBounds(point(param), l, b, r, t);
|
||||
}
|
||||
if (bot.y) {
|
||||
double param = (p[1].y-p[0].y)/bot.y;
|
||||
if (param > 0 && param < 1)
|
||||
real param = (p[1].y-p[0].y)/bot.y;
|
||||
if (param > real(0) && param < real(1))
|
||||
pointBounds(point(param), l, b, r, t);
|
||||
}
|
||||
}
|
||||
|
||||
void CubicSegment::bound(double &l, double &b, double &r, double &t) const {
|
||||
void CubicSegment::bound(real &l, real &b, real &r, real &t) const {
|
||||
pointBounds(p[0], l, b, r, t);
|
||||
pointBounds(p[3], l, b, r, t);
|
||||
Vector2 a0 = p[1]-p[0];
|
||||
Vector2 a1 = 2*(p[2]-p[1]-a0);
|
||||
Vector2 a2 = p[3]-3*p[2]+3*p[1]-p[0];
|
||||
double params[2];
|
||||
Vector2 a1 = real(2)*(p[2]-p[1]-a0);
|
||||
Vector2 a2 = p[3]-real(3)*p[2]+real(3)*p[1]-p[0];
|
||||
real params[2];
|
||||
int solutions;
|
||||
solutions = solveQuadratic(params, a2.x, a1.x, a0.x);
|
||||
for (int i = 0; i < solutions; ++i)
|
||||
if (params[i] > 0 && params[i] < 1)
|
||||
if (params[i] > real(0) && params[i] < real(1))
|
||||
pointBounds(point(params[i]), l, b, r, t);
|
||||
solutions = solveQuadratic(params, a2.y, a1.y, a0.y);
|
||||
for (int i = 0; i < solutions; ++i)
|
||||
if (params[i] > 0 && params[i] < 1)
|
||||
if (params[i] > real(0) && params[i] < real(1))
|
||||
pointBounds(point(params[i]), l, b, r, t);
|
||||
}
|
||||
|
||||
|
|
@ -526,7 +526,7 @@ void QuadraticSegment::moveStartPoint(Point2 to) {
|
|||
Point2 origP1 = p[1];
|
||||
p[1] += crossProduct(p[0]-p[1], to-p[0])/crossProduct(p[0]-p[1], p[2]-p[1])*(p[2]-p[1]);
|
||||
p[0] = to;
|
||||
if (dotProduct(origSDir, p[0]-p[1]) < 0)
|
||||
if (dotProduct(origSDir, p[0]-p[1]) < real(0))
|
||||
p[1] = origP1;
|
||||
}
|
||||
|
||||
|
|
@ -544,7 +544,7 @@ void QuadraticSegment::moveEndPoint(Point2 to) {
|
|||
Point2 origP1 = p[1];
|
||||
p[1] += crossProduct(p[2]-p[1], to-p[2])/crossProduct(p[2]-p[1], p[0]-p[1])*(p[0]-p[1]);
|
||||
p[2] = to;
|
||||
if (dotProduct(origEDir, p[2]-p[1]) < 0)
|
||||
if (dotProduct(origEDir, p[2]-p[1]) < real(0))
|
||||
p[1] = origP1;
|
||||
}
|
||||
|
||||
|
|
@ -554,40 +554,40 @@ void CubicSegment::moveEndPoint(Point2 to) {
|
|||
}
|
||||
|
||||
void LinearSegment::splitInThirds(EdgeSegment *&part1, EdgeSegment *&part2, EdgeSegment *&part3) const {
|
||||
part1 = new LinearSegment(p[0], point(1/3.), color);
|
||||
part2 = new LinearSegment(point(1/3.), point(2/3.), color);
|
||||
part3 = new LinearSegment(point(2/3.), p[1], color);
|
||||
part1 = new LinearSegment(p[0], point(real(1)/real(3)), color);
|
||||
part2 = new LinearSegment(point(real(1)/real(3)), point(real(2)/real(3)), color);
|
||||
part3 = new LinearSegment(point(real(2)/real(3)), p[1], color);
|
||||
}
|
||||
|
||||
void QuadraticSegment::splitInThirds(EdgeSegment *&part1, EdgeSegment *&part2, EdgeSegment *&part3) const {
|
||||
part1 = new QuadraticSegment(p[0], mix(p[0], p[1], 1/3.), point(1/3.), color);
|
||||
part2 = new QuadraticSegment(point(1/3.), mix(mix(p[0], p[1], 5/9.), mix(p[1], p[2], 4/9.), .5), point(2/3.), color);
|
||||
part3 = new QuadraticSegment(point(2/3.), mix(p[1], p[2], 2/3.), p[2], color);
|
||||
part1 = new QuadraticSegment(p[0], mix(p[0], p[1], real(1)/real(3)), point(real(1)/real(3)), color);
|
||||
part2 = new QuadraticSegment(point(real(1)/real(3)), mix(mix(p[0], p[1], real(5)/real(9)), mix(p[1], p[2], real(4)/real(9)), real(.5)), point(real(2)/real(3)), color);
|
||||
part3 = new QuadraticSegment(point(real(2)/real(3)), mix(p[1], p[2], real(2)/real(3)), p[2], color);
|
||||
}
|
||||
|
||||
void CubicSegment::splitInThirds(EdgeSegment *&part1, EdgeSegment *&part2, EdgeSegment *&part3) const {
|
||||
part1 = new CubicSegment(p[0], p[0] == p[1] ? p[0] : mix(p[0], p[1], 1/3.), mix(mix(p[0], p[1], 1/3.), mix(p[1], p[2], 1/3.), 1/3.), point(1/3.), color);
|
||||
part2 = new CubicSegment(point(1/3.),
|
||||
mix(mix(mix(p[0], p[1], 1/3.), mix(p[1], p[2], 1/3.), 1/3.), mix(mix(p[1], p[2], 1/3.), mix(p[2], p[3], 1/3.), 1/3.), 2/3.),
|
||||
mix(mix(mix(p[0], p[1], 2/3.), mix(p[1], p[2], 2/3.), 2/3.), mix(mix(p[1], p[2], 2/3.), mix(p[2], p[3], 2/3.), 2/3.), 1/3.),
|
||||
point(2/3.), color);
|
||||
part3 = new CubicSegment(point(2/3.), mix(mix(p[1], p[2], 2/3.), mix(p[2], p[3], 2/3.), 2/3.), p[2] == p[3] ? p[3] : mix(p[2], p[3], 2/3.), p[3], color);
|
||||
part1 = new CubicSegment(p[0], p[0] == p[1] ? p[0] : mix(p[0], p[1], real(1)/real(3)), mix(mix(p[0], p[1], real(1)/real(3)), mix(p[1], p[2], real(1)/real(3)), real(1)/real(3)), point(real(1)/real(3)), color);
|
||||
part2 = new CubicSegment(point(real(1)/real(3)),
|
||||
mix(mix(mix(p[0], p[1], real(1)/real(3)), mix(p[1], p[2], real(1)/real(3)), real(1)/real(3)), mix(mix(p[1], p[2], real(1)/real(3)), mix(p[2], p[3], real(1)/real(3)), real(1)/real(3)), real(2)/real(3)),
|
||||
mix(mix(mix(p[0], p[1], real(2)/real(3)), mix(p[1], p[2], real(2)/real(3)), real(2)/real(3)), mix(mix(p[1], p[2], real(2)/real(3)), mix(p[2], p[3], real(2)/real(3)), real(2)/real(3)), real(1)/real(3)),
|
||||
point(real(2)/real(3)), color);
|
||||
part3 = new CubicSegment(point(real(2)/real(3)), mix(mix(p[1], p[2], real(2)/real(3)), mix(p[2], p[3], real(2)/real(3)), real(2)/real(3)), p[2] == p[3] ? p[3] : mix(p[2], p[3], real(2)/real(3)), p[3], color);
|
||||
}
|
||||
|
||||
EdgeSegment *QuadraticSegment::convertToCubic() const {
|
||||
return new CubicSegment(p[0], mix(p[0], p[1], 2/3.), mix(p[1], p[2], 1/3.), p[2], color);
|
||||
return new CubicSegment(p[0], mix(p[0], p[1], real(2)/real(3)), mix(p[1], p[2], real(1)/real(3)), p[2], color);
|
||||
}
|
||||
|
||||
void CubicSegment::deconverge(int param, double amount) {
|
||||
void CubicSegment::deconverge(int param, real amount) {
|
||||
Vector2 dir = direction(param);
|
||||
Vector2 normal = dir.getOrthonormal();
|
||||
double h = dotProduct(directionChange(param)-dir, normal);
|
||||
real h = dotProduct(directionChange(param)-dir, normal);
|
||||
switch (param) {
|
||||
case 0:
|
||||
p[1] += amount*(dir+sign(h)*sqrt(fabs(h))*normal);
|
||||
p[1] += amount*(dir+real(sign(h))*sqrt(fabs(h))*normal);
|
||||
break;
|
||||
case 1:
|
||||
p[2] -= amount*(dir-sign(h)*sqrt(fabs(h))*normal);
|
||||
p[2] -= amount*(dir-real(sign(h))*sqrt(fabs(h))*normal);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
#include "Vector2.hpp"
|
||||
#include "SignedDistance.hpp"
|
||||
#include "EdgeColor.h"
|
||||
|
|
@ -22,19 +23,19 @@ public:
|
|||
/// Returns the array of control points.
|
||||
virtual const Point2 *controlPoints() const = 0;
|
||||
/// Returns the point on the edge specified by the parameter (between 0 and 1).
|
||||
virtual Point2 point(double param) const = 0;
|
||||
virtual Point2 point(real param) const = 0;
|
||||
/// Returns the direction the edge has at the point specified by the parameter.
|
||||
virtual Vector2 direction(double param) const = 0;
|
||||
virtual Vector2 direction(real param) const = 0;
|
||||
/// Returns the change of direction (second derivative) at the point specified by the parameter.
|
||||
virtual Vector2 directionChange(double param) const = 0;
|
||||
virtual Vector2 directionChange(real param) const = 0;
|
||||
/// Returns the minimum signed distance between origin and the edge.
|
||||
virtual SignedDistance signedDistance(Point2 origin, double ¶m) const = 0;
|
||||
virtual SignedDistance signedDistance(Point2 origin, real ¶m) const = 0;
|
||||
/// Converts a previously retrieved signed distance from origin to pseudo-distance.
|
||||
virtual void distanceToPseudoDistance(SignedDistance &distance, Point2 origin, double param) const;
|
||||
virtual void distanceToPseudoDistance(SignedDistance &distance, Point2 origin, real 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;
|
||||
virtual int scanlineIntersections(real x[3], int dy[3], real y) const = 0;
|
||||
/// Adjusts the bounding box to fit the edge segment.
|
||||
virtual void bound(double &l, double &b, double &r, double &t) const = 0;
|
||||
virtual void bound(real &l, real &b, real &r, real &t) const = 0;
|
||||
|
||||
/// Reverses the edge (swaps its start point and end point).
|
||||
virtual void reverse() = 0;
|
||||
|
|
@ -61,13 +62,13 @@ public:
|
|||
LinearSegment *clone() const;
|
||||
int type() const;
|
||||
const Point2 *controlPoints() const;
|
||||
Point2 point(double param) const;
|
||||
Vector2 direction(double param) const;
|
||||
Vector2 directionChange(double param) const;
|
||||
double length() const;
|
||||
SignedDistance signedDistance(Point2 origin, double ¶m) const;
|
||||
int scanlineIntersections(double x[3], int dy[3], double y) const;
|
||||
void bound(double &l, double &b, double &r, double &t) const;
|
||||
Point2 point(real param) const;
|
||||
Vector2 direction(real param) const;
|
||||
Vector2 directionChange(real param) const;
|
||||
real length() const;
|
||||
SignedDistance signedDistance(Point2 origin, real ¶m) const;
|
||||
int scanlineIntersections(real x[3], int dy[3], real y) const;
|
||||
void bound(real &l, real &b, real &r, real &t) const;
|
||||
|
||||
void reverse();
|
||||
void moveStartPoint(Point2 to);
|
||||
|
|
@ -90,13 +91,13 @@ public:
|
|||
QuadraticSegment *clone() const;
|
||||
int type() const;
|
||||
const Point2 *controlPoints() const;
|
||||
Point2 point(double param) const;
|
||||
Vector2 direction(double param) const;
|
||||
Vector2 directionChange(double param) const;
|
||||
double length() const;
|
||||
SignedDistance signedDistance(Point2 origin, double ¶m) const;
|
||||
int scanlineIntersections(double x[3], int dy[3], double y) const;
|
||||
void bound(double &l, double &b, double &r, double &t) const;
|
||||
Point2 point(real param) const;
|
||||
Vector2 direction(real param) const;
|
||||
Vector2 directionChange(real param) const;
|
||||
real length() const;
|
||||
SignedDistance signedDistance(Point2 origin, real ¶m) const;
|
||||
int scanlineIntersections(real x[3], int dy[3], real y) const;
|
||||
void bound(real &l, real &b, real &r, real &t) const;
|
||||
|
||||
void reverse();
|
||||
void moveStartPoint(Point2 to);
|
||||
|
|
@ -121,19 +122,19 @@ public:
|
|||
CubicSegment *clone() const;
|
||||
int type() const;
|
||||
const Point2 *controlPoints() const;
|
||||
Point2 point(double param) const;
|
||||
Vector2 direction(double param) const;
|
||||
Vector2 directionChange(double param) const;
|
||||
SignedDistance signedDistance(Point2 origin, double ¶m) const;
|
||||
int scanlineIntersections(double x[3], int dy[3], double y) const;
|
||||
void bound(double &l, double &b, double &r, double &t) const;
|
||||
Point2 point(real param) const;
|
||||
Vector2 direction(real param) const;
|
||||
Vector2 directionChange(real param) const;
|
||||
SignedDistance signedDistance(Point2 origin, real ¶m) const;
|
||||
int scanlineIntersections(real x[3], int dy[3], real y) const;
|
||||
void bound(real &l, real &b, real &r, real &t) const;
|
||||
|
||||
void reverse();
|
||||
void moveStartPoint(Point2 to);
|
||||
void moveEndPoint(Point2 to);
|
||||
void splitInThirds(EdgeSegment *&part1, EdgeSegment *&part2, EdgeSegment *&part3) const;
|
||||
|
||||
void deconverge(int param, double amount);
|
||||
void deconverge(int param, real amount);
|
||||
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -6,20 +6,20 @@
|
|||
|
||||
namespace msdfgen {
|
||||
|
||||
#define DISTANCE_DELTA_FACTOR 1.001
|
||||
#define DISTANCE_DELTA_FACTOR ::msdfgen::real(1.001)
|
||||
|
||||
TrueDistanceSelector::EdgeCache::EdgeCache() : absDistance(0) { }
|
||||
|
||||
void TrueDistanceSelector::reset(const Point2 &p) {
|
||||
double delta = DISTANCE_DELTA_FACTOR*(p-this->p).length();
|
||||
minDistance.distance += nonZeroSign(minDistance.distance)*delta;
|
||||
real delta = DISTANCE_DELTA_FACTOR*(p-this->p).length();
|
||||
minDistance.distance += real(nonZeroSign(minDistance.distance))*delta;
|
||||
this->p = p;
|
||||
}
|
||||
|
||||
void TrueDistanceSelector::addEdge(EdgeCache &cache, const EdgeSegment *prevEdge, const EdgeSegment *edge, const EdgeSegment *nextEdge) {
|
||||
double delta = DISTANCE_DELTA_FACTOR*(p-cache.point).length();
|
||||
real delta = DISTANCE_DELTA_FACTOR*(p-cache.point).length();
|
||||
if (cache.absDistance-delta <= fabs(minDistance.distance)) {
|
||||
double dummy;
|
||||
real dummy;
|
||||
SignedDistance distance = edge->signedDistance(p, dummy);
|
||||
if (distance < minDistance)
|
||||
minDistance = distance;
|
||||
|
|
@ -39,10 +39,10 @@ TrueDistanceSelector::DistanceType TrueDistanceSelector::distance() const {
|
|||
|
||||
PseudoDistanceSelectorBase::EdgeCache::EdgeCache() : absDistance(0), aDomainDistance(0), bDomainDistance(0), aPseudoDistance(0), bPseudoDistance(0) { }
|
||||
|
||||
bool PseudoDistanceSelectorBase::getPseudoDistance(double &distance, const Vector2 &ep, const Vector2 &edgeDir) {
|
||||
double ts = dotProduct(ep, edgeDir);
|
||||
if (ts > 0) {
|
||||
double pseudoDistance = crossProduct(ep, edgeDir);
|
||||
bool PseudoDistanceSelectorBase::getPseudoDistance(real &distance, const Vector2 &ep, const Vector2 &edgeDir) {
|
||||
real ts = dotProduct(ep, edgeDir);
|
||||
if (ts > real(0)) {
|
||||
real pseudoDistance = crossProduct(ep, edgeDir);
|
||||
if (fabs(pseudoDistance) < fabs(distance)) {
|
||||
distance = pseudoDistance;
|
||||
return true;
|
||||
|
|
@ -53,8 +53,8 @@ bool PseudoDistanceSelectorBase::getPseudoDistance(double &distance, const Vecto
|
|||
|
||||
PseudoDistanceSelectorBase::PseudoDistanceSelectorBase() : minNegativePseudoDistance(-fabs(minTrueDistance.distance)), minPositivePseudoDistance(fabs(minTrueDistance.distance)), nearEdge(NULL), nearEdgeParam(0) { }
|
||||
|
||||
void PseudoDistanceSelectorBase::reset(double delta) {
|
||||
minTrueDistance.distance += nonZeroSign(minTrueDistance.distance)*delta;
|
||||
void PseudoDistanceSelectorBase::reset(real delta) {
|
||||
minTrueDistance.distance += real(nonZeroSign(minTrueDistance.distance))*delta;
|
||||
minNegativePseudoDistance = -fabs(minTrueDistance.distance);
|
||||
minPositivePseudoDistance = fabs(minTrueDistance.distance);
|
||||
nearEdge = NULL;
|
||||
|
|
@ -62,23 +62,23 @@ void PseudoDistanceSelectorBase::reset(double delta) {
|
|||
}
|
||||
|
||||
bool PseudoDistanceSelectorBase::isEdgeRelevant(const EdgeCache &cache, const EdgeSegment *edge, const Point2 &p) const {
|
||||
double delta = DISTANCE_DELTA_FACTOR*(p-cache.point).length();
|
||||
real 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.aDomainDistance > real(0) && (cache.aPseudoDistance < real(0) ?
|
||||
cache.aPseudoDistance+delta >= minNegativePseudoDistance :
|
||||
cache.aPseudoDistance-delta <= minPositivePseudoDistance
|
||||
)) ||
|
||||
(cache.bDomainDistance > 0 && (cache.bPseudoDistance < 0 ?
|
||||
(cache.bDomainDistance > real(0) && (cache.bPseudoDistance < real(0) ?
|
||||
cache.bPseudoDistance+delta >= minNegativePseudoDistance :
|
||||
cache.bPseudoDistance-delta <= minPositivePseudoDistance
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
void PseudoDistanceSelectorBase::addEdgeTrueDistance(const EdgeSegment *edge, const SignedDistance &distance, double param) {
|
||||
void PseudoDistanceSelectorBase::addEdgeTrueDistance(const EdgeSegment *edge, const SignedDistance &distance, real param) {
|
||||
if (distance < minTrueDistance) {
|
||||
minTrueDistance = distance;
|
||||
nearEdge = edge;
|
||||
|
|
@ -86,10 +86,10 @@ void PseudoDistanceSelectorBase::addEdgeTrueDistance(const EdgeSegment *edge, co
|
|||
}
|
||||
}
|
||||
|
||||
void PseudoDistanceSelectorBase::addEdgePseudoDistance(double distance) {
|
||||
if (distance <= 0 && distance > minNegativePseudoDistance)
|
||||
void PseudoDistanceSelectorBase::addEdgePseudoDistance(real distance) {
|
||||
if (distance <= real(0) && distance > minNegativePseudoDistance)
|
||||
minNegativePseudoDistance = distance;
|
||||
if (distance >= 0 && distance < minPositivePseudoDistance)
|
||||
if (distance >= real(0) && distance < minPositivePseudoDistance)
|
||||
minPositivePseudoDistance = distance;
|
||||
}
|
||||
|
||||
|
|
@ -105,8 +105,8 @@ void PseudoDistanceSelectorBase::merge(const PseudoDistanceSelectorBase &other)
|
|||
minPositivePseudoDistance = other.minPositivePseudoDistance;
|
||||
}
|
||||
|
||||
double PseudoDistanceSelectorBase::computeDistance(const Point2 &p) const {
|
||||
double minDistance = minTrueDistance.distance < 0 ? minNegativePseudoDistance : minPositivePseudoDistance;
|
||||
real PseudoDistanceSelectorBase::computeDistance(const Point2 &p) const {
|
||||
real minDistance = minTrueDistance.distance < real(0) ? minNegativePseudoDistance : minPositivePseudoDistance;
|
||||
if (nearEdge) {
|
||||
SignedDistance distance = minTrueDistance;
|
||||
nearEdge->distanceToPseudoDistance(distance, p, nearEdgeParam);
|
||||
|
|
@ -121,14 +121,14 @@ SignedDistance PseudoDistanceSelectorBase::trueDistance() const {
|
|||
}
|
||||
|
||||
void PseudoDistanceSelector::reset(const Point2 &p) {
|
||||
double delta = DISTANCE_DELTA_FACTOR*(p-this->p).length();
|
||||
real delta = DISTANCE_DELTA_FACTOR*(p-this->p).length();
|
||||
PseudoDistanceSelectorBase::reset(delta);
|
||||
this->p = p;
|
||||
}
|
||||
|
||||
void PseudoDistanceSelector::addEdge(EdgeCache &cache, const EdgeSegment *prevEdge, const EdgeSegment *edge, const EdgeSegment *nextEdge) {
|
||||
if (isEdgeRelevant(cache, edge, p)) {
|
||||
double param;
|
||||
real param;
|
||||
SignedDistance distance = edge->signedDistance(p, param);
|
||||
addEdgeTrueDistance(edge, distance, param);
|
||||
cache.point = p;
|
||||
|
|
@ -140,16 +140,16 @@ void PseudoDistanceSelector::addEdge(EdgeCache &cache, const EdgeSegment *prevEd
|
|||
Vector2 bDir = edge->direction(1).normalize(true);
|
||||
Vector2 prevDir = prevEdge->direction(1).normalize(true);
|
||||
Vector2 nextDir = nextEdge->direction(0).normalize(true);
|
||||
double add = dotProduct(ap, (prevDir+aDir).normalize(true));
|
||||
double bdd = -dotProduct(bp, (bDir+nextDir).normalize(true));
|
||||
if (add > 0) {
|
||||
double pd = distance.distance;
|
||||
real add = dotProduct(ap, (prevDir+aDir).normalize(true));
|
||||
real bdd = -dotProduct(bp, (bDir+nextDir).normalize(true));
|
||||
if (add > real(0)) {
|
||||
real pd = distance.distance;
|
||||
if (getPseudoDistance(pd, ap, -aDir))
|
||||
addEdgePseudoDistance(pd = -pd);
|
||||
cache.aPseudoDistance = pd;
|
||||
}
|
||||
if (bdd > 0) {
|
||||
double pd = distance.distance;
|
||||
if (bdd > real(0)) {
|
||||
real pd = distance.distance;
|
||||
if (getPseudoDistance(pd, bp, bDir))
|
||||
addEdgePseudoDistance(pd);
|
||||
cache.bPseudoDistance = pd;
|
||||
|
|
@ -164,7 +164,7 @@ PseudoDistanceSelector::DistanceType PseudoDistanceSelector::distance() const {
|
|||
}
|
||||
|
||||
void MultiDistanceSelector::reset(const Point2 &p) {
|
||||
double delta = DISTANCE_DELTA_FACTOR*(p-this->p).length();
|
||||
real delta = DISTANCE_DELTA_FACTOR*(p-this->p).length();
|
||||
r.reset(delta);
|
||||
g.reset(delta);
|
||||
b.reset(delta);
|
||||
|
|
@ -177,7 +177,7 @@ void MultiDistanceSelector::addEdge(EdgeCache &cache, const EdgeSegment *prevEdg
|
|||
(edge->color&GREEN && g.isEdgeRelevant(cache, edge, p)) ||
|
||||
(edge->color&BLUE && b.isEdgeRelevant(cache, edge, p))
|
||||
) {
|
||||
double param;
|
||||
real param;
|
||||
SignedDistance distance = edge->signedDistance(p, param);
|
||||
if (edge->color&RED)
|
||||
r.addEdgeTrueDistance(edge, distance, param);
|
||||
|
|
@ -194,10 +194,10 @@ void MultiDistanceSelector::addEdge(EdgeCache &cache, const EdgeSegment *prevEdg
|
|||
Vector2 bDir = edge->direction(1).normalize(true);
|
||||
Vector2 prevDir = prevEdge->direction(1).normalize(true);
|
||||
Vector2 nextDir = nextEdge->direction(0).normalize(true);
|
||||
double add = dotProduct(ap, (prevDir+aDir).normalize(true));
|
||||
double bdd = -dotProduct(bp, (bDir+nextDir).normalize(true));
|
||||
if (add > 0) {
|
||||
double pd = distance.distance;
|
||||
real add = dotProduct(ap, (prevDir+aDir).normalize(true));
|
||||
real bdd = -dotProduct(bp, (bDir+nextDir).normalize(true));
|
||||
if (add > real(0)) {
|
||||
real pd = distance.distance;
|
||||
if (PseudoDistanceSelectorBase::getPseudoDistance(pd, ap, -aDir)) {
|
||||
pd = -pd;
|
||||
if (edge->color&RED)
|
||||
|
|
@ -209,8 +209,8 @@ void MultiDistanceSelector::addEdge(EdgeCache &cache, const EdgeSegment *prevEdg
|
|||
}
|
||||
cache.aPseudoDistance = pd;
|
||||
}
|
||||
if (bdd > 0) {
|
||||
double pd = distance.distance;
|
||||
if (bdd > real(0)) {
|
||||
real pd = distance.distance;
|
||||
if (PseudoDistanceSelectorBase::getPseudoDistance(pd, bp, bDir)) {
|
||||
if (edge->color&RED)
|
||||
r.addEdgePseudoDistance(pd);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
#include "Vector2.hpp"
|
||||
#include "SignedDistance.hpp"
|
||||
#include "edge-segments.h"
|
||||
|
|
@ -8,21 +9,21 @@
|
|||
namespace msdfgen {
|
||||
|
||||
struct MultiDistance {
|
||||
double r, g, b;
|
||||
real r, g, b;
|
||||
};
|
||||
struct MultiAndTrueDistance : MultiDistance {
|
||||
double a;
|
||||
real a;
|
||||
};
|
||||
|
||||
/// Selects the nearest edge by its true distance.
|
||||
class TrueDistanceSelector {
|
||||
|
||||
public:
|
||||
typedef double DistanceType;
|
||||
typedef real DistanceType;
|
||||
|
||||
struct EdgeCache {
|
||||
Point2 point;
|
||||
double absDistance;
|
||||
real absDistance;
|
||||
|
||||
EdgeCache();
|
||||
};
|
||||
|
|
@ -43,30 +44,30 @@ class PseudoDistanceSelectorBase {
|
|||
public:
|
||||
struct EdgeCache {
|
||||
Point2 point;
|
||||
double absDistance;
|
||||
double aDomainDistance, bDomainDistance;
|
||||
double aPseudoDistance, bPseudoDistance;
|
||||
real absDistance;
|
||||
real aDomainDistance, bDomainDistance;
|
||||
real aPseudoDistance, bPseudoDistance;
|
||||
|
||||
EdgeCache();
|
||||
};
|
||||
|
||||
static bool getPseudoDistance(double &distance, const Vector2 &ep, const Vector2 &edgeDir);
|
||||
static bool getPseudoDistance(real &distance, const Vector2 &ep, const Vector2 &edgeDir);
|
||||
|
||||
PseudoDistanceSelectorBase();
|
||||
void reset(double delta);
|
||||
void reset(real 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 addEdgeTrueDistance(const EdgeSegment *edge, const SignedDistance &distance, real param);
|
||||
void addEdgePseudoDistance(real distance);
|
||||
void merge(const PseudoDistanceSelectorBase &other);
|
||||
double computeDistance(const Point2 &p) const;
|
||||
real computeDistance(const Point2 &p) const;
|
||||
SignedDistance trueDistance() const;
|
||||
|
||||
private:
|
||||
SignedDistance minTrueDistance;
|
||||
double minNegativePseudoDistance;
|
||||
double minPositivePseudoDistance;
|
||||
real minNegativePseudoDistance;
|
||||
real minPositivePseudoDistance;
|
||||
const EdgeSegment *nearEdge;
|
||||
double nearEdgeParam;
|
||||
real nearEdgeParam;
|
||||
|
||||
};
|
||||
|
||||
|
|
@ -74,7 +75,7 @@ private:
|
|||
class PseudoDistanceSelector : public PseudoDistanceSelectorBase {
|
||||
|
||||
public:
|
||||
typedef double DistanceType;
|
||||
typedef real DistanceType;
|
||||
|
||||
void reset(const Point2 &p);
|
||||
void addEdge(EdgeCache &cache, const EdgeSegment *prevEdge, const EdgeSegment *edge, const EdgeSegment *nextEdge);
|
||||
|
|
|
|||
|
|
@ -4,70 +4,72 @@
|
|||
#define _USE_MATH_DEFINES
|
||||
#include <cmath>
|
||||
|
||||
#define LARGE_RATIO ::msdfgen::real(1e10)
|
||||
|
||||
#ifndef MSDFGEN_CUBE_ROOT
|
||||
#define MSDFGEN_CUBE_ROOT(x) pow((x), 1/3.)
|
||||
#define MSDFGEN_CUBE_ROOT(x) pow((x), ::msdfgen::real(1)/::msdfgen::real(3))
|
||||
#endif
|
||||
|
||||
namespace msdfgen {
|
||||
|
||||
int solveQuadratic(double x[2], double a, double b, double c) {
|
||||
int solveQuadratic(real x[2], real a, real b, real c) {
|
||||
// a == 0 -> linear equation
|
||||
if (a == 0 || fabs(b) > 1e12*fabs(a)) {
|
||||
if (a == real(0) || fabs(b) > LARGE_RATIO*fabs(a)) {
|
||||
// a == 0, b == 0 -> no solution
|
||||
if (b == 0) {
|
||||
if (c == 0)
|
||||
if (b == real(0)) {
|
||||
if (c == real(0))
|
||||
return -1; // 0 == 0
|
||||
return 0;
|
||||
}
|
||||
x[0] = -c/b;
|
||||
return 1;
|
||||
}
|
||||
double dscr = b*b-4*a*c;
|
||||
if (dscr > 0) {
|
||||
real dscr = b*b-real(4)*a*c;
|
||||
if (dscr > real(0)) {
|
||||
dscr = sqrt(dscr);
|
||||
x[0] = (-b+dscr)/(2*a);
|
||||
x[1] = (-b-dscr)/(2*a);
|
||||
x[0] = (-b+dscr)/(a+a);
|
||||
x[1] = (-b-dscr)/(a+a);
|
||||
return 2;
|
||||
} else if (dscr == 0) {
|
||||
x[0] = -b/(2*a);
|
||||
x[0] = -b/(a+a);
|
||||
return 1;
|
||||
} else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int solveCubicNormed(double x[3], double a, double b, double c) {
|
||||
double a2 = a*a;
|
||||
double q = 1/9.*(a2-3*b);
|
||||
double r = 1/54.*(a*(2*a2-9*b)+27*c);
|
||||
double r2 = r*r;
|
||||
double q3 = q*q*q;
|
||||
a *= 1/3.;
|
||||
static int solveCubicNormed(real x[3], real a, real b, real c) {
|
||||
real a2 = a*a;
|
||||
real q = real(1)/real(9)*(a2-real(3)*b);
|
||||
real r = real(1)/real(54)*(a*(real(2)*a2-real(9)*b)+real(27)*c);
|
||||
real r2 = r*r;
|
||||
real q3 = q*q*q;
|
||||
a *= real(1)/real(3);
|
||||
if (r2 < q3) {
|
||||
double t = r/sqrt(q3);
|
||||
if (t < -1) t = -1;
|
||||
if (t > 1) t = 1;
|
||||
t = acos(t);
|
||||
q = -2*sqrt(q);
|
||||
x[0] = q*cos(1/3.*t)-a;
|
||||
x[1] = q*cos(1/3.*(t+2*M_PI))-a;
|
||||
x[2] = q*cos(1/3.*(t-2*M_PI))-a;
|
||||
real t = r/sqrt(q3);
|
||||
if (t < real(-1)) t = -1;
|
||||
if (t > real(1)) t = 1;
|
||||
t = real(1)/real(3)*acos(t);
|
||||
q = real(-2)*sqrt(q);
|
||||
x[0] = q*cos(t)-a;
|
||||
x[1] = q*cos(t+real(2)/real(3)*real(M_PI))-a;
|
||||
x[2] = q*cos(t-real(2)/real(3)*real(M_PI))-a;
|
||||
return 3;
|
||||
} else {
|
||||
double u = (r < 0 ? 1 : -1)*MSDFGEN_CUBE_ROOT(fabs(r)+sqrt(r2-q3));
|
||||
double v = u == 0 ? 0 : q/u;
|
||||
real u = (r < real(0) ? real(1) : real(-1))*MSDFGEN_CUBE_ROOT(fabs(r)+sqrt(r2-q3));
|
||||
real v = u == real(0) ? real(0) : q/u;
|
||||
x[0] = (u+v)-a;
|
||||
if (u == v || fabs(u-v) < 1e-12*fabs(u+v)) {
|
||||
x[1] = -.5*(u+v)-a;
|
||||
if (u == v || LARGE_RATIO*fabs(u-v) < fabs(u+v)) {
|
||||
x[1] = real(-.5)*(u+v)-a;
|
||||
return 2;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
int solveCubic(double x[3], double a, double b, double c, double d) {
|
||||
int solveCubic(real x[3], real a, real b, real c, real d) {
|
||||
if (a != 0) {
|
||||
double bn = b/a;
|
||||
if (fabs(bn) < 1e6) // Above this ratio, the numerical error gets larger than if we treated a as zero
|
||||
real bn = b/a;
|
||||
if (bn*bn < LARGE_RATIO) // Above this ratio, the numerical error gets larger than if we treated a as zero
|
||||
return solveCubicNormed(x, bn, c/a, d/a);
|
||||
}
|
||||
return solveQuadratic(x, b, c, d);
|
||||
|
|
|
|||
|
|
@ -1,12 +1,14 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
|
||||
namespace msdfgen {
|
||||
|
||||
// ax^2 + bx + c = 0
|
||||
int solveQuadratic(double x[2], double a, double b, double c);
|
||||
int solveQuadratic(real x[2], real a, real b, real c);
|
||||
|
||||
// ax^3 + bx^2 + cx + d = 0
|
||||
int solveCubic(double x[3], double a, double b, double c, double d);
|
||||
int solveCubic(real x[3], real a, real b, real c, real d);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include "BitmapRef.hpp"
|
||||
#include "types.h"
|
||||
|
||||
#ifndef MSDFGEN_PUBLIC
|
||||
#define MSDFGEN_PUBLIC // for DLL import/export
|
||||
|
|
@ -13,9 +13,9 @@ namespace msdfgen {
|
|||
/// The configuration of the MSDF error correction pass.
|
||||
struct ErrorCorrectionConfig {
|
||||
/// The default value of minDeviationRatio.
|
||||
static MSDFGEN_PUBLIC const double defaultMinDeviationRatio;
|
||||
static MSDFGEN_PUBLIC const real defaultMinDeviationRatio;
|
||||
/// The default value of minImproveRatio.
|
||||
static MSDFGEN_PUBLIC const double defaultMinImproveRatio;
|
||||
static MSDFGEN_PUBLIC const real defaultMinImproveRatio;
|
||||
|
||||
/// Mode of operation.
|
||||
enum Mode {
|
||||
|
|
@ -38,13 +38,13 @@ struct ErrorCorrectionConfig {
|
|||
ALWAYS_CHECK_DISTANCE
|
||||
} distanceCheckMode;
|
||||
/// The minimum ratio between the actual and maximum expected distance delta to be considered an error.
|
||||
double minDeviationRatio;
|
||||
real minDeviationRatio;
|
||||
/// The minimum ratio between the pre-correction distance error and the post-correction distance error. Has no effect for DO_NOT_CHECK_DISTANCE.
|
||||
double minImproveRatio;
|
||||
real minImproveRatio;
|
||||
/// An optional buffer to avoid dynamic allocation. Must have at least as many bytes as the MSDF has pixels.
|
||||
byte *buffer;
|
||||
|
||||
inline explicit ErrorCorrectionConfig(Mode mode = EDGE_PRIORITY, DistanceCheckMode distanceCheckMode = CHECK_DISTANCE_AT_EDGE, double minDeviationRatio = defaultMinDeviationRatio, double minImproveRatio = defaultMinImproveRatio, byte *buffer = NULL) : mode(mode), distanceCheckMode(distanceCheckMode), minDeviationRatio(minDeviationRatio), minImproveRatio(minImproveRatio), buffer(buffer) { }
|
||||
inline explicit ErrorCorrectionConfig(Mode mode = EDGE_PRIORITY, DistanceCheckMode distanceCheckMode = CHECK_DISTANCE_AT_EDGE, real minDeviationRatio = defaultMinDeviationRatio, real minImproveRatio = defaultMinImproveRatio, byte *buffer = NULL) : mode(mode), distanceCheckMode(distanceCheckMode), minDeviationRatio(minDeviationRatio), minImproveRatio(minImproveRatio), buffer(buffer) { }
|
||||
};
|
||||
|
||||
/// The configuration of the distance field generator algorithm.
|
||||
|
|
|
|||
|
|
@ -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 Projection &projection, real range, const MSDFGeneratorConfig &config) {
|
||||
if (config.errorCorrection.mode == ErrorCorrectionConfig::DISABLED)
|
||||
return;
|
||||
Bitmap<byte, 1> stencilBuffer;
|
||||
|
|
@ -49,7 +49,7 @@ 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 Projection &projection, real range, real minDeviationRatio, bool protectAll) {
|
||||
Bitmap<byte, 1> stencilBuffer(sdf.width, sdf.height);
|
||||
MSDFErrorCorrection ec(stencilBuffer, projection, range);
|
||||
ec.setMinDeviationRatio(minDeviationRatio);
|
||||
|
|
@ -59,31 +59,31 @@ 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) {
|
||||
void msdfErrorCorrection(const BitmapRef<float, 3> &sdf, const Shape &shape, const Projection &projection, real range, const MSDFGeneratorConfig &config) {
|
||||
msdfErrorCorrectionInner(sdf, shape, projection, range, config);
|
||||
}
|
||||
void msdfErrorCorrection(const BitmapRef<float, 4> &sdf, const Shape &shape, const Projection &projection, double range, const MSDFGeneratorConfig &config) {
|
||||
void msdfErrorCorrection(const BitmapRef<float, 4> &sdf, const Shape &shape, const Projection &projection, real range, const MSDFGeneratorConfig &config) {
|
||||
msdfErrorCorrectionInner(sdf, shape, projection, range, config);
|
||||
}
|
||||
|
||||
void msdfFastDistanceErrorCorrection(const BitmapRef<float, 3> &sdf, const Projection &projection, double range, double minDeviationRatio) {
|
||||
void msdfFastDistanceErrorCorrection(const BitmapRef<float, 3> &sdf, const Projection &projection, real range, real minDeviationRatio) {
|
||||
msdfErrorCorrectionShapeless(sdf, projection, range, minDeviationRatio, false);
|
||||
}
|
||||
void msdfFastDistanceErrorCorrection(const BitmapRef<float, 4> &sdf, const Projection &projection, double range, double minDeviationRatio) {
|
||||
void msdfFastDistanceErrorCorrection(const BitmapRef<float, 4> &sdf, const Projection &projection, real range, real minDeviationRatio) {
|
||||
msdfErrorCorrectionShapeless(sdf, projection, range, minDeviationRatio, false);
|
||||
}
|
||||
|
||||
void msdfFastEdgeErrorCorrection(const BitmapRef<float, 3> &sdf, const Projection &projection, double range, double minDeviationRatio) {
|
||||
void msdfFastEdgeErrorCorrection(const BitmapRef<float, 3> &sdf, const Projection &projection, real range, real minDeviationRatio) {
|
||||
msdfErrorCorrectionShapeless(sdf, projection, range, minDeviationRatio, true);
|
||||
}
|
||||
void msdfFastEdgeErrorCorrection(const BitmapRef<float, 4> &sdf, const Projection &projection, double range, double minDeviationRatio) {
|
||||
void msdfFastEdgeErrorCorrection(const BitmapRef<float, 4> &sdf, const Projection &projection, real range, real minDeviationRatio) {
|
||||
msdfErrorCorrectionShapeless(sdf, projection, range, minDeviationRatio, true);
|
||||
}
|
||||
|
||||
|
||||
// Legacy version
|
||||
|
||||
inline static bool detectClash(const float *a, const float *b, double threshold) {
|
||||
inline static bool detectClash(const float *a, const float *b, real threshold) {
|
||||
// Sort channels so that pairs (a0, b0), (a1, b1), (a2, b2) go from biggest to smallest absolute difference
|
||||
float a0 = a[0], a1 = a[1], a2 = a[2];
|
||||
float b0 = b[0], b1 = b[1], b2 = b[2];
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
#include "Vector2.hpp"
|
||||
#include "Projection.h"
|
||||
#include "Shape.h"
|
||||
|
|
@ -10,16 +11,16 @@
|
|||
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 Projection &projection, real range, const MSDFGeneratorConfig &config = MSDFGeneratorConfig());
|
||||
void msdfErrorCorrection(const BitmapRef<float, 4> &sdf, const Shape &shape, const Projection &projection, real 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 Projection &projection, real range, real minDeviationRatio = ErrorCorrectionConfig::defaultMinDeviationRatio);
|
||||
void msdfFastDistanceErrorCorrection(const BitmapRef<float, 4> &sdf, const Projection &projection, real range, real 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 Projection &projection, real range, real minDeviationRatio = ErrorCorrectionConfig::defaultMinDeviationRatio);
|
||||
void msdfFastEdgeErrorCorrection(const BitmapRef<float, 4> &sdf, const Projection &projection, real range, real minDeviationRatio = ErrorCorrectionConfig::defaultMinDeviationRatio);
|
||||
|
||||
/// The original version of the error correction algorithm.
|
||||
void msdfErrorCorrection_legacy(const BitmapRef<float, 3> &output, const Vector2 &threshold);
|
||||
|
|
|
|||
100
core/msdfgen.cpp
100
core/msdfgen.cpp
|
|
@ -13,45 +13,45 @@ template <typename DistanceType>
|
|||
class DistancePixelConversion;
|
||||
|
||||
template <>
|
||||
class DistancePixelConversion<double> {
|
||||
double invRange;
|
||||
class DistancePixelConversion<real> {
|
||||
real invRange;
|
||||
public:
|
||||
typedef BitmapRef<float, 1> BitmapRefType;
|
||||
inline explicit DistancePixelConversion(double range) : invRange(1/range) { }
|
||||
inline void operator()(float *pixels, double distance) const {
|
||||
*pixels = float(invRange*distance+.5);
|
||||
inline explicit DistancePixelConversion(real range) : invRange(real(1)/range) { }
|
||||
inline void operator()(float *pixels, real distance) const {
|
||||
*pixels = float(invRange*distance+real(.5));
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
class DistancePixelConversion<MultiDistance> {
|
||||
double invRange;
|
||||
real invRange;
|
||||
public:
|
||||
typedef BitmapRef<float, 3> BitmapRefType;
|
||||
inline explicit DistancePixelConversion(double range) : invRange(1/range) { }
|
||||
inline explicit DistancePixelConversion(real range) : invRange(real(1)/range) { }
|
||||
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(invRange*distance.r+real(.5));
|
||||
pixels[1] = float(invRange*distance.g+real(.5));
|
||||
pixels[2] = float(invRange*distance.b+real(.5));
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
class DistancePixelConversion<MultiAndTrueDistance> {
|
||||
double invRange;
|
||||
real invRange;
|
||||
public:
|
||||
typedef BitmapRef<float, 4> BitmapRefType;
|
||||
inline explicit DistancePixelConversion(double range) : invRange(1/range) { }
|
||||
inline explicit DistancePixelConversion(real range) : invRange(real(1)/range) { }
|
||||
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(invRange*distance.r+real(.5));
|
||||
pixels[1] = float(invRange*distance.g+real(.5));
|
||||
pixels[2] = float(invRange*distance.b+real(.5));
|
||||
pixels[3] = float(invRange*distance.a+real(.5));
|
||||
}
|
||||
};
|
||||
|
||||
template <class ContourCombiner>
|
||||
void generateDistanceField(const typename DistancePixelConversion<typename ContourCombiner::DistanceType>::BitmapRefType &output, const Shape &shape, const Projection &projection, double range) {
|
||||
void generateDistanceField(const typename DistancePixelConversion<typename ContourCombiner::DistanceType>::BitmapRefType &output, const Shape &shape, const Projection &projection, real range) {
|
||||
DistancePixelConversion<typename ContourCombiner::DistanceType> distancePixelConversion(range);
|
||||
#ifdef MSDFGEN_USE_OPENMP
|
||||
#pragma omp parallel
|
||||
|
|
@ -66,7 +66,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 = projection.unproject(Point2(real(x)+real(.5), real(y)+real(.5)));
|
||||
typename ContourCombiner::DistanceType distance = distanceFinder.distance(p);
|
||||
distancePixelConversion(output(x, row), distance);
|
||||
}
|
||||
|
|
@ -75,21 +75,21 @@ 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 Projection &projection, real range, const GeneratorConfig &config) {
|
||||
if (config.overlapSupport)
|
||||
generateDistanceField<OverlappingContourCombiner<TrueDistanceSelector> >(output, shape, projection, range);
|
||||
else
|
||||
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 generatePseudoSDF(const BitmapRef<float, 1> &output, const Shape &shape, const Projection &projection, real range, const GeneratorConfig &config) {
|
||||
if (config.overlapSupport)
|
||||
generateDistanceField<OverlappingContourCombiner<PseudoDistanceSelector> >(output, shape, projection, range);
|
||||
else
|
||||
generateDistanceField<SimpleContourCombiner<PseudoDistanceSelector> >(output, shape, projection, range);
|
||||
}
|
||||
|
||||
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 Projection &projection, real range, const MSDFGeneratorConfig &config) {
|
||||
if (config.overlapSupport)
|
||||
generateDistanceField<OverlappingContourCombiner<MultiDistanceSelector> >(output, shape, projection, range);
|
||||
else
|
||||
|
|
@ -97,7 +97,7 @@ void generateMSDF(const BitmapRef<float, 3> &output, const Shape &shape, const P
|
|||
msdfErrorCorrection(output, shape, projection, range, 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 Projection &projection, real range, const MSDFGeneratorConfig &config) {
|
||||
if (config.overlapSupport)
|
||||
generateDistanceField<OverlappingContourCombiner<MultiAndTrueDistanceSelector> >(output, shape, projection, range);
|
||||
else
|
||||
|
|
@ -107,33 +107,33 @@ void generateMTSDF(const BitmapRef<float, 4> &output, const Shape &shape, const
|
|||
|
||||
// Legacy API
|
||||
|
||||
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, real range, const Vector2 &scale, const Vector2 &translate, bool overlapSupport) {
|
||||
generateSDF(output, shape, Projection(scale, translate), range, GeneratorConfig(overlapSupport));
|
||||
}
|
||||
|
||||
void generatePseudoSDF(const BitmapRef<float, 1> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, bool overlapSupport) {
|
||||
void generatePseudoSDF(const BitmapRef<float, 1> &output, const Shape &shape, real range, const Vector2 &scale, const Vector2 &translate, bool overlapSupport) {
|
||||
generatePseudoSDF(output, shape, Projection(scale, translate), range, GeneratorConfig(overlapSupport));
|
||||
}
|
||||
|
||||
void generateMSDF(const BitmapRef<float, 3> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, const ErrorCorrectionConfig &errorCorrectionConfig, bool overlapSupport) {
|
||||
void generateMSDF(const BitmapRef<float, 3> &output, const Shape &shape, real 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, real 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, real range, const Vector2 &scale, const Vector2 &translate) {
|
||||
#ifdef MSDFGEN_USE_OPENMP
|
||||
#pragma omp parallel for
|
||||
#endif
|
||||
for (int y = 0; y < output.height; ++y) {
|
||||
int row = shape.inverseYAxis ? output.height-y-1 : y;
|
||||
for (int x = 0; x < output.width; ++x) {
|
||||
double dummy;
|
||||
Point2 p = Vector2(x+.5, y+.5)/scale-translate;
|
||||
real dummy;
|
||||
Point2 p = Vector2(real(x)+real(.5), real(y)+real(.5))/scale-translate;
|
||||
SignedDistance minDistance;
|
||||
for (std::vector<Contour>::const_iterator contour = shape.contours.begin(); contour != shape.contours.end(); ++contour)
|
||||
for (std::vector<EdgeHolder>::const_iterator edge = contour->edges.begin(); edge != contour->edges.end(); ++edge) {
|
||||
|
|
@ -141,25 +141,25 @@ 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(minDistance.distance/range+real(.5));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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, real range, const Vector2 &scale, const Vector2 &translate) {
|
||||
#ifdef MSDFGEN_USE_OPENMP
|
||||
#pragma omp parallel for
|
||||
#endif
|
||||
for (int y = 0; y < output.height; ++y) {
|
||||
int row = shape.inverseYAxis ? output.height-y-1 : y;
|
||||
for (int x = 0; x < output.width; ++x) {
|
||||
Point2 p = Vector2(x+.5, y+.5)/scale-translate;
|
||||
Point2 p = Vector2(real(x)+real(.5), real(y)+real(.5))/scale-translate;
|
||||
SignedDistance minDistance;
|
||||
const EdgeHolder *nearEdge = NULL;
|
||||
double nearParam = 0;
|
||||
real nearParam = 0;
|
||||
for (std::vector<Contour>::const_iterator contour = shape.contours.begin(); contour != shape.contours.end(); ++contour)
|
||||
for (std::vector<EdgeHolder>::const_iterator edge = contour->edges.begin(); edge != contour->edges.end(); ++edge) {
|
||||
double param;
|
||||
real param;
|
||||
SignedDistance distance = (*edge)->signedDistance(p, param);
|
||||
if (distance < minDistance) {
|
||||
minDistance = distance;
|
||||
|
|
@ -169,31 +169,31 @@ void generatePseudoSDF_legacy(const BitmapRef<float, 1> &output, const Shape &sh
|
|||
}
|
||||
if (nearEdge)
|
||||
(*nearEdge)->distanceToPseudoDistance(minDistance, p, nearParam);
|
||||
*output(x, row) = float(minDistance.distance/range+.5);
|
||||
*output(x, row) = float(minDistance.distance/range+real(.5));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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, real range, const Vector2 &scale, const Vector2 &translate, ErrorCorrectionConfig errorCorrectionConfig) {
|
||||
#ifdef MSDFGEN_USE_OPENMP
|
||||
#pragma omp parallel for
|
||||
#endif
|
||||
for (int y = 0; y < output.height; ++y) {
|
||||
int row = shape.inverseYAxis ? output.height-y-1 : y;
|
||||
for (int x = 0; x < output.width; ++x) {
|
||||
Point2 p = Vector2(x+.5, y+.5)/scale-translate;
|
||||
Point2 p = Vector2(real(x)+real(.5), real(y)+real(.5))/scale-translate;
|
||||
|
||||
struct {
|
||||
SignedDistance minDistance;
|
||||
const EdgeHolder *nearEdge;
|
||||
double nearParam;
|
||||
real nearParam;
|
||||
} r, g, b;
|
||||
r.nearEdge = g.nearEdge = b.nearEdge = NULL;
|
||||
r.nearParam = g.nearParam = b.nearParam = 0;
|
||||
|
||||
for (std::vector<Contour>::const_iterator contour = shape.contours.begin(); contour != shape.contours.end(); ++contour)
|
||||
for (std::vector<EdgeHolder>::const_iterator edge = contour->edges.begin(); edge != contour->edges.end(); ++edge) {
|
||||
double param;
|
||||
real param;
|
||||
SignedDistance distance = (*edge)->signedDistance(p, param);
|
||||
if ((*edge)->color&RED && distance < r.minDistance) {
|
||||
r.minDistance = distance;
|
||||
|
|
@ -218,9 +218,9 @@ void generateMSDF_legacy(const BitmapRef<float, 3> &output, const Shape &shape,
|
|||
(*g.nearEdge)->distanceToPseudoDistance(g.minDistance, p, g.nearParam);
|
||||
if (b.nearEdge)
|
||||
(*b.nearEdge)->distanceToPseudoDistance(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(r.minDistance.distance/range+real(.5));
|
||||
output(x, row)[1] = float(g.minDistance.distance/range+real(.5));
|
||||
output(x, row)[2] = float(b.minDistance.distance/range+real(.5));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -228,27 +228,27 @@ 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, real range, const Vector2 &scale, const Vector2 &translate, ErrorCorrectionConfig errorCorrectionConfig) {
|
||||
#ifdef MSDFGEN_USE_OPENMP
|
||||
#pragma omp parallel for
|
||||
#endif
|
||||
for (int y = 0; y < output.height; ++y) {
|
||||
int row = shape.inverseYAxis ? output.height-y-1 : y;
|
||||
for (int x = 0; x < output.width; ++x) {
|
||||
Point2 p = Vector2(x+.5, y+.5)/scale-translate;
|
||||
Point2 p = Vector2(real(x)+real(.5), real(y)+real(.5))/scale-translate;
|
||||
|
||||
SignedDistance minDistance;
|
||||
struct {
|
||||
SignedDistance minDistance;
|
||||
const EdgeHolder *nearEdge;
|
||||
double nearParam;
|
||||
real nearParam;
|
||||
} r, g, b;
|
||||
r.nearEdge = g.nearEdge = b.nearEdge = NULL;
|
||||
r.nearParam = g.nearParam = b.nearParam = 0;
|
||||
|
||||
for (std::vector<Contour>::const_iterator contour = shape.contours.begin(); contour != shape.contours.end(); ++contour)
|
||||
for (std::vector<EdgeHolder>::const_iterator edge = contour->edges.begin(); edge != contour->edges.end(); ++edge) {
|
||||
double param;
|
||||
real param;
|
||||
SignedDistance distance = (*edge)->signedDistance(p, param);
|
||||
if (distance < minDistance)
|
||||
minDistance = distance;
|
||||
|
|
@ -275,10 +275,10 @@ void generateMTSDF_legacy(const BitmapRef<float, 4> &output, const Shape &shape,
|
|||
(*g.nearEdge)->distanceToPseudoDistance(g.minDistance, p, g.nearParam);
|
||||
if (b.nearEdge)
|
||||
(*b.nearEdge)->distanceToPseudoDistance(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(r.minDistance.distance/range+real(.5));
|
||||
output(x, row)[1] = float(g.minDistance.distance/range+real(.5));
|
||||
output(x, row)[2] = float(b.minDistance.distance/range+real(.5));
|
||||
output(x, row)[3] = float(minDistance.distance/range+real(.5));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,12 +1,11 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
#include "arithmetics.hpp"
|
||||
|
||||
namespace msdfgen {
|
||||
|
||||
typedef unsigned char byte;
|
||||
|
||||
inline byte pixelFloatToByte(float x) {
|
||||
return byte(clamp(256.f*x, 255.f));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,9 +10,9 @@ void rasterize(const BitmapRef<float, 1> &output, const Shape &shape, const Proj
|
|||
Scanline scanline;
|
||||
for (int y = 0; y < output.height; ++y) {
|
||||
int row = shape.inverseYAxis ? output.height-y-1 : y;
|
||||
shape.scanline(scanline, projection.unprojectY(y+.5));
|
||||
shape.scanline(scanline, projection.unprojectY(real(y)+real(.5)));
|
||||
for (int x = 0; x < output.width; ++x)
|
||||
*output(x, row) = (float) scanline.filled(projection.unprojectX(x+.5), fillRule);
|
||||
*output(x, row) = (float) scanline.filled(projection.unprojectX(real(x)+real(.5)), fillRule);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -20,9 +20,9 @@ void distanceSignCorrection(const BitmapRef<float, 1> &sdf, const Shape &shape,
|
|||
Scanline scanline;
|
||||
for (int y = 0; y < sdf.height; ++y) {
|
||||
int row = shape.inverseYAxis ? sdf.height-y-1 : y;
|
||||
shape.scanline(scanline, projection.unprojectY(y+.5));
|
||||
shape.scanline(scanline, projection.unprojectY(real(y)+real(.5)));
|
||||
for (int x = 0; x < sdf.width; ++x) {
|
||||
bool fill = scanline.filled(projection.unprojectX(x+.5), fillRule);
|
||||
bool fill = scanline.filled(projection.unprojectX(real(x)+real(.5)), fillRule);
|
||||
float &sd = *sdf(x, row);
|
||||
if ((sd > .5f) != fill)
|
||||
sd = 1.f-sd;
|
||||
|
|
@ -42,9 +42,9 @@ static void multiDistanceSignCorrection(const BitmapRef<float, N> &sdf, const Sh
|
|||
char *match = &matchMap[0];
|
||||
for (int y = 0; y < h; ++y) {
|
||||
int row = shape.inverseYAxis ? h-y-1 : y;
|
||||
shape.scanline(scanline, projection.unprojectY(y+.5));
|
||||
shape.scanline(scanline, projection.unprojectY(real(y)+real(.5)));
|
||||
for (int x = 0; x < w; ++x) {
|
||||
bool fill = scanline.filled(projection.unprojectX(x+.5), fillRule);
|
||||
bool fill = scanline.filled(projection.unprojectX(real(x)+real(.5)), fillRule);
|
||||
float *msd = sdf(x, row);
|
||||
float sd = median(msd[0], msd[1], msd[2]);
|
||||
if (sd == .5f)
|
||||
|
|
|
|||
|
|
@ -7,30 +7,30 @@
|
|||
|
||||
namespace msdfgen {
|
||||
|
||||
static float distVal(float dist, double pxRange, float midValue) {
|
||||
static float distVal(float dist, real pxRange, float midValue) {
|
||||
if (!pxRange)
|
||||
return (float) (dist > midValue);
|
||||
return (float) clamp((dist-midValue)*pxRange+.5);
|
||||
return (float) clamp((dist-midValue)*pxRange+real(.5));
|
||||
}
|
||||
|
||||
void renderSDF(const BitmapRef<float, 1> &output, const BitmapConstRef<float, 1> &sdf, double pxRange, float midValue) {
|
||||
Vector2 scale((double) sdf.width/output.width, (double) sdf.height/output.height);
|
||||
pxRange *= (double) (output.width+output.height)/(sdf.width+sdf.height);
|
||||
void renderSDF(const BitmapRef<float, 1> &output, const BitmapConstRef<float, 1> &sdf, real pxRange, float midValue) {
|
||||
Vector2 scale(real(sdf.width)/real(output.width), real(sdf.height)/real(output.height));
|
||||
pxRange *= real(output.width+output.height)/real(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));
|
||||
interpolate(&sd, sdf, scale*Point2(real(x)+real(.5), real(y)+real(.5)));
|
||||
*output(x, y) = distVal(sd, pxRange, midValue);
|
||||
}
|
||||
}
|
||||
|
||||
void renderSDF(const BitmapRef<float, 3> &output, const BitmapConstRef<float, 1> &sdf, double pxRange, float midValue) {
|
||||
Vector2 scale((double) sdf.width/output.width, (double) sdf.height/output.height);
|
||||
pxRange *= (double) (output.width+output.height)/(sdf.width+sdf.height);
|
||||
void renderSDF(const BitmapRef<float, 3> &output, const BitmapConstRef<float, 1> &sdf, real pxRange, float midValue) {
|
||||
Vector2 scale(real(sdf.width)/real(output.width), real(sdf.height)/real(output.height));
|
||||
pxRange *= real(output.width+output.height)/real(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));
|
||||
interpolate(&sd, sdf, scale*Point2(real(x)+real(.5), real(y)+real(.5)));
|
||||
float v = distVal(sd, pxRange, midValue);
|
||||
output(x, y)[0] = v;
|
||||
output(x, y)[1] = v;
|
||||
|
|
@ -38,48 +38,48 @@ void renderSDF(const BitmapRef<float, 3> &output, const BitmapConstRef<float, 1>
|
|||
}
|
||||
}
|
||||
|
||||
void renderSDF(const BitmapRef<float, 1> &output, const BitmapConstRef<float, 3> &sdf, double pxRange, float midValue) {
|
||||
Vector2 scale((double) sdf.width/output.width, (double) sdf.height/output.height);
|
||||
pxRange *= (double) (output.width+output.height)/(sdf.width+sdf.height);
|
||||
void renderSDF(const BitmapRef<float, 1> &output, const BitmapConstRef<float, 3> &sdf, real pxRange, float midValue) {
|
||||
Vector2 scale(real(sdf.width)/real(output.width), real(sdf.height)/real(output.height));
|
||||
pxRange *= real(output.width+output.height)/real(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));
|
||||
interpolate(sd, sdf, scale*Point2(real(x)+real(.5), real(y)+real(.5)));
|
||||
*output(x, y) = distVal(median(sd[0], sd[1], sd[2]), pxRange, midValue);
|
||||
}
|
||||
}
|
||||
|
||||
void renderSDF(const BitmapRef<float, 3> &output, const BitmapConstRef<float, 3> &sdf, double pxRange, float midValue) {
|
||||
Vector2 scale((double) sdf.width/output.width, (double) sdf.height/output.height);
|
||||
pxRange *= (double) (output.width+output.height)/(sdf.width+sdf.height);
|
||||
void renderSDF(const BitmapRef<float, 3> &output, const BitmapConstRef<float, 3> &sdf, real pxRange, float midValue) {
|
||||
Vector2 scale(real(sdf.width)/real(output.width), real(sdf.height)/real(output.height));
|
||||
pxRange *= real(output.width+output.height)/real(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));
|
||||
interpolate(sd, sdf, scale*Point2(real(x)+real(.5), real(y)+real(.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);
|
||||
}
|
||||
}
|
||||
|
||||
void renderSDF(const BitmapRef<float, 1> &output, const BitmapConstRef<float, 4> &sdf, double pxRange, float midValue) {
|
||||
Vector2 scale((double) sdf.width/output.width, (double) sdf.height/output.height);
|
||||
pxRange *= (double) (output.width+output.height)/(sdf.width+sdf.height);
|
||||
void renderSDF(const BitmapRef<float, 1> &output, const BitmapConstRef<float, 4> &sdf, real pxRange, float midValue) {
|
||||
Vector2 scale(real(sdf.width)/real(output.width), real(sdf.height)/real(output.height));
|
||||
pxRange *= real(output.width+output.height)/real(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));
|
||||
interpolate(sd, sdf, scale*Point2(real(x)+real(.5), real(y)+real(.5)));
|
||||
*output(x, y) = distVal(median(sd[0], sd[1], sd[2]), pxRange, midValue);
|
||||
}
|
||||
}
|
||||
|
||||
void renderSDF(const BitmapRef<float, 4> &output, const BitmapConstRef<float, 4> &sdf, double pxRange, float midValue) {
|
||||
Vector2 scale((double) sdf.width/output.width, (double) sdf.height/output.height);
|
||||
pxRange *= (double) (output.width+output.height)/(sdf.width+sdf.height);
|
||||
void renderSDF(const BitmapRef<float, 4> &output, const BitmapConstRef<float, 4> &sdf, real pxRange, float midValue) {
|
||||
Vector2 scale(real(sdf.width)/real(output.width), real(sdf.height)/real(output.height));
|
||||
pxRange *= real(output.width+output.height)/real(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));
|
||||
interpolate(sd, sdf, scale*Point2(real(x)+real(.5), real(y)+real(.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);
|
||||
|
|
|
|||
|
|
@ -1,18 +1,19 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
#include "Vector2.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, real pxRange = 0, float midValue = .5f);
|
||||
void renderSDF(const BitmapRef<float, 3> &output, const BitmapConstRef<float, 1> &sdf, real pxRange = 0, float midValue = .5f);
|
||||
void renderSDF(const BitmapRef<float, 1> &output, const BitmapConstRef<float, 3> &sdf, real pxRange = 0, float midValue = .5f);
|
||||
void renderSDF(const BitmapRef<float, 3> &output, const BitmapConstRef<float, 3> &sdf, real pxRange = 0, float midValue = .5f);
|
||||
void renderSDF(const BitmapRef<float, 1> &output, const BitmapConstRef<float, 4> &sdf, real pxRange = 0, float midValue = .5f);
|
||||
void renderSDF(const BitmapRef<float, 4> &output, const BitmapConstRef<float, 4> &sdf, real pxRange = 0, float midValue = .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);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
#include "BitmapRef.hpp"
|
||||
|
||||
namespace msdfgen {
|
||||
|
|
|
|||
|
|
@ -2,19 +2,20 @@
|
|||
#include "sdf-error-estimation.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <cfloat>
|
||||
#include "arithmetics.hpp"
|
||||
|
||||
namespace msdfgen {
|
||||
|
||||
void scanlineSDF(Scanline &line, const BitmapConstRef<float, 1> &sdf, const Projection &projection, double y, bool inverseYAxis) {
|
||||
void scanlineSDF(Scanline &line, const BitmapConstRef<float, 1> &sdf, const Projection &projection, real y, bool inverseYAxis) {
|
||||
if (!(sdf.width > 0 && sdf.height > 0))
|
||||
return line.setIntersections(std::vector<Scanline::Intersection>());
|
||||
double pixelY = clamp(projection.projectY(y)-.5, double(sdf.height-1));
|
||||
real pixelY = clamp(projection.projectY(y)-real(.5), real(sdf.height-1));
|
||||
if (inverseYAxis)
|
||||
pixelY = sdf.height-1-pixelY;
|
||||
int b = (int) floor(pixelY);
|
||||
int t = b+1;
|
||||
double bt = pixelY-b;
|
||||
real bt = pixelY-real(b);
|
||||
if (t >= sdf.height) {
|
||||
b = sdf.height-1;
|
||||
t = sdf.height-1;
|
||||
|
|
@ -24,16 +25,16 @@ void scanlineSDF(Scanline &line, const BitmapConstRef<float, 1> &sdf, const Proj
|
|||
std::vector<Scanline::Intersection> intersections;
|
||||
float lv, rv = mix(*sdf(0, b), *sdf(0, t), bt);
|
||||
if ((inside = rv > .5f)) {
|
||||
Scanline::Intersection intersection = { -1e240, 1 };
|
||||
Scanline::Intersection intersection = { -FLT_MAX, 1 };
|
||||
intersections.push_back(intersection);
|
||||
}
|
||||
for (int l = 0, r = 1; r < sdf.width; ++l, ++r) {
|
||||
lv = rv;
|
||||
rv = mix(*sdf(r, b), *sdf(r, t), bt);
|
||||
if (lv != rv) {
|
||||
double lr = double(.5f-lv)/double(rv-lv);
|
||||
if (lr >= 0 && lr <= 1) {
|
||||
Scanline::Intersection intersection = { projection.unprojectX(l+lr+.5), sign(rv-lv) };
|
||||
real lr = real(.5f-lv)/real(rv-lv);
|
||||
if (lr >= real(0) && lr <= real(1)) {
|
||||
Scanline::Intersection intersection = { projection.unprojectX(real(l)+lr+real(.5)), sign(rv-lv) };
|
||||
intersections.push_back(intersection);
|
||||
}
|
||||
}
|
||||
|
|
@ -46,15 +47,15 @@ void scanlineSDF(Scanline &line, const BitmapConstRef<float, 1> &sdf, const Proj
|
|||
}
|
||||
|
||||
template <int N>
|
||||
void scanlineMSDF(Scanline &line, const BitmapConstRef<float, N> &sdf, const Projection &projection, double y, bool inverseYAxis) {
|
||||
void scanlineMSDF(Scanline &line, const BitmapConstRef<float, N> &sdf, const Projection &projection, real y, bool inverseYAxis) {
|
||||
if (!(sdf.width > 0 && sdf.height > 0))
|
||||
return line.setIntersections(std::vector<Scanline::Intersection>());
|
||||
double pixelY = clamp(projection.projectY(y)-.5, double(sdf.height-1));
|
||||
real pixelY = clamp(projection.projectY(y)-real(.5), real(sdf.height-1));
|
||||
if (inverseYAxis)
|
||||
pixelY = sdf.height-1-pixelY;
|
||||
int b = (int) floor(pixelY);
|
||||
int t = b+1;
|
||||
double bt = pixelY-b;
|
||||
real bt = pixelY-real(b);
|
||||
if (t >= sdf.height) {
|
||||
b = sdf.height-1;
|
||||
t = sdf.height-1;
|
||||
|
|
@ -67,7 +68,7 @@ void scanlineMSDF(Scanline &line, const BitmapConstRef<float, N> &sdf, const Pro
|
|||
rv[1] = mix(sdf(0, b)[1], sdf(0, t)[1], bt);
|
||||
rv[2] = mix(sdf(0, b)[2], sdf(0, t)[2], bt);
|
||||
if ((inside = median(rv[0], rv[1], rv[2]) > .5f)) {
|
||||
Scanline::Intersection intersection = { -1e240, 1 };
|
||||
Scanline::Intersection intersection = { -FLT_MAX, 1 };
|
||||
intersections.push_back(intersection);
|
||||
}
|
||||
for (int l = 0, r = 1; r < sdf.width; ++l, ++r) {
|
||||
|
|
@ -79,15 +80,15 @@ void scanlineMSDF(Scanline &line, const BitmapConstRef<float, N> &sdf, const Pro
|
|||
int newIntersectionCount = 0;
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
if (lv[i] != rv[i]) {
|
||||
double lr = double(.5f-lv[i])/double(rv[i]-lv[i]);
|
||||
if (lr >= 0 && lr <= 1) {
|
||||
real lr = real(.5f-lv[i])/real(rv[i]-lv[i]);
|
||||
if (lr >= real(0) && lr <= real(1)) {
|
||||
float v[3] = {
|
||||
mix(lv[0], rv[0], lr),
|
||||
mix(lv[1], rv[1], lr),
|
||||
mix(lv[2], rv[2], lr)
|
||||
};
|
||||
if (median(v[0], v[1], v[2]) == v[i]) {
|
||||
newIntersections[newIntersectionCount].x = projection.unprojectX(l+lr+.5);
|
||||
newIntersections[newIntersectionCount].x = projection.unprojectX(real(l)+lr+real(.5));
|
||||
newIntersections[newIntersectionCount].direction = sign(rv[i]-lv[i]);
|
||||
++newIntersectionCount;
|
||||
}
|
||||
|
|
@ -124,68 +125,68 @@ void scanlineMSDF(Scanline &line, const BitmapConstRef<float, N> &sdf, const Pro
|
|||
#endif
|
||||
}
|
||||
|
||||
void scanlineSDF(Scanline &line, const BitmapConstRef<float, 3> &sdf, const Projection &projection, double y, bool inverseYAxis) {
|
||||
void scanlineSDF(Scanline &line, const BitmapConstRef<float, 3> &sdf, const Projection &projection, real y, bool inverseYAxis) {
|
||||
scanlineMSDF(line, sdf, projection, y, inverseYAxis);
|
||||
}
|
||||
void scanlineSDF(Scanline &line, const BitmapConstRef<float, 4> &sdf, const Projection &projection, double y, bool inverseYAxis) {
|
||||
void scanlineSDF(Scanline &line, const BitmapConstRef<float, 4> &sdf, const Projection &projection, real y, bool inverseYAxis) {
|
||||
scanlineMSDF(line, sdf, projection, y, inverseYAxis);
|
||||
}
|
||||
|
||||
template <int N>
|
||||
double estimateSDFErrorInner(const BitmapConstRef<float, N> &sdf, const Shape &shape, const Projection &projection, int scanlinesPerRow, FillRule fillRule) {
|
||||
real estimateSDFErrorInner(const BitmapConstRef<float, N> &sdf, const Shape &shape, const Projection &projection, int scanlinesPerRow, FillRule fillRule) {
|
||||
if (sdf.width <= 1 || sdf.height <= 1 || scanlinesPerRow < 1)
|
||||
return 0;
|
||||
double subRowSize = 1./scanlinesPerRow;
|
||||
double xFrom = projection.unprojectX(.5);
|
||||
double xTo = projection.unprojectX(sdf.width-.5);
|
||||
double overlapFactor = 1/(xTo-xFrom);
|
||||
double error = 0;
|
||||
real subRowSize = real(1)/real(scanlinesPerRow);
|
||||
real xFrom = projection.unprojectX(real(.5));
|
||||
real xTo = projection.unprojectX(real(sdf.width)-real(.5));
|
||||
real overlapFactor = real(1)/(xTo-xFrom);
|
||||
real error = 0;
|
||||
Scanline refScanline, sdfScanline;
|
||||
for (int row = 0; row < sdf.height-1; ++row) {
|
||||
for (int subRow = 0; subRow < scanlinesPerRow; ++subRow) {
|
||||
double bt = (subRow+.5)*subRowSize;
|
||||
double y = projection.unprojectY(row+bt+.5);
|
||||
real bt = (real(subRow)+real(.5))*subRowSize;
|
||||
real y = projection.unprojectY(real(row)+bt+real(.5));
|
||||
shape.scanline(refScanline, y);
|
||||
scanlineSDF(sdfScanline, sdf, projection, y, shape.inverseYAxis);
|
||||
error += 1-overlapFactor*Scanline::overlap(refScanline, sdfScanline, xFrom, xTo, fillRule);
|
||||
error += real(1)-overlapFactor*Scanline::overlap(refScanline, sdfScanline, xFrom, xTo, fillRule);
|
||||
}
|
||||
}
|
||||
return error/((sdf.height-1)*scanlinesPerRow);
|
||||
return error/real((sdf.height-1)*scanlinesPerRow);
|
||||
}
|
||||
|
||||
double estimateSDFError(const BitmapConstRef<float, 1> &sdf, const Shape &shape, const Projection &projection, int scanlinesPerRow, FillRule fillRule) {
|
||||
real estimateSDFError(const BitmapConstRef<float, 1> &sdf, const Shape &shape, const Projection &projection, int scanlinesPerRow, FillRule fillRule) {
|
||||
return estimateSDFErrorInner(sdf, shape, projection, scanlinesPerRow, fillRule);
|
||||
}
|
||||
double estimateSDFError(const BitmapConstRef<float, 3> &sdf, const Shape &shape, const Projection &projection, int scanlinesPerRow, FillRule fillRule) {
|
||||
real estimateSDFError(const BitmapConstRef<float, 3> &sdf, const Shape &shape, const Projection &projection, int scanlinesPerRow, FillRule fillRule) {
|
||||
return estimateSDFErrorInner(sdf, shape, projection, scanlinesPerRow, fillRule);
|
||||
}
|
||||
double estimateSDFError(const BitmapConstRef<float, 4> &sdf, const Shape &shape, const Projection &projection, int scanlinesPerRow, FillRule fillRule) {
|
||||
real estimateSDFError(const BitmapConstRef<float, 4> &sdf, const Shape &shape, const Projection &projection, int scanlinesPerRow, FillRule fillRule) {
|
||||
return estimateSDFErrorInner(sdf, shape, projection, scanlinesPerRow, fillRule);
|
||||
}
|
||||
|
||||
// Legacy API
|
||||
|
||||
void scanlineSDF(Scanline &line, const BitmapConstRef<float, 1> &sdf, const Vector2 &scale, const Vector2 &translate, bool inverseYAxis, double y) {
|
||||
void scanlineSDF(Scanline &line, const BitmapConstRef<float, 1> &sdf, const Vector2 &scale, const Vector2 &translate, bool inverseYAxis, real y) {
|
||||
scanlineSDF(line, sdf, Projection(scale, translate), y, inverseYAxis);
|
||||
}
|
||||
|
||||
void scanlineSDF(Scanline &line, const BitmapConstRef<float, 3> &sdf, const Vector2 &scale, const Vector2 &translate, bool inverseYAxis, double y) {
|
||||
void scanlineSDF(Scanline &line, const BitmapConstRef<float, 3> &sdf, const Vector2 &scale, const Vector2 &translate, bool inverseYAxis, real y) {
|
||||
scanlineSDF(line, sdf, Projection(scale, translate), y, inverseYAxis);
|
||||
}
|
||||
|
||||
void scanlineSDF(Scanline &line, const BitmapConstRef<float, 4> &sdf, const Vector2 &scale, const Vector2 &translate, bool inverseYAxis, double y) {
|
||||
void scanlineSDF(Scanline &line, const BitmapConstRef<float, 4> &sdf, const Vector2 &scale, const Vector2 &translate, bool inverseYAxis, real y) {
|
||||
scanlineSDF(line, sdf, Projection(scale, translate), y, inverseYAxis);
|
||||
}
|
||||
|
||||
double estimateSDFError(const BitmapConstRef<float, 1> &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, int scanlinesPerRow, FillRule fillRule) {
|
||||
real estimateSDFError(const BitmapConstRef<float, 1> &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, int scanlinesPerRow, FillRule fillRule) {
|
||||
return estimateSDFError(sdf, shape, Projection(scale, translate), scanlinesPerRow, fillRule);
|
||||
}
|
||||
|
||||
double estimateSDFError(const BitmapConstRef<float, 3> &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, int scanlinesPerRow, FillRule fillRule) {
|
||||
real estimateSDFError(const BitmapConstRef<float, 3> &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, int scanlinesPerRow, FillRule fillRule) {
|
||||
return estimateSDFError(sdf, shape, Projection(scale, translate), scanlinesPerRow, fillRule);
|
||||
}
|
||||
|
||||
double estimateSDFError(const BitmapConstRef<float, 4> &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, int scanlinesPerRow, FillRule fillRule) {
|
||||
real estimateSDFError(const BitmapConstRef<float, 4> &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, int scanlinesPerRow, FillRule fillRule) {
|
||||
return estimateSDFError(sdf, shape, Projection(scale, translate), scanlinesPerRow, fillRule);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
#include "Vector2.hpp"
|
||||
#include "Shape.h"
|
||||
#include "Projection.h"
|
||||
|
|
@ -10,21 +11,21 @@
|
|||
namespace msdfgen {
|
||||
|
||||
/// Analytically constructs a scanline at y evaluating fill by linear interpolation of the SDF.
|
||||
void scanlineSDF(Scanline &line, const BitmapConstRef<float, 1> &sdf, const Projection &projection, double y, bool inverseYAxis = false);
|
||||
void scanlineSDF(Scanline &line, const BitmapConstRef<float, 3> &sdf, const Projection &projection, double y, bool inverseYAxis = false);
|
||||
void scanlineSDF(Scanline &line, const BitmapConstRef<float, 4> &sdf, const Projection &projection, double y, bool inverseYAxis = false);
|
||||
void scanlineSDF(Scanline &line, const BitmapConstRef<float, 1> &sdf, const Projection &projection, real y, bool inverseYAxis = false);
|
||||
void scanlineSDF(Scanline &line, const BitmapConstRef<float, 3> &sdf, const Projection &projection, real y, bool inverseYAxis = false);
|
||||
void scanlineSDF(Scanline &line, const BitmapConstRef<float, 4> &sdf, const Projection &projection, real y, bool inverseYAxis = false);
|
||||
|
||||
/// Estimates the portion of the area that will be filled incorrectly when rendering using the SDF.
|
||||
double estimateSDFError(const BitmapConstRef<float, 1> &sdf, const Shape &shape, const Projection &projection, int scanlinesPerRow, FillRule fillRule = FILL_NONZERO);
|
||||
double estimateSDFError(const BitmapConstRef<float, 3> &sdf, const Shape &shape, const Projection &projection, int scanlinesPerRow, FillRule fillRule = FILL_NONZERO);
|
||||
double estimateSDFError(const BitmapConstRef<float, 4> &sdf, const Shape &shape, const Projection &projection, int scanlinesPerRow, FillRule fillRule = FILL_NONZERO);
|
||||
real estimateSDFError(const BitmapConstRef<float, 1> &sdf, const Shape &shape, const Projection &projection, int scanlinesPerRow, FillRule fillRule = FILL_NONZERO);
|
||||
real estimateSDFError(const BitmapConstRef<float, 3> &sdf, const Shape &shape, const Projection &projection, int scanlinesPerRow, FillRule fillRule = FILL_NONZERO);
|
||||
real estimateSDFError(const BitmapConstRef<float, 4> &sdf, const Shape &shape, const Projection &projection, int scanlinesPerRow, FillRule fillRule = FILL_NONZERO);
|
||||
|
||||
// Old version of the function API's kept for backwards compatibility
|
||||
void scanlineSDF(Scanline &line, const BitmapConstRef<float, 1> &sdf, const Vector2 &scale, const Vector2 &translate, bool inverseYAxis, double y);
|
||||
void scanlineSDF(Scanline &line, const BitmapConstRef<float, 3> &sdf, const Vector2 &scale, const Vector2 &translate, bool inverseYAxis, double y);
|
||||
void scanlineSDF(Scanline &line, const BitmapConstRef<float, 4> &sdf, const Vector2 &scale, const Vector2 &translate, bool inverseYAxis, double y);
|
||||
double estimateSDFError(const BitmapConstRef<float, 1> &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, int scanlinesPerRow, FillRule fillRule = FILL_NONZERO);
|
||||
double estimateSDFError(const BitmapConstRef<float, 3> &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, int scanlinesPerRow, FillRule fillRule = FILL_NONZERO);
|
||||
double estimateSDFError(const BitmapConstRef<float, 4> &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, int scanlinesPerRow, FillRule fillRule = FILL_NONZERO);
|
||||
void scanlineSDF(Scanline &line, const BitmapConstRef<float, 1> &sdf, const Vector2 &scale, const Vector2 &translate, bool inverseYAxis, real y);
|
||||
void scanlineSDF(Scanline &line, const BitmapConstRef<float, 3> &sdf, const Vector2 &scale, const Vector2 &translate, bool inverseYAxis, real y);
|
||||
void scanlineSDF(Scanline &line, const BitmapConstRef<float, 4> &sdf, const Vector2 &scale, const Vector2 &translate, bool inverseYAxis, real y);
|
||||
real estimateSDFError(const BitmapConstRef<float, 1> &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, int scanlinesPerRow, FillRule fillRule = FILL_NONZERO);
|
||||
real estimateSDFError(const BitmapConstRef<float, 3> &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, int scanlinesPerRow, FillRule fillRule = FILL_NONZERO);
|
||||
real estimateSDFError(const BitmapConstRef<float, 4> &sdf, const Shape &shape, const Vector2 &scale, const Vector2 &translate, int scanlinesPerRow, FillRule fillRule = FILL_NONZERO);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,18 +25,23 @@ int readCharS(const char **input) {
|
|||
}
|
||||
|
||||
int readCoordF(FILE *input, Point2 &coord) {
|
||||
return fscanf(input, "%lf,%lf", &coord.x, &coord.y);
|
||||
double x = 0, y = 0;
|
||||
int n = fscanf(input, "%lf,%lf", &x, &y);
|
||||
coord = Point2(x, y);
|
||||
return n;
|
||||
}
|
||||
|
||||
int readCoordS(const char **input, Point2 &coord) {
|
||||
double x = 0, y = 0;
|
||||
int read = 0;
|
||||
int result = sscanf(*input, "%lf,%lf%n", &coord.x, &coord.y, &read);
|
||||
int n = sscanf(*input, "%lf,%lf%n", &x, &y, &read);
|
||||
coord = Point2(x, y);
|
||||
*input += read;
|
||||
return result;
|
||||
return n;
|
||||
}
|
||||
|
||||
static bool writeCoord(FILE *output, Point2 coord) {
|
||||
fprintf(output, "%.12g, %.12g", coord.x, coord.y);
|
||||
fprintf(output, "%.12g, %.12g", double(coord.x), double(coord.y));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,15 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
namespace msdfgen {
|
||||
|
||||
typedef unsigned char byte;
|
||||
typedef unsigned unicode_t;
|
||||
|
||||
#ifdef MSDFGEN_REAL
|
||||
typedef MSDFGEN_REAL real;
|
||||
#else
|
||||
typedef double real;
|
||||
#endif
|
||||
|
||||
}
|
||||
|
|
@ -12,9 +12,9 @@
|
|||
|
||||
namespace msdfgen {
|
||||
|
||||
#define F26DOT6_TO_DOUBLE(x) (1/64.*double(x))
|
||||
#define F16DOT16_TO_DOUBLE(x) (1/65536.*double(x))
|
||||
#define DOUBLE_TO_F16DOT16(x) FT_Fixed(65536.*x)
|
||||
#define F26DOT6_TO_REAL(x) (::msdfgen::real(1)/::msdfgen::real(64)*::msdfgen::real(x))
|
||||
#define F16DOT16_TO_REAL(x) (::msdfgen::real(1)/::msdfgen::real(65536)*::msdfgen::real(x))
|
||||
#define REAL_TO_F16DOT16(x) FT_Fixed(::msdfgen::real(65536)*::msdfgen::real(x))
|
||||
|
||||
class FreetypeHandle {
|
||||
friend FreetypeHandle *initializeFreetype();
|
||||
|
|
@ -22,7 +22,7 @@ class FreetypeHandle {
|
|||
friend FontHandle *loadFont(FreetypeHandle *library, const char *filename);
|
||||
friend FontHandle *loadFontData(FreetypeHandle *library, const byte *data, int length);
|
||||
#ifndef MSDFGEN_DISABLE_VARIABLE_FONTS
|
||||
friend bool setFontVariationAxis(FreetypeHandle *library, FontHandle *font, const char *name, double coordinate);
|
||||
friend bool setFontVariationAxis(FreetypeHandle *library, FontHandle *font, const char *name, real coordinate);
|
||||
friend bool listFontVariationAxes(std::vector<FontVariationAxis> &axes, FreetypeHandle *library, FontHandle *font);
|
||||
#endif
|
||||
|
||||
|
|
@ -36,14 +36,14 @@ class FontHandle {
|
|||
friend FontHandle *loadFontData(FreetypeHandle *library, const byte *data, int length);
|
||||
friend void destroyFont(FontHandle *font);
|
||||
friend bool getFontMetrics(FontMetrics &metrics, FontHandle *font);
|
||||
friend bool getFontWhitespaceWidth(double &spaceAdvance, double &tabAdvance, FontHandle *font);
|
||||
friend bool getFontWhitespaceWidth(real &spaceAdvance, real &tabAdvance, FontHandle *font);
|
||||
friend bool getGlyphIndex(GlyphIndex &glyphIndex, FontHandle *font, unicode_t unicode);
|
||||
friend bool loadGlyph(Shape &output, FontHandle *font, GlyphIndex glyphIndex, double *advance);
|
||||
friend bool loadGlyph(Shape &output, FontHandle *font, unicode_t unicode, double *advance);
|
||||
friend bool getKerning(double &output, FontHandle *font, GlyphIndex glyphIndex1, GlyphIndex glyphIndex2);
|
||||
friend bool getKerning(double &output, FontHandle *font, unicode_t unicode1, unicode_t unicode2);
|
||||
friend bool loadGlyph(Shape &output, FontHandle *font, GlyphIndex glyphIndex, real *advance);
|
||||
friend bool loadGlyph(Shape &output, FontHandle *font, unicode_t unicode, real *advance);
|
||||
friend bool getKerning(real &output, FontHandle *font, GlyphIndex glyphIndex1, GlyphIndex glyphIndex2);
|
||||
friend bool getKerning(real &output, FontHandle *font, unicode_t unicode1, unicode_t unicode2);
|
||||
#ifndef MSDFGEN_DISABLE_VARIABLE_FONTS
|
||||
friend bool setFontVariationAxis(FreetypeHandle *library, FontHandle *font, const char *name, double coordinate);
|
||||
friend bool setFontVariationAxis(FreetypeHandle *library, FontHandle *font, const char *name, real coordinate);
|
||||
friend bool listFontVariationAxes(std::vector<FontVariationAxis> &axes, FreetypeHandle *library, FontHandle *font);
|
||||
#endif
|
||||
|
||||
|
|
@ -59,7 +59,7 @@ struct FtContext {
|
|||
};
|
||||
|
||||
static Point2 ftPoint2(const FT_Vector &vector) {
|
||||
return Point2(F26DOT6_TO_DOUBLE(vector.x), F26DOT6_TO_DOUBLE(vector.y));
|
||||
return Point2(F26DOT6_TO_REAL(vector.x), F26DOT6_TO_REAL(vector.y));
|
||||
}
|
||||
|
||||
static int ftMoveTo(const FT_Vector *to, void *user) {
|
||||
|
|
@ -173,24 +173,24 @@ void destroyFont(FontHandle *font) {
|
|||
}
|
||||
|
||||
bool getFontMetrics(FontMetrics &metrics, FontHandle *font) {
|
||||
metrics.emSize = F26DOT6_TO_DOUBLE(font->face->units_per_EM);
|
||||
metrics.ascenderY = F26DOT6_TO_DOUBLE(font->face->ascender);
|
||||
metrics.descenderY = F26DOT6_TO_DOUBLE(font->face->descender);
|
||||
metrics.lineHeight = F26DOT6_TO_DOUBLE(font->face->height);
|
||||
metrics.underlineY = F26DOT6_TO_DOUBLE(font->face->underline_position);
|
||||
metrics.underlineThickness = F26DOT6_TO_DOUBLE(font->face->underline_thickness);
|
||||
metrics.emSize = F26DOT6_TO_REAL(font->face->units_per_EM);
|
||||
metrics.ascenderY = F26DOT6_TO_REAL(font->face->ascender);
|
||||
metrics.descenderY = F26DOT6_TO_REAL(font->face->descender);
|
||||
metrics.lineHeight = F26DOT6_TO_REAL(font->face->height);
|
||||
metrics.underlineY = F26DOT6_TO_REAL(font->face->underline_position);
|
||||
metrics.underlineThickness = F26DOT6_TO_REAL(font->face->underline_thickness);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool getFontWhitespaceWidth(double &spaceAdvance, double &tabAdvance, FontHandle *font) {
|
||||
bool getFontWhitespaceWidth(real &spaceAdvance, real &tabAdvance, FontHandle *font) {
|
||||
FT_Error error = FT_Load_Char(font->face, ' ', FT_LOAD_NO_SCALE);
|
||||
if (error)
|
||||
return false;
|
||||
spaceAdvance = F26DOT6_TO_DOUBLE(font->face->glyph->advance.x);
|
||||
spaceAdvance = F26DOT6_TO_REAL(font->face->glyph->advance.x);
|
||||
error = FT_Load_Char(font->face, '\t', FT_LOAD_NO_SCALE);
|
||||
if (error)
|
||||
return false;
|
||||
tabAdvance = F26DOT6_TO_DOUBLE(font->face->glyph->advance.x);
|
||||
tabAdvance = F26DOT6_TO_REAL(font->face->glyph->advance.x);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -199,38 +199,38 @@ bool getGlyphIndex(GlyphIndex &glyphIndex, FontHandle *font, unicode_t unicode)
|
|||
return glyphIndex.getIndex() != 0;
|
||||
}
|
||||
|
||||
bool loadGlyph(Shape &output, FontHandle *font, GlyphIndex glyphIndex, double *advance) {
|
||||
bool loadGlyph(Shape &output, FontHandle *font, GlyphIndex glyphIndex, real *advance) {
|
||||
if (!font)
|
||||
return false;
|
||||
FT_Error error = FT_Load_Glyph(font->face, glyphIndex.getIndex(), FT_LOAD_NO_SCALE);
|
||||
if (error)
|
||||
return false;
|
||||
if (advance)
|
||||
*advance = F26DOT6_TO_DOUBLE(font->face->glyph->advance.x);
|
||||
*advance = F26DOT6_TO_REAL(font->face->glyph->advance.x);
|
||||
return !readFreetypeOutline(output, &font->face->glyph->outline);
|
||||
}
|
||||
|
||||
bool loadGlyph(Shape &output, FontHandle *font, unicode_t unicode, double *advance) {
|
||||
bool loadGlyph(Shape &output, FontHandle *font, unicode_t unicode, real *advance) {
|
||||
return loadGlyph(output, font, GlyphIndex(FT_Get_Char_Index(font->face, unicode)), advance);
|
||||
}
|
||||
|
||||
bool getKerning(double &output, FontHandle *font, GlyphIndex glyphIndex1, GlyphIndex glyphIndex2) {
|
||||
bool getKerning(real &output, FontHandle *font, GlyphIndex glyphIndex1, GlyphIndex glyphIndex2) {
|
||||
FT_Vector kerning;
|
||||
if (FT_Get_Kerning(font->face, glyphIndex1.getIndex(), glyphIndex2.getIndex(), FT_KERNING_UNSCALED, &kerning)) {
|
||||
output = 0;
|
||||
return false;
|
||||
}
|
||||
output = F26DOT6_TO_DOUBLE(kerning.x);
|
||||
output = F26DOT6_TO_REAL(kerning.x);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool getKerning(double &output, FontHandle *font, unicode_t unicode1, unicode_t unicode2) {
|
||||
bool getKerning(real &output, FontHandle *font, unicode_t unicode1, unicode_t unicode2) {
|
||||
return getKerning(output, font, GlyphIndex(FT_Get_Char_Index(font->face, unicode1)), GlyphIndex(FT_Get_Char_Index(font->face, unicode2)));
|
||||
}
|
||||
|
||||
#ifndef MSDFGEN_DISABLE_VARIABLE_FONTS
|
||||
|
||||
bool setFontVariationAxis(FreetypeHandle *library, FontHandle *font, const char *name, double coordinate) {
|
||||
bool setFontVariationAxis(FreetypeHandle *library, FontHandle *font, const char *name, real coordinate) {
|
||||
bool success = false;
|
||||
if (font->face->face_flags&FT_FACE_FLAG_MULTIPLE_MASTERS) {
|
||||
FT_MM_Var *master = NULL;
|
||||
|
|
@ -241,7 +241,7 @@ bool setFontVariationAxis(FreetypeHandle *library, FontHandle *font, const char
|
|||
if (!FT_Get_Var_Design_Coordinates(font->face, FT_UInt(coords.size()), &coords[0])) {
|
||||
for (FT_UInt i = 0; i < master->num_axis; ++i) {
|
||||
if (!strcmp(name, master->axis[i].name)) {
|
||||
coords[i] = DOUBLE_TO_F16DOT16(coordinate);
|
||||
coords[i] = REAL_TO_F16DOT16(coordinate);
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
|
|
@ -264,9 +264,9 @@ bool listFontVariationAxes(std::vector<FontVariationAxis> &axes, FreetypeHandle
|
|||
for (FT_UInt i = 0; i < master->num_axis; ++i) {
|
||||
FontVariationAxis &axis = axes[i];
|
||||
axis.name = master->axis[i].name;
|
||||
axis.minValue = F16DOT16_TO_DOUBLE(master->axis[i].minimum);
|
||||
axis.maxValue = F16DOT16_TO_DOUBLE(master->axis[i].maximum);
|
||||
axis.defaultValue = F16DOT16_TO_DOUBLE(master->axis[i].def);
|
||||
axis.minValue = F16DOT16_TO_REAL(master->axis[i].minimum);
|
||||
axis.maxValue = F16DOT16_TO_REAL(master->axis[i].maximum);
|
||||
axis.defaultValue = F16DOT16_TO_REAL(master->axis[i].def);
|
||||
}
|
||||
FT_Done_MM_Var(library->library, master);
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -2,13 +2,11 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include "../core/types.h"
|
||||
#include "../core/Shape.h"
|
||||
|
||||
namespace msdfgen {
|
||||
|
||||
typedef unsigned char byte;
|
||||
typedef unsigned unicode_t;
|
||||
|
||||
class FreetypeHandle;
|
||||
class FontHandle;
|
||||
|
||||
|
|
@ -26,13 +24,13 @@ private:
|
|||
/// Global metrics of a typeface (in font units).
|
||||
struct FontMetrics {
|
||||
/// The size of one EM.
|
||||
double emSize;
|
||||
real emSize;
|
||||
/// The vertical position of the ascender and descender relative to the baseline.
|
||||
double ascenderY, descenderY;
|
||||
real ascenderY, descenderY;
|
||||
/// The vertical difference between consecutive baselines.
|
||||
double lineHeight;
|
||||
real lineHeight;
|
||||
/// The vertical position and thickness of the underline.
|
||||
double underlineY, underlineThickness;
|
||||
real underlineY, underlineThickness;
|
||||
};
|
||||
|
||||
/// A structure to model a given axis of a variable font.
|
||||
|
|
@ -40,11 +38,11 @@ struct FontVariationAxis {
|
|||
/// The name of the variation axis.
|
||||
const char *name;
|
||||
/// The axis's minimum coordinate value.
|
||||
double minValue;
|
||||
real minValue;
|
||||
/// The axis's maximum coordinate value.
|
||||
double maxValue;
|
||||
real maxValue;
|
||||
/// The axis's default coordinate value. FreeType computes meaningful default values for Adobe MM fonts.
|
||||
double defaultValue;
|
||||
real defaultValue;
|
||||
};
|
||||
|
||||
/// Initializes the FreeType library.
|
||||
|
|
@ -68,19 +66,19 @@ void destroyFont(FontHandle *font);
|
|||
/// Outputs the metrics of a font file.
|
||||
bool getFontMetrics(FontMetrics &metrics, FontHandle *font);
|
||||
/// Outputs the width of the space and tab characters.
|
||||
bool getFontWhitespaceWidth(double &spaceAdvance, double &tabAdvance, FontHandle *font);
|
||||
bool getFontWhitespaceWidth(real &spaceAdvance, real &tabAdvance, FontHandle *font);
|
||||
/// Outputs the glyph index corresponding to the specified Unicode character.
|
||||
bool getGlyphIndex(GlyphIndex &glyphIndex, FontHandle *font, unicode_t unicode);
|
||||
/// Loads the geometry of a glyph from a font file.
|
||||
bool loadGlyph(Shape &output, FontHandle *font, GlyphIndex glyphIndex, double *advance = NULL);
|
||||
bool loadGlyph(Shape &output, FontHandle *font, unicode_t unicode, double *advance = NULL);
|
||||
bool loadGlyph(Shape &output, FontHandle *font, GlyphIndex glyphIndex, real *advance = NULL);
|
||||
bool loadGlyph(Shape &output, FontHandle *font, unicode_t unicode, real *advance = NULL);
|
||||
/// Outputs the kerning distance adjustment between two specific glyphs.
|
||||
bool getKerning(double &output, FontHandle *font, GlyphIndex glyphIndex1, GlyphIndex glyphIndex2);
|
||||
bool getKerning(double &output, FontHandle *font, unicode_t unicode1, unicode_t unicode2);
|
||||
bool getKerning(real &output, FontHandle *font, GlyphIndex glyphIndex1, GlyphIndex glyphIndex2);
|
||||
bool getKerning(real &output, FontHandle *font, unicode_t unicode1, unicode_t unicode2);
|
||||
|
||||
#ifndef MSDFGEN_DISABLE_VARIABLE_FONTS
|
||||
/// Sets a single variation axis of a variable font.
|
||||
bool setFontVariationAxis(FreetypeHandle *library, FontHandle *font, const char *name, double coordinate);
|
||||
bool setFontVariationAxis(FreetypeHandle *library, FontHandle *font, const char *name, real coordinate);
|
||||
/// Lists names and ranges of variation axes of a variable font.
|
||||
bool listFontVariationAxes(std::vector<FontVariationAxis> &axes, FreetypeHandle *library, FontHandle *font);
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@
|
|||
#include "../core/arithmetics.hpp"
|
||||
|
||||
#define ARC_SEGMENTS_PER_PI 2
|
||||
#define ENDPOINT_SNAP_RANGE_PROPORTION (1/16384.)
|
||||
#define ENDPOINT_SNAP_RANGE_PROPORTION (::msdfgen::real(1)/::msdfgen::real(16384))
|
||||
|
||||
namespace msdfgen {
|
||||
|
||||
|
|
@ -65,7 +65,7 @@ static bool readCoord(Point2 &output, const char *&pathDef) {
|
|||
return false;
|
||||
}
|
||||
|
||||
static bool readDouble(double &output, const char *&pathDef) {
|
||||
static bool readReal(real &output, const char *&pathDef) {
|
||||
skipExtraChars(pathDef);
|
||||
int shift;
|
||||
double v;
|
||||
|
|
@ -89,51 +89,51 @@ static bool readBool(bool &output, const char *&pathDef) {
|
|||
return false;
|
||||
}
|
||||
|
||||
static double arcAngle(Vector2 u, Vector2 v) {
|
||||
return nonZeroSign(crossProduct(u, v))*acos(clamp(dotProduct(u, v)/(u.length()*v.length()), -1., +1.));
|
||||
static real arcAngle(Vector2 u, Vector2 v) {
|
||||
return real(nonZeroSign(crossProduct(u, v)))*acos(clamp(dotProduct(u, v)/(u.length()*v.length()), real(-1), real(+1)));
|
||||
}
|
||||
|
||||
static Vector2 rotateVector(Vector2 v, Vector2 direction) {
|
||||
return Vector2(direction.x*v.x-direction.y*v.y, direction.y*v.x+direction.x*v.y);
|
||||
}
|
||||
|
||||
static void addArcApproximate(Contour &contour, Point2 startPoint, Point2 endPoint, Vector2 radius, double rotation, bool largeArc, bool sweep) {
|
||||
static void addArcApproximate(Contour &contour, Point2 startPoint, Point2 endPoint, Vector2 radius, real rotation, bool largeArc, bool sweep) {
|
||||
if (endPoint == startPoint)
|
||||
return;
|
||||
if (radius.x == 0 || radius.y == 0)
|
||||
if (radius.x == real(0) || radius.y == real(0))
|
||||
return contour.addEdge(new LinearSegment(startPoint, endPoint));
|
||||
|
||||
radius.x = fabs(radius.x);
|
||||
radius.y = fabs(radius.y);
|
||||
Vector2 axis(cos(rotation), sin(rotation));
|
||||
|
||||
Vector2 rm = rotateVector(.5*(startPoint-endPoint), Vector2(axis.x, -axis.y));
|
||||
Vector2 rm = rotateVector(real(.5)*(startPoint-endPoint), Vector2(axis.x, -axis.y));
|
||||
Vector2 rm2 = rm*rm;
|
||||
Vector2 radius2 = radius*radius;
|
||||
double radiusGap = rm2.x/radius2.x+rm2.y/radius2.y;
|
||||
if (radiusGap > 1) {
|
||||
real radiusGap = rm2.x/radius2.x+rm2.y/radius2.y;
|
||||
if (radiusGap > real(1)) {
|
||||
radius *= sqrt(radiusGap);
|
||||
radius2 = radius*radius;
|
||||
}
|
||||
double dq = (radius2.x*rm2.y+radius2.y*rm2.x);
|
||||
double pq = radius2.x*radius2.y/dq-1;
|
||||
double q = (largeArc == sweep ? -1 : +1)*sqrt(max(pq, 0.));
|
||||
real dq = (radius2.x*rm2.y+radius2.y*rm2.x);
|
||||
real pq = radius2.x*radius2.y/dq-1;
|
||||
real q = (largeArc == sweep ? real(-1) : real(+1))*sqrt(max(pq, real(0)));
|
||||
Vector2 rc(q*radius.x*rm.y/radius.y, -q*radius.y*rm.x/radius.x);
|
||||
Point2 center = .5*(startPoint+endPoint)+rotateVector(rc, axis);
|
||||
Point2 center = real(.5)*(startPoint+endPoint)+rotateVector(rc, axis);
|
||||
|
||||
double angleStart = arcAngle(Vector2(1, 0), (rm-rc)/radius);
|
||||
double angleExtent = arcAngle((rm-rc)/radius, (-rm-rc)/radius);
|
||||
if (!sweep && angleExtent > 0)
|
||||
angleExtent -= 2*M_PI;
|
||||
else if (sweep && angleExtent < 0)
|
||||
angleExtent += 2*M_PI;
|
||||
real angleStart = arcAngle(Vector2(1, 0), (rm-rc)/radius);
|
||||
real angleExtent = arcAngle((rm-rc)/radius, (-rm-rc)/radius);
|
||||
if (!sweep && angleExtent > real(0))
|
||||
angleExtent -= real(2*M_PI);
|
||||
else if (sweep && angleExtent < real(0))
|
||||
angleExtent += real(2*M_PI);
|
||||
|
||||
int segments = (int) ceil(ARC_SEGMENTS_PER_PI/M_PI*fabs(angleExtent));
|
||||
double angleIncrement = angleExtent/segments;
|
||||
double cl = 4/3.*sin(.5*angleIncrement)/(1+cos(.5*angleIncrement));
|
||||
int segments = (int) ceil(real(ARC_SEGMENTS_PER_PI)/real(M_PI)*fabs(angleExtent));
|
||||
real angleIncrement = angleExtent/segments;
|
||||
real cl = real(4)/real(3)*sin(real(.5)*angleIncrement)/(real(1)+cos(real(.5)*angleIncrement));
|
||||
|
||||
Point2 prevNode = startPoint;
|
||||
double angle = angleStart;
|
||||
real angle = angleStart;
|
||||
for (int i = 0; i < segments; ++i) {
|
||||
Point2 controlPoint[2];
|
||||
Vector2 d(cos(angle), sin(angle));
|
||||
|
|
@ -187,7 +187,7 @@ static void findPathByBackwardIndex(tinyxml2::XMLElement *&path, int &flags, int
|
|||
}
|
||||
}
|
||||
|
||||
bool buildShapeFromSvgPath(Shape &shape, const char *pathDef, double endpointSnapRange) {
|
||||
bool buildShapeFromSvgPath(Shape &shape, const char *pathDef, real endpointSnapRange) {
|
||||
char nodeType = '\0';
|
||||
char prevNodeType = '\0';
|
||||
Point2 prevNode(0, 0);
|
||||
|
|
@ -224,13 +224,13 @@ bool buildShapeFromSvgPath(Shape &shape, const char *pathDef, double endpointSna
|
|||
contour.addEdge(new LinearSegment(prevNode, node));
|
||||
break;
|
||||
case 'H': case 'h':
|
||||
REQUIRE(readDouble(node.x, pathDef));
|
||||
REQUIRE(readReal(node.x, pathDef));
|
||||
if (nodeType == 'h')
|
||||
node.x += prevNode.x;
|
||||
contour.addEdge(new LinearSegment(prevNode, node));
|
||||
break;
|
||||
case 'V': case 'v':
|
||||
REQUIRE(readDouble(node.y, pathDef));
|
||||
REQUIRE(readReal(node.y, pathDef));
|
||||
if (nodeType == 'v')
|
||||
node.y += prevNode.y;
|
||||
contour.addEdge(new LinearSegment(prevNode, node));
|
||||
|
|
@ -281,17 +281,17 @@ bool buildShapeFromSvgPath(Shape &shape, const char *pathDef, double endpointSna
|
|||
case 'A': case 'a':
|
||||
{
|
||||
Vector2 radius;
|
||||
double angle;
|
||||
real angle;
|
||||
bool largeArg;
|
||||
bool sweep;
|
||||
REQUIRE(readCoord(radius, pathDef));
|
||||
REQUIRE(readDouble(angle, pathDef));
|
||||
REQUIRE(readReal(angle, pathDef));
|
||||
REQUIRE(readBool(largeArg, pathDef));
|
||||
REQUIRE(readBool(sweep, pathDef));
|
||||
REQUIRE(readCoord(node, pathDef));
|
||||
if (nodeType == 'a')
|
||||
node += prevNode;
|
||||
angle *= M_PI/180.0;
|
||||
angle *= real(M_PI)/real(180);
|
||||
addArcApproximate(contour, prevNode, node, radius, angle, largeArg, sweep);
|
||||
}
|
||||
break;
|
||||
|
|
@ -338,16 +338,16 @@ bool loadSvgShape(Shape &output, const char *filename, int pathIndex, Vector2 *d
|
|||
if (!pd)
|
||||
return false;
|
||||
|
||||
Vector2 dims(root->DoubleAttribute("width"), root->DoubleAttribute("height"));
|
||||
double left, top;
|
||||
double left, bottom;
|
||||
double width = root->DoubleAttribute("width"), height = root->DoubleAttribute("height");
|
||||
const char *viewBox = root->Attribute("viewBox");
|
||||
if (viewBox)
|
||||
sscanf(viewBox, "%lf %lf %lf %lf", &left, &top, &dims.x, &dims.y);
|
||||
(void) sscanf(viewBox, "%lf %lf %lf %lf", &left, &bottom, &width, &height);
|
||||
if (dimensions)
|
||||
*dimensions = dims;
|
||||
*dimensions = Vector2(width, height);
|
||||
output.contours.clear();
|
||||
output.inverseYAxis = true;
|
||||
return buildShapeFromSvgPath(output, pd, ENDPOINT_SNAP_RANGE_PROPORTION*dims.length());
|
||||
return buildShapeFromSvgPath(output, pd, ENDPOINT_SNAP_RANGE_PROPORTION*Vector2(width, height).length());
|
||||
}
|
||||
|
||||
#ifndef MSDFGEN_USE_SKIA
|
||||
|
|
@ -370,16 +370,18 @@ int loadSvgShape(Shape &output, Shape::Bounds &viewBox, const char *filename) {
|
|||
if (!pd)
|
||||
return SVG_IMPORT_FAILURE;
|
||||
|
||||
viewBox.l = 0, viewBox.b = 0;
|
||||
Vector2 dims(root->DoubleAttribute("width"), root->DoubleAttribute("height"));
|
||||
double left = 0, bottom = 0;
|
||||
double width = root->DoubleAttribute("width"), height = root->DoubleAttribute("height");
|
||||
const char *viewBoxStr = root->Attribute("viewBox");
|
||||
if (viewBoxStr)
|
||||
sscanf(viewBoxStr, "%lf %lf %lf %lf", &viewBox.l, &viewBox.b, &dims.x, &dims.y);
|
||||
viewBox.r = viewBox.l+dims.x;
|
||||
viewBox.t = viewBox.b+dims.y;
|
||||
(void) sscanf(viewBoxStr, "%lf %lf %lf %lf", &left, &bottom, &width, &height);
|
||||
viewBox.l = left;
|
||||
viewBox.b = bottom;
|
||||
viewBox.r = left+width;
|
||||
viewBox.t = bottom+height;
|
||||
output.contours.clear();
|
||||
output.inverseYAxis = true;
|
||||
if (!buildShapeFromSvgPath(output, pd, ENDPOINT_SNAP_RANGE_PROPORTION*dims.length()))
|
||||
if (!buildShapeFromSvgPath(output, pd, ENDPOINT_SNAP_RANGE_PROPORTION*Vector2(width, height).length()))
|
||||
return SVG_IMPORT_FAILURE;
|
||||
return flags;
|
||||
}
|
||||
|
|
@ -397,8 +399,8 @@ static bool readTransformationOp(SkScalar dst[6], int &count, const char *&str,
|
|||
skipExtraChars(++curStr);
|
||||
count = 0;
|
||||
while (*curStr && *curStr != ')') {
|
||||
double x;
|
||||
if (!(count < 6 && readDouble(x, curStr)))
|
||||
real x;
|
||||
if (!(count < 6 && readReal(x, curStr)))
|
||||
return false;
|
||||
dst[count++] = SkScalar(x);
|
||||
skipExtraChars(curStr);
|
||||
|
|
@ -435,9 +437,9 @@ static SkMatrix parseTransformation(int &flags, const char *str) {
|
|||
else
|
||||
partial.setRotate(values[0]);
|
||||
} else if (readTransformationOp(values, count, str, "skewX") && count == 1) {
|
||||
partial.setSkewX(SkScalar(tan(M_PI/180*values[0])));
|
||||
partial.setSkewX(tan(SkScalar(M_PI)/SkScalar(180)*values[0]));
|
||||
} else if (readTransformationOp(values, count, str, "skewY") && count == 1) {
|
||||
partial.setSkewY(SkScalar(tan(M_PI/180*values[0])));
|
||||
partial.setSkewY(tan(SkScalar(M_PI)/SkScalar(180)*values[0]));
|
||||
} else {
|
||||
flags |= SVG_IMPORT_PARTIAL_FAILURE_FLAG;
|
||||
break;
|
||||
|
|
@ -545,13 +547,15 @@ int loadSvgShape(Shape &output, Shape::Bounds &viewBox, const char *filename) {
|
|||
output.inverseYAxis = true;
|
||||
output.orientContours();
|
||||
|
||||
viewBox.l = 0, viewBox.b = 0;
|
||||
Vector2 dims(root->DoubleAttribute("width"), root->DoubleAttribute("height"));
|
||||
double left = 0, bottom = 0;
|
||||
double width = root->DoubleAttribute("width"), height = root->DoubleAttribute("height");
|
||||
const char *viewBoxStr = root->Attribute("viewBox");
|
||||
if (viewBoxStr)
|
||||
sscanf(viewBoxStr, "%lf %lf %lf %lf", &viewBox.l, &viewBox.b, &dims.x, &dims.y);
|
||||
viewBox.r = viewBox.l+dims.x;
|
||||
viewBox.t = viewBox.b+dims.y;
|
||||
(void) sscanf(viewBoxStr, "%lf %lf %lf %lf", &left, &bottom, &width, &height);
|
||||
viewBox.l = left;
|
||||
viewBox.b = bottom;
|
||||
viewBox.r = left+width;
|
||||
viewBox.t = bottom+height;
|
||||
return flags;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include "../core/types.h"
|
||||
#include "../core/Shape.h"
|
||||
|
||||
#ifndef MSDFGEN_DISABLE_SVG
|
||||
|
|
@ -20,7 +21,7 @@ extern MSDFGEN_EXT_PUBLIC const int SVG_IMPORT_UNSUPPORTED_FEATURE_FLAG;
|
|||
extern MSDFGEN_EXT_PUBLIC const int SVG_IMPORT_TRANSFORMATION_IGNORED_FLAG;
|
||||
|
||||
/// Builds a shape from an SVG path string
|
||||
bool buildShapeFromSvgPath(Shape &shape, const char *pathDef, double endpointSnapRange = 0);
|
||||
bool buildShapeFromSvgPath(Shape &shape, const char *pathDef, real endpointSnapRange = 0);
|
||||
|
||||
/// Reads a single <path> element found in the specified SVG file and converts it to output Shape
|
||||
bool loadSvgShape(Shape &output, const char *filename, int pathIndex = 0, Vector2 *dimensions = NULL);
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
#include <skia/core/SkPath.h>
|
||||
#include <skia/pathops/SkPathOps.h>
|
||||
#include "../core/types.h"
|
||||
#include "../core/Vector2.hpp"
|
||||
#include "../core/edge-segments.h"
|
||||
#include "../core/Contour.h"
|
||||
|
|
@ -16,7 +17,7 @@ SkPoint pointToSkiaPoint(Point2 p) {
|
|||
}
|
||||
|
||||
Point2 pointFromSkiaPoint(const SkPoint p) {
|
||||
return Point2((double) p.x(), (double) p.y());
|
||||
return Point2((real) p.x(), (real) p.y());
|
||||
}
|
||||
|
||||
void shapeToSkiaPath(SkPath &skPath, const Shape &shape) {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "../core/types.h"
|
||||
#include "../core/BitmapRef.hpp"
|
||||
|
||||
#ifndef MSDFGEN_DISABLE_PNG
|
||||
|
|
|
|||
102
main.cpp
102
main.cpp
|
|
@ -23,7 +23,7 @@
|
|||
#include "core/ShapeDistanceFinder.h"
|
||||
|
||||
#define SDF_ERROR_ESTIMATE_PRECISION 19
|
||||
#define DEFAULT_ANGLE_THRESHOLD 3.
|
||||
#define DEFAULT_ANGLE_THRESHOLD ::msdfgen::real(3)
|
||||
|
||||
#if defined(MSDFGEN_EXTENSIONS) && !defined(MSDFGEN_DISABLE_PNG)
|
||||
#define DEFAULT_IMAGE_EXTENSION "png"
|
||||
|
|
@ -73,18 +73,26 @@ static bool parseUnsignedLL(unsigned long long &value, const char *arg) {
|
|||
return sscanf(arg, "%llu%c", &value, &c) == 1;
|
||||
}
|
||||
|
||||
static bool parseDouble(double &value, const char *arg) {
|
||||
static bool parseReal(real &value, const char *arg) {
|
||||
char c;
|
||||
return sscanf(arg, "%lf%c", &value, &c) == 1;
|
||||
double dblValue;
|
||||
if (sscanf(arg, "%lf%c", &dblValue, &c) == 1) {
|
||||
value = dblValue;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool parseAngle(double &value, const char *arg) {
|
||||
static bool parseAngle(real &value, const char *arg) {
|
||||
char c1, c2;
|
||||
int result = sscanf(arg, "%lf%c%c", &value, &c1, &c2);
|
||||
if (result == 1)
|
||||
double dblValue;
|
||||
int result = sscanf(arg, "%lf%c%c", &dblValue, &c1, &c2);
|
||||
if (result == 1) {
|
||||
value = real(dblValue);
|
||||
return true;
|
||||
}
|
||||
if (result == 2 && (c1 == 'd' || c1 == 'D')) {
|
||||
value *= M_PI/180;
|
||||
value = real(M_PI)/real(180)*real(dblValue);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
|
@ -162,7 +170,7 @@ static FontHandle *loadVarFont(FreetypeHandle *library, const char *filename) {
|
|||
double value = 0;
|
||||
int skip = 0;
|
||||
if (sscanf(++filename, "%lf%n", &value, &skip) == 1) {
|
||||
setFontVariationAxis(library, font, buffer.c_str(), value);
|
||||
setFontVariationAxis(library, font, buffer.c_str(), real(value));
|
||||
filename += skip;
|
||||
}
|
||||
}
|
||||
|
|
@ -542,12 +550,12 @@ int main(int argc, const char *const *argv) {
|
|||
RANGE_UNIT,
|
||||
RANGE_PX
|
||||
} rangeMode = RANGE_PX;
|
||||
double range = 1;
|
||||
double pxRange = 2;
|
||||
real range = 1;
|
||||
real pxRange = 2;
|
||||
Vector2 translate;
|
||||
Vector2 scale = 1;
|
||||
bool scaleSpecified = false;
|
||||
double angleThreshold = DEFAULT_ANGLE_THRESHOLD;
|
||||
real angleThreshold = DEFAULT_ANGLE_THRESHOLD;
|
||||
float outputDistanceShift = 0.f;
|
||||
const char *edgeAssignment = NULL;
|
||||
bool yFlip = false;
|
||||
|
|
@ -560,7 +568,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 &, real, unsigned long long) = edgeColoringSimple;
|
||||
bool explicitErrorCorrectionMode = false;
|
||||
|
||||
int argPos = 1;
|
||||
|
|
@ -741,8 +749,8 @@ int main(int argc, const char *const *argv) {
|
|||
continue;
|
||||
}
|
||||
ARG_CASE("-range", 1) {
|
||||
double r;
|
||||
if (!(parseDouble(r, argv[argPos+1]) && r > 0))
|
||||
real r;
|
||||
if (!(parseReal(r, argv[argPos+1]) && r > real(0)))
|
||||
ABORT("Invalid range argument. Use -range <range> with a positive real number.");
|
||||
rangeMode = RANGE_UNIT;
|
||||
range = r;
|
||||
|
|
@ -750,8 +758,8 @@ int main(int argc, const char *const *argv) {
|
|||
continue;
|
||||
}
|
||||
ARG_CASE("-pxrange", 1) {
|
||||
double r;
|
||||
if (!(parseDouble(r, argv[argPos+1]) && r > 0))
|
||||
real r;
|
||||
if (!(parseReal(r, argv[argPos+1]) && r > real(0)))
|
||||
ABORT("Invalid range argument. Use -pxrange <range> with a positive real number.");
|
||||
rangeMode = RANGE_PX;
|
||||
pxRange = r;
|
||||
|
|
@ -759,8 +767,8 @@ int main(int argc, const char *const *argv) {
|
|||
continue;
|
||||
}
|
||||
ARG_CASE("-scale", 1) {
|
||||
double s;
|
||||
if (!(parseDouble(s, argv[argPos+1]) && s > 0))
|
||||
real s;
|
||||
if (!(parseReal(s, argv[argPos+1]) && s > real(0)))
|
||||
ABORT("Invalid scale argument. Use -scale <scale> with a positive real number.");
|
||||
scale = s;
|
||||
scaleSpecified = true;
|
||||
|
|
@ -768,8 +776,8 @@ int main(int argc, const char *const *argv) {
|
|||
continue;
|
||||
}
|
||||
ARG_CASE("-ascale", 2) {
|
||||
double sx, sy;
|
||||
if (!(parseDouble(sx, argv[argPos+1]) && parseDouble(sy, argv[argPos+2]) && sx > 0 && sy > 0))
|
||||
real sx, sy;
|
||||
if (!(parseReal(sx, argv[argPos+1]) && parseReal(sy, argv[argPos+2]) && sx > real(0) && sy > real(0)))
|
||||
ABORT("Invalid scale arguments. Use -ascale <x> <y> with two positive real numbers.");
|
||||
scale.set(sx, sy);
|
||||
scaleSpecified = true;
|
||||
|
|
@ -777,15 +785,15 @@ int main(int argc, const char *const *argv) {
|
|||
continue;
|
||||
}
|
||||
ARG_CASE("-translate", 2) {
|
||||
double tx, ty;
|
||||
if (!(parseDouble(tx, argv[argPos+1]) && parseDouble(ty, argv[argPos+2])))
|
||||
real tx, ty;
|
||||
if (!(parseReal(tx, argv[argPos+1]) && parseReal(ty, argv[argPos+2])))
|
||||
ABORT("Invalid translate arguments. Use -translate <x> <y> with two real numbers.");
|
||||
translate.set(tx, ty);
|
||||
argPos += 3;
|
||||
continue;
|
||||
}
|
||||
ARG_CASE("-angle", 1) {
|
||||
double at;
|
||||
real at;
|
||||
if (!parseAngle(at, argv[argPos+1]))
|
||||
ABORT("Invalid angle threshold. Use -angle <min angle> with a positive real number less than PI or a value in degrees followed by 'd' below 180d.");
|
||||
angleThreshold = at;
|
||||
|
|
@ -827,16 +835,16 @@ int main(int argc, const char *const *argv) {
|
|||
continue;
|
||||
}
|
||||
ARG_CASE("-errordeviationratio", 1) {
|
||||
double edr;
|
||||
if (!(parseDouble(edr, argv[argPos+1]) && edr > 0))
|
||||
real edr;
|
||||
if (!(parseReal(edr, argv[argPos+1]) && edr > real(0)))
|
||||
ABORT("Invalid error deviation ratio. Use -errordeviationratio <ratio> with a positive real number.");
|
||||
generatorConfig.errorCorrection.minDeviationRatio = edr;
|
||||
argPos += 2;
|
||||
continue;
|
||||
}
|
||||
ARG_CASE("-errorimproveratio", 1) {
|
||||
double eir;
|
||||
if (!(parseDouble(eir, argv[argPos+1]) && eir > 0))
|
||||
real eir;
|
||||
if (!(parseReal(eir, argv[argPos+1]) && eir > real(0)))
|
||||
ABORT("Invalid error improvement ratio. Use -errorimproveratio <ratio> with a positive real number.");
|
||||
generatorConfig.errorCorrection.minImproveRatio = eir;
|
||||
argPos += 2;
|
||||
|
|
@ -865,8 +873,8 @@ int main(int argc, const char *const *argv) {
|
|||
continue;
|
||||
}
|
||||
ARG_CASE("-distanceshift", 1) {
|
||||
double ds;
|
||||
if (!parseDouble(ds, argv[argPos+1]))
|
||||
real ds;
|
||||
if (!parseReal(ds, argv[argPos+1]))
|
||||
ABORT("Invalid distance shift. Use -distanceshift <shift> with a real value.");
|
||||
outputDistanceShift = (float) ds;
|
||||
argPos += 2;
|
||||
|
|
@ -948,7 +956,7 @@ int main(int argc, const char *const *argv) {
|
|||
|
||||
// Load input
|
||||
Shape::Bounds svgViewBox = { };
|
||||
double glyphAdvance = 0;
|
||||
real glyphAdvance = 0;
|
||||
if (!inputType || !input) {
|
||||
#ifdef MSDFGEN_EXTENSIONS
|
||||
#ifdef MSDFGEN_DISABLE_SVG
|
||||
|
|
@ -1057,35 +1065,35 @@ int main(int argc, const char *const *argv) {
|
|||
if (yFlip)
|
||||
shape.inverseYAxis = !shape.inverseYAxis;
|
||||
|
||||
double avgScale = .5*(scale.x+scale.y);
|
||||
real avgScale = real(.5)*(scale.x+scale.y);
|
||||
Shape::Bounds bounds = { };
|
||||
if (autoFrame || mode == METRICS || printMetrics || orientation == GUESS)
|
||||
bounds = shape.getBounds();
|
||||
|
||||
// Auto-frame
|
||||
if (autoFrame) {
|
||||
double l = bounds.l, b = bounds.b, r = bounds.r, t = bounds.t;
|
||||
real l = bounds.l, b = bounds.b, r = bounds.r, t = bounds.t;
|
||||
Vector2 frame(width, height);
|
||||
double m = .5+(double) outputDistanceShift;
|
||||
real m = real(.5)+real(outputDistanceShift);
|
||||
if (!scaleSpecified) {
|
||||
if (rangeMode == RANGE_UNIT)
|
||||
l -= m*range, b -= m*range, r += m*range, t += m*range;
|
||||
else
|
||||
frame -= 2*m*pxRange;
|
||||
frame -= real(2)*m*pxRange;
|
||||
}
|
||||
if (l >= r || b >= t)
|
||||
l = 0, b = 0, r = 1, t = 1;
|
||||
if (frame.x <= 0 || frame.y <= 0)
|
||||
if (frame.x <= real(0) || frame.y <= real(0))
|
||||
ABORT("Cannot fit the specified pixel range.");
|
||||
Vector2 dims(r-l, t-b);
|
||||
if (scaleSpecified)
|
||||
translate = .5*(frame/scale-dims)-Vector2(l, b);
|
||||
translate = real(.5)*(frame/scale-dims)-Vector2(l, b);
|
||||
else {
|
||||
if (dims.x*frame.y < dims.y*frame.x) {
|
||||
translate.set(.5*(frame.x/frame.y*dims.y-dims.x)-l, -b);
|
||||
translate.set(real(.5)*(frame.x/frame.y*dims.y-dims.x)-l, -b);
|
||||
scale = avgScale = frame.y/dims.y;
|
||||
} else {
|
||||
translate.set(-l, .5*(frame.y/frame.x*dims.x-dims.y)-b);
|
||||
translate.set(-l, real(.5)*(frame.y/frame.x*dims.x-dims.y)-b);
|
||||
scale = avgScale = frame.x/dims.x;
|
||||
}
|
||||
}
|
||||
|
|
@ -1106,18 +1114,18 @@ int main(int argc, const char *const *argv) {
|
|||
if (shape.inverseYAxis)
|
||||
fprintf(out, "inverseY = true\n");
|
||||
if (svgViewBox.l < svgViewBox.r && svgViewBox.b < svgViewBox.t)
|
||||
fprintf(out, "view box = %.17g, %.17g, %.17g, %.17g\n", svgViewBox.l, svgViewBox.b, svgViewBox.r, svgViewBox.t);
|
||||
fprintf(out, "view box = %.17g, %.17g, %.17g, %.17g\n", double(svgViewBox.l), double(svgViewBox.b), double(svgViewBox.r), double(svgViewBox.t));
|
||||
if (bounds.l < bounds.r && bounds.b < bounds.t)
|
||||
fprintf(out, "bounds = %.17g, %.17g, %.17g, %.17g\n", bounds.l, bounds.b, bounds.r, bounds.t);
|
||||
fprintf(out, "bounds = %.17g, %.17g, %.17g, %.17g\n", double(bounds.l), double(bounds.b), double(bounds.r), double(bounds.t));
|
||||
if (glyphAdvance != 0)
|
||||
fprintf(out, "advance = %.17g\n", glyphAdvance);
|
||||
fprintf(out, "advance = %.17g\n", double(glyphAdvance));
|
||||
if (autoFrame) {
|
||||
if (!scaleSpecified)
|
||||
fprintf(out, "scale = %.17g\n", avgScale);
|
||||
fprintf(out, "translate = %.17g, %.17g\n", translate.x, translate.y);
|
||||
fprintf(out, "scale = %.17g\n", double(avgScale));
|
||||
fprintf(out, "translate = %.17g, %.17g\n", double(translate.x), double(translate.y));
|
||||
}
|
||||
if (rangeMode == RANGE_PX)
|
||||
fprintf(out, "range = %.17g\n", range);
|
||||
fprintf(out, "range = %.17g\n", double(range));
|
||||
if (mode == METRICS && outputSpecified)
|
||||
fclose(out);
|
||||
}
|
||||
|
|
@ -1188,9 +1196,9 @@ int main(int argc, const char *const *argv) {
|
|||
|
||||
if (orientation == GUESS) {
|
||||
// Get sign of signed distance outside bounds
|
||||
Point2 p(bounds.l-(bounds.r-bounds.l)-1, bounds.b-(bounds.t-bounds.b)-1);
|
||||
double distance = SimpleTrueShapeDistanceFinder::oneShotDistance(shape, p);
|
||||
orientation = distance <= 0 ? KEEP : REVERSE;
|
||||
Point2 p(bounds.l-(bounds.r-bounds.l)-real(1), bounds.b-(bounds.t-bounds.b)-real(1));
|
||||
real distance = SimpleTrueShapeDistanceFinder::oneShotDistance(shape, p);
|
||||
orientation = distance <= real(0) ? KEEP : REVERSE;
|
||||
}
|
||||
if (orientation == REVERSE) {
|
||||
switch (mode) {
|
||||
|
|
|
|||
25
msdfgen.h
25
msdfgen.h
|
|
@ -15,6 +15,7 @@
|
|||
*
|
||||
*/
|
||||
|
||||
#include "core/types.h"
|
||||
#include "core/arithmetics.hpp"
|
||||
#include "core/Vector2.hpp"
|
||||
#include "core/Projection.h"
|
||||
|
|
@ -37,27 +38,27 @@
|
|||
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 Projection &projection, real 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());
|
||||
void generatePseudoSDF(const BitmapRef<float, 1> &output, const Shape &shape, const Projection &projection, real 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());
|
||||
void generateMSDF(const BitmapRef<float, 3> &output, const Shape &shape, const Projection &projection, real range, 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 Projection &projection, real range, const MSDFGeneratorConfig &config = MSDFGeneratorConfig());
|
||||
|
||||
// Old version of the function API's kept for backwards compatibility
|
||||
void generateSDF(const BitmapRef<float, 1> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, bool overlapSupport = true);
|
||||
void generatePseudoSDF(const BitmapRef<float, 1> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, bool overlapSupport = true);
|
||||
void generateMSDF(const BitmapRef<float, 3> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, 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, real range, const Vector2 &scale, const Vector2 &translate, bool overlapSupport = true);
|
||||
void generatePseudoSDF(const BitmapRef<float, 1> &output, const Shape &shape, real range, const Vector2 &scale, const Vector2 &translate, bool overlapSupport = true);
|
||||
void generateMSDF(const BitmapRef<float, 3> &output, const Shape &shape, real range, const Vector2 &scale, const Vector2 &translate, const ErrorCorrectionConfig &errorCorrectionConfig = ErrorCorrectionConfig(), bool overlapSupport = true);
|
||||
void generateMTSDF(const BitmapRef<float, 4> &output, const Shape &shape, real 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 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, real range, const Vector2 &scale, const Vector2 &translate);
|
||||
void generatePseudoSDF_legacy(const BitmapRef<float, 1> &output, const Shape &shape, real range, const Vector2 &scale, const Vector2 &translate);
|
||||
void generateMSDF_legacy(const BitmapRef<float, 3> &output, const Shape &shape, real range, const Vector2 &scale, const Vector2 &translate, ErrorCorrectionConfig errorCorrectionConfig = ErrorCorrectionConfig());
|
||||
void generateMTSDF_legacy(const BitmapRef<float, 4> &output, const Shape &shape, real range, const Vector2 &scale, const Vector2 &translate, ErrorCorrectionConfig errorCorrectionConfig = ErrorCorrectionConfig());
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue