Replaced double by configurable real type

This commit is contained in:
Chlumsky 2023-10-22 20:53:51 +02:00
parent 3e8774abde
commit 901b099386
45 changed files with 838 additions and 786 deletions

View File

@ -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 {

View File

@ -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);

View File

@ -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.

View File

@ -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]);

View File

@ -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;
};

View File

@ -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;
}

View File

@ -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;

View File

@ -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);
}

View File

@ -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;
};

View File

@ -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) {

View File

@ -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.

View File

@ -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) { }
};

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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)

View File

@ -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))

View File

@ -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);

View File

@ -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);
}

View File

@ -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 &param) const {
SignedDistance LinearSegment::signedDistance(Point2 origin, real &param) 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 &param) const {
SignedDistance QuadraticSegment::signedDistance(Point2 origin, real &param) 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 &param) const {
SignedDistance CubicSegment::signedDistance(Point2 origin, real &param) 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 &param) const {
SignedDistance QuadraticSegment::signedDistance(Point2 origin, real &param) 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 &param) const {
SignedDistance CubicSegment::signedDistance(Point2 origin, real &param) 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 &param) 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;
}
}

View File

@ -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 &param) const = 0;
virtual SignedDistance signedDistance(Point2 origin, real &param) 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 &param) 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 &param) 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 &param) 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 &param) 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 &param) 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 &param) 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);
};

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);
}

View File

@ -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.

View File

@ -10,7 +10,7 @@
namespace msdfgen {
template <int N>
static void msdfErrorCorrectionInner(const BitmapRef<float, N> &sdf, const Shape &shape, const Projection &projection, double range, const MSDFGeneratorConfig &config) {
static void msdfErrorCorrectionInner(const BitmapRef<float, N> &sdf, const Shape &shape, const 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];

View File

@ -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);

View File

@ -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));
}
}

View File

@ -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));
}

View File

@ -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)

View File

@ -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);

View File

@ -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);

View File

@ -1,6 +1,7 @@
#pragma once
#include "types.h"
#include "BitmapRef.hpp"
namespace msdfgen {

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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;
}

15
core/types.h Normal file
View File

@ -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
}

View File

@ -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;

View File

@ -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

View File

@ -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;
}

View File

@ -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);

View File

@ -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) {

View File

@ -1,6 +1,7 @@
#pragma once
#include "../core/types.h"
#include "../core/BitmapRef.hpp"
#ifndef MSDFGEN_DISABLE_PNG

102
main.cpp
View File

@ -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) {

View File

@ -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());
}