mirror of https://github.com/Chlumsky/msdfgen.git
Complete overhaul of artifact patcher (CLI integration not finished)
This commit is contained in:
parent
37a1fff421
commit
3e684b1d68
|
|
@ -17,9 +17,13 @@ struct GeneratorConfig {
|
|||
struct ArtifactPatcherConfig {
|
||||
/// The mode of operation.
|
||||
enum Mode {
|
||||
/// Skips artifact patcher pass.
|
||||
DISABLED,
|
||||
/// Patches all discontinuities of the distance field regardless if edges are adversely affected.
|
||||
INDISCRIMINATE,
|
||||
/// Patches artifacts at edges and other discontinuous distances only if it does not affect edges or corners.
|
||||
EDGE_PRIORITY,
|
||||
/// Only patches artifacts at edges, may be significantly faster than the other modes.
|
||||
EDGE_ONLY
|
||||
} mode;
|
||||
/// The minimum ratio of improvement required to patch a pixel. Must be greater than or equal to 1.
|
||||
|
|
|
|||
|
|
@ -13,162 +13,393 @@
|
|||
|
||||
namespace msdfgen {
|
||||
|
||||
static bool isHotspot(float am, float bm, float xm) {
|
||||
return (am > .5f && bm > .5f && xm < .5f) || (am < .5f && bm < .5f && xm > .5f);
|
||||
// A much more aggressive version for the entire distance field (not just edges): return median(am, bm, xm) != xm;
|
||||
}
|
||||
#define PROTECTION_RADIUS_TOLERANCE 1.001
|
||||
|
||||
static int findLinearChannelHotspots(double t[1], const float *a, const float *b, float dA, float dB) {
|
||||
int found = 0;
|
||||
double x = (double) dA/(dA-dB);
|
||||
if (x > 0 && x < 1) {
|
||||
float am = median(a[0], a[1], a[2]);
|
||||
float bm = median(b[0], b[1], b[2]);
|
||||
float xm = median(
|
||||
mix(a[0], b[0], x),
|
||||
mix(a[1], b[1], x),
|
||||
mix(a[2], b[2], x)
|
||||
);
|
||||
if (isHotspot(am, bm, xm))
|
||||
t[found++] = x;
|
||||
#ifdef MSDFGEN_USE_OPENMP
|
||||
// std::vector<bool> cannot be modified concurrently
|
||||
#define STENCIL_TYPE std::vector<char>
|
||||
#else
|
||||
#define STENCIL_TYPE std::vector<bool>
|
||||
#endif
|
||||
#define STENCIL_INDEX(x, y) (sdf.width*(y)+(x))
|
||||
|
||||
template <class ArtifactClassifier, template <typename> class ContourCombiner, int N>
|
||||
struct ArtifactFinderData {
|
||||
ShapeDistanceFinder<ContourCombiner<PseudoDistanceSelector> > distanceFinder;
|
||||
const ArtifactClassifier artifactClassifier;
|
||||
const BitmapConstRef<float, N> sdf;
|
||||
const double invRange;
|
||||
const Vector2 pDelta;
|
||||
Point2 p, sdfCoord;
|
||||
const float *msd;
|
||||
float psd;
|
||||
bool isProtected;
|
||||
|
||||
inline ArtifactFinderData(const ArtifactClassifier &artifactClassifier, const BitmapConstRef<float, N> &sdf, const Shape &shape, double range, const Vector2 &pDelta) :
|
||||
distanceFinder(shape), artifactClassifier(artifactClassifier), sdf(sdf), invRange(1/range), pDelta(pDelta) { }
|
||||
};
|
||||
|
||||
class IndiscriminateArtifactClassifier {
|
||||
double minImproveRatio;
|
||||
public:
|
||||
inline static bool observesProtected() {
|
||||
return false;
|
||||
}
|
||||
return found;
|
||||
inline static bool isEquivalent(float am, float bm) {
|
||||
return am == bm;
|
||||
}
|
||||
inline explicit IndiscriminateArtifactClassifier(double minImproveRatio) : minImproveRatio(minImproveRatio) { }
|
||||
inline bool isCandidate(float am, float bm, float xm, bool isProtected) const {
|
||||
return median(am, bm, xm) != xm;
|
||||
}
|
||||
inline bool isArtifact(float refSD, float newSD, float oldSD) const {
|
||||
return minImproveRatio*fabsf(newSD-refSD) < double(fabsf(oldSD-refSD));
|
||||
}
|
||||
};
|
||||
|
||||
class EdgePriorityArtifactClassifier {
|
||||
double minImproveRatio;
|
||||
public:
|
||||
inline static bool observesProtected() {
|
||||
return true;
|
||||
}
|
||||
inline static bool isEquivalent(float am, float bm) {
|
||||
return am == bm;
|
||||
}
|
||||
inline explicit EdgePriorityArtifactClassifier(double minImproveRatio) : minImproveRatio(minImproveRatio) { }
|
||||
inline bool isCandidate(float am, float bm, float xm, bool isProtected) const {
|
||||
return (am > .5f && bm > .5f && xm < .5f) || (am < .5f && bm < .5f && xm > .5f) || (!isProtected && median(am, bm, xm) != xm);
|
||||
}
|
||||
inline bool isArtifact(float refSD, float newSD, float oldSD) const {
|
||||
float oldDelta = fabsf(oldSD-refSD);
|
||||
float newDelta = fabsf(newSD-refSD);
|
||||
return newDelta < oldDelta && (minImproveRatio*newDelta < double(oldDelta) || (refSD > .5f && newSD > .5f && oldSD < .5f) || (refSD < .5f && newSD < .5f && oldSD > .5f));
|
||||
}
|
||||
};
|
||||
|
||||
class EdgeOnlyArtifactClassifier {
|
||||
public:
|
||||
inline static bool observesProtected() {
|
||||
return false;
|
||||
}
|
||||
inline static bool isEquivalent(float am, float bm) {
|
||||
return (am <= .5f && bm <= .5f) || (am >= .5f && bm >= .5f);
|
||||
}
|
||||
inline bool isCandidate(float am, float bm, float xm, bool isProtected) const {
|
||||
return (am > .5f && bm > .5f && xm < .5f) || (am < .5f && bm < .5f && xm > .5f);
|
||||
}
|
||||
inline bool isArtifact(float refSD, float newSD, float oldSD) const {
|
||||
return fabsf(newSD-refSD) <= fabsf(oldSD-refSD) && ((refSD > .5f && newSD > .5f && oldSD < .5f) || (refSD < .5f && newSD < .5f && oldSD > .5f));
|
||||
}
|
||||
};
|
||||
|
||||
static float interpolatedMedian(const float *a, const float *b, double t) {
|
||||
return median(
|
||||
mix(a[0], b[0], t),
|
||||
mix(a[1], b[1], t),
|
||||
mix(a[2], b[2], t)
|
||||
);
|
||||
}
|
||||
|
||||
static int findDiagonalChannelHotspots(double t[2], const float *a, const float *b, const float *c, const float *d, float dA, float dB, float dC, float dD) {
|
||||
int found = 0;
|
||||
double x[2];
|
||||
int solutions = solveQuadratic(x, (dD-dC)-(dB-dA), dC+dB-2*dA, dA);
|
||||
for (int i = 0; i < solutions; ++i)
|
||||
if (x[i] > 0 && x[i] < 1) {
|
||||
float am = median(a[0], a[1], a[2]);
|
||||
float dm = median(d[0], d[1], d[2]);
|
||||
float xm = median(
|
||||
mix(mix(a[0], b[0], x[i]), mix(c[0], d[0], x[i]), x[i]),
|
||||
mix(mix(a[1], b[1], x[i]), mix(c[1], d[1], x[i]), x[i]),
|
||||
mix(mix(a[2], b[2], x[i]), mix(c[2], d[2], x[i]), x[i])
|
||||
);
|
||||
if (isHotspot(am, dm, xm))
|
||||
t[found++] = x[i];
|
||||
}
|
||||
return found;
|
||||
static float interpolatedMedian(const float *a, const float *lin, const float *quad, double t) {
|
||||
return float(median(
|
||||
t*(t*quad[0]+lin[0])+a[0],
|
||||
t*(t*quad[1]+lin[1])+a[1],
|
||||
t*(t*quad[2]+lin[2])+a[2]
|
||||
));
|
||||
}
|
||||
|
||||
static int findLinearHotspots(double t[3], const float *a, const float *b) {
|
||||
int found = 0;
|
||||
found += findLinearChannelHotspots(t+found, a, b, a[1]-a[0], b[1]-b[0]);
|
||||
found += findLinearChannelHotspots(t+found, a, b, a[2]-a[1], b[2]-b[1]);
|
||||
found += findLinearChannelHotspots(t+found, a, b, a[0]-a[2], b[0]-b[2]);
|
||||
return found;
|
||||
static bool edgeBetweenTexelsChannel(const float *a, const float *b, int channel) {
|
||||
float t = (a[channel]-.5f)/(a[channel]-b[channel]);
|
||||
if (t > 0.f && t < 1.f) {
|
||||
float c[3] = {
|
||||
mix(a[0], b[0], t),
|
||||
mix(a[1], b[1], t),
|
||||
mix(a[2], b[2], t)
|
||||
};
|
||||
return median(c[0], c[1], c[2]) == c[channel];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static int findDiagonalHotspots(double t[6], const float *a, const float *b, const float *c, const float *d) {
|
||||
int found = 0;
|
||||
found += findDiagonalChannelHotspots(t+found, a, b, c, d, a[1]-a[0], b[1]-b[0], c[1]-c[0], d[1]-d[0]);
|
||||
found += findDiagonalChannelHotspots(t+found, a, b, c, d, a[2]-a[1], b[2]-b[1], c[2]-c[1], d[2]-d[1]);
|
||||
found += findDiagonalChannelHotspots(t+found, a, b, c, d, a[0]-a[2], b[0]-b[2], c[0]-c[2], d[0]-d[2]);
|
||||
return found;
|
||||
static bool edgeBetweenTexels(const float *a, const float *b) {
|
||||
return (
|
||||
edgeBetweenTexelsChannel(a, b, 0) ||
|
||||
edgeBetweenTexelsChannel(a, b, 1) ||
|
||||
edgeBetweenTexelsChannel(a, b, 2)
|
||||
);
|
||||
}
|
||||
|
||||
template <int N>
|
||||
void findHotspots(std::vector<Point2> &hotspots, const BitmapConstRef<float, N> &sdf) {
|
||||
// All hotspots intersect either the horizontal, vertical, or diagonal line that connects neighboring texels
|
||||
static void flagProtected(STENCIL_TYPE &stencil, const BitmapConstRef<float, N> &sdf, const Shape &shape, const Projection &projection, double range) {
|
||||
// Protect texels that are interpolated at corners
|
||||
for (std::vector<Contour>::const_iterator contour = shape.contours.begin(); contour != shape.contours.end(); ++contour)
|
||||
if (!contour->edges.empty()) {
|
||||
const EdgeSegment *prevEdge = contour->edges.back();
|
||||
for (std::vector<EdgeHolder>::const_iterator edge = contour->edges.begin(); edge != contour->edges.end(); ++edge) {
|
||||
int commonColor = prevEdge->color&(*edge)->color;
|
||||
if (!(commonColor&(commonColor-1))) { // Color switch between prevEdge and edge => a corner
|
||||
Point2 p = projection.project((*edge)->point(0));
|
||||
if (shape.inverseYAxis)
|
||||
p.y = sdf.height-p.y;
|
||||
int l = (int) floor(p.x-.5);
|
||||
int b = (int) floor(p.y-.5);
|
||||
int r = l+1;
|
||||
int t = b+1;
|
||||
if (l < sdf.width && b < sdf.height && r >= 0 && t >= 0) {
|
||||
if (l >= 0 && b >= 0)
|
||||
stencil[STENCIL_INDEX(l, b)] = true;
|
||||
if (r < sdf.width && b >= 0)
|
||||
stencil[STENCIL_INDEX(r, b)] = true;
|
||||
if (l >= 0 && t < sdf.height)
|
||||
stencil[STENCIL_INDEX(l, t)] = true;
|
||||
if (r < sdf.width && t < sdf.height)
|
||||
stencil[STENCIL_INDEX(r, t)] = true;
|
||||
}
|
||||
}
|
||||
prevEdge = *edge;
|
||||
}
|
||||
}
|
||||
// Protect texels that contribute to edges
|
||||
float radius;
|
||||
// Horizontal:
|
||||
radius = float(PROTECTION_RADIUS_TOLERANCE*projection.unprojectVector(Vector2(1/range, 0)).length());
|
||||
for (int y = 0; y < sdf.height; ++y) {
|
||||
const float *left = sdf(0, y);
|
||||
const float *right = sdf(1, y);
|
||||
for (int x = 0; x < sdf.width-1; ++x) {
|
||||
double t[3];
|
||||
int found = findLinearHotspots(t, left, right);
|
||||
for (int i = 0; i < found; ++i)
|
||||
hotspots.push_back(Point2(x+.5+t[i], y+.5));
|
||||
float lm = median(left[0], left[1], left[2]);
|
||||
float rm = median(right[0], right[1], right[2]);
|
||||
if (fabsf(lm-.5f)+fabsf(rm-.5f) < radius && edgeBetweenTexels(left, right)) {
|
||||
stencil[STENCIL_INDEX(x, y)] = true;
|
||||
stencil[STENCIL_INDEX(x+1, y)] = true;
|
||||
}
|
||||
left += N, right += N;
|
||||
}
|
||||
}
|
||||
// Vertical:
|
||||
radius = float(PROTECTION_RADIUS_TOLERANCE*projection.unprojectVector(Vector2(0, 1/range)).length());
|
||||
for (int y = 0; y < sdf.height-1; ++y) {
|
||||
const float *bottom = sdf(0, y);
|
||||
const float *top = sdf(0, y+1);
|
||||
for (int x = 0; x < sdf.width; ++x) {
|
||||
double t[3];
|
||||
int found = findLinearHotspots(t, bottom, top);
|
||||
for (int i = 0; i < found; ++i)
|
||||
hotspots.push_back(Point2(x+.5, y+.5+t[i]));
|
||||
float bm = median(bottom[0], bottom[1], bottom[2]);
|
||||
float tm = median(top[0], top[1], top[2]);
|
||||
if (fabsf(bm-.5f)+fabsf(tm-.5f) < radius && edgeBetweenTexels(bottom, top)) {
|
||||
stencil[STENCIL_INDEX(x, y)] = true;
|
||||
stencil[STENCIL_INDEX(x, y+1)] = true;
|
||||
}
|
||||
bottom += N, top += N;
|
||||
}
|
||||
}
|
||||
// Diagonal:
|
||||
radius = float(PROTECTION_RADIUS_TOLERANCE*projection.unprojectVector(Vector2(1/range)).length());
|
||||
for (int y = 0; y < sdf.height-1; ++y) {
|
||||
const float *lb = sdf(0, y);
|
||||
const float *rb = sdf(1, y);
|
||||
const float *lt = sdf(0, y+1);
|
||||
const float *rt = sdf(1, y+1);
|
||||
for (int x = 0; x < sdf.width-1; ++x) {
|
||||
double t[6];
|
||||
int found = 0;
|
||||
found = findDiagonalHotspots(t, lb, rb, lt, rt);
|
||||
for (int i = 0; i < found; ++i)
|
||||
hotspots.push_back(Point2(x+.5+t[i], y+.5+t[i]));
|
||||
found = findDiagonalHotspots(t, lt, rt, lb, rb);
|
||||
for (int i = 0; i < found; ++i)
|
||||
hotspots.push_back(Point2(x+.5+t[i], y+1.5-t[i]));
|
||||
float mlb = median(lb[0], lb[1], lb[2]);
|
||||
float mrb = median(rb[0], rb[1], rb[2]);
|
||||
float mlt = median(lt[0], lt[1], lt[2]);
|
||||
float mrt = median(rt[0], rt[1], rt[2]);
|
||||
if (fabsf(mlb-.5f)+fabsf(mrt-.5f) < radius && edgeBetweenTexels(lb, rt)) {
|
||||
stencil[STENCIL_INDEX(x, y)] = true;
|
||||
stencil[STENCIL_INDEX(x+1, y+1)] = true;
|
||||
}
|
||||
if (fabsf(mrb-.5f)+fabsf(mlt-.5f) < radius && edgeBetweenTexels(rb, lt)) {
|
||||
stencil[STENCIL_INDEX(x+1, y)] = true;
|
||||
stencil[STENCIL_INDEX(x, y+1)] = true;
|
||||
}
|
||||
lb += N, rb += N, lt += N, rt += N;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <template <typename> class ContourCombiner, int N>
|
||||
static void msdfPatchArtifactsInner(const BitmapRef<float, N> &sdf, const Shape &shape, const Projection &projection, double range) {
|
||||
ShapeDistanceFinder<ContourCombiner<PseudoDistanceSelector> > distanceFinder(shape);
|
||||
std::vector<Point2> hotspots;
|
||||
findHotspots(hotspots, BitmapConstRef<float, N>(sdf));
|
||||
std::vector<std::pair<int, int> > artifacts;
|
||||
artifacts.reserve(hotspots.size());
|
||||
for (std::vector<Point2>::const_iterator hotspot = hotspots.begin(); hotspot != hotspots.end(); ++hotspot) {
|
||||
Point2 pos = projection.unproject(*hotspot);
|
||||
double actualDistance = distanceFinder.distance(pos);
|
||||
float sd = float(actualDistance/range+.5);
|
||||
template <class ArtifactClassifier, template <typename> class ContourCombiner, int N>
|
||||
static bool isArtifact(ArtifactFinderData<ArtifactClassifier, ContourCombiner, N> &data, const Vector2 &v) {
|
||||
Point2 sdfCoord = data.sdfCoord+v;
|
||||
float oldMSD[N], newMSD[3];
|
||||
interpolate(oldMSD, data.sdf, data.sdfCoord+v);
|
||||
double aWeight = (1-fabs(v.x))*(1-fabs(v.y));
|
||||
newMSD[0] = float(oldMSD[0]+aWeight*(data.psd-data.msd[0]));
|
||||
newMSD[1] = float(oldMSD[1]+aWeight*(data.psd-data.msd[1]));
|
||||
newMSD[2] = float(oldMSD[2]+aWeight*(data.psd-data.msd[2]));
|
||||
float oldPSD = median(oldMSD[0], oldMSD[1], oldMSD[2]);
|
||||
float newPSD = median(newMSD[0], newMSD[1], newMSD[2]);
|
||||
if (ArtifactClassifier::isEquivalent(oldPSD, newPSD))
|
||||
return false;
|
||||
float refPSD = float(data.invRange*data.distanceFinder.distance(data.p+v*data.pDelta)+.5);
|
||||
return data.artifactClassifier.isArtifact(refPSD, newPSD, oldPSD);
|
||||
}
|
||||
|
||||
// Store hotspot's closest texel's current color
|
||||
float *subject = sdf((int) hotspot->x, (int) hotspot->y);
|
||||
float texel[N];
|
||||
memcpy(texel, subject, N*sizeof(float));
|
||||
// Sample signed distance at hotspot
|
||||
float msd[N];
|
||||
interpolate(msd, BitmapConstRef<float, N>(sdf), *hotspot);
|
||||
float oldSsd = median(msd[0], msd[1], msd[2]);
|
||||
// Flatten hotspot's closest texel
|
||||
float med = median(subject[0], subject[1], subject[2]);
|
||||
subject[0] = med, subject[1] = med, subject[2] = med;
|
||||
// Sample signed distance at hotspot after flattening
|
||||
interpolate(msd, BitmapConstRef<float, N>(sdf), *hotspot);
|
||||
float newSsd = median(msd[0], msd[1], msd[2]);
|
||||
// Revert modified texel
|
||||
memcpy(subject, texel, N*sizeof(float));
|
||||
|
||||
// Consider hotspot an artifact if flattening improved the sample
|
||||
if (fabsf(newSsd-sd) < fabsf(oldSsd-sd))
|
||||
artifacts.push_back(std::make_pair((int) hotspot->x, (int) hotspot->y));
|
||||
template <class ArtifactClassifier, template <typename> class ContourCombiner, int N>
|
||||
static bool hasLinearArtifact(ArtifactFinderData<ArtifactClassifier, ContourCombiner, N> &data, const Vector2 &v, const float *b, float bm, float dA, float dB) {
|
||||
double t = (double) dA/(dA-dB);
|
||||
if (t > 0 && t < 1) {
|
||||
float xm = interpolatedMedian(data.msd, b, t);
|
||||
return data.artifactClassifier.isCandidate(data.psd, bm, xm, data.isProtected) && isArtifact(data, t*v);
|
||||
}
|
||||
for (std::vector<std::pair<int, int> >::const_iterator artifact = artifacts.begin(); artifact != artifacts.end(); ++artifact) {
|
||||
float *pixel = sdf(artifact->first, artifact->second);
|
||||
float med = median(pixel[0], pixel[1], pixel[2]);
|
||||
pixel[0] = med, pixel[1] = med, pixel[2] = med;
|
||||
return false;
|
||||
}
|
||||
|
||||
template <class ArtifactClassifier, template <typename> class ContourCombiner, int N>
|
||||
static bool hasDiagonalArtifact(ArtifactFinderData<ArtifactClassifier, ContourCombiner, N> &data, const Vector2 &v, const float *lin, const float *quad, float dm, float dA, float dBC, float dD, double tEx0, double tEx1) {
|
||||
const float *a = data.msd;
|
||||
double t[2];
|
||||
int solutions = solveQuadratic(t, dD-dBC+dA, dBC-dA-dA, dA);
|
||||
for (int i = 0; i < solutions; ++i) {
|
||||
if (t[i] > 0 && t[i] < 1) {
|
||||
float xm = interpolatedMedian(a, lin, quad, t[i]);
|
||||
bool candidate = data.artifactClassifier.isCandidate(data.psd, dm, xm, data.isProtected);
|
||||
float m[2];
|
||||
if (!candidate && tEx0 > 0 && tEx0 < 1) {
|
||||
m[0] = data.psd, m[1] = dm;
|
||||
m[tEx0 > t[i]] = interpolatedMedian(a, lin, quad, tEx0);
|
||||
candidate = data.artifactClassifier.isCandidate(m[0], m[1], xm, data.isProtected);
|
||||
}
|
||||
if (!candidate && tEx1 > 0 && tEx1 < 1) {
|
||||
m[0] = data.psd, m[1] = dm;
|
||||
m[tEx1 > t[i]] = interpolatedMedian(a, lin, quad, tEx1);
|
||||
candidate = data.artifactClassifier.isCandidate(m[0], m[1], xm, data.isProtected);
|
||||
}
|
||||
if (candidate && isArtifact(data, t[i]*v))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <class ArtifactClassifier, template <typename> class ContourCombiner, int N>
|
||||
static bool hasLinearArtifact(ArtifactFinderData<ArtifactClassifier, ContourCombiner, N> &data, const Vector2 &v, const float *b) {
|
||||
const float *a = data.msd;
|
||||
float bm = median(b[0], b[1], b[2]);
|
||||
return (fabsf(data.psd-.5f) > fabsf(bm-.5f) && (
|
||||
hasLinearArtifact(data, v, b, bm, a[1]-a[0], b[1]-b[0]) ||
|
||||
hasLinearArtifact(data, v, b, bm, a[2]-a[1], b[2]-b[1]) ||
|
||||
hasLinearArtifact(data, v, b, bm, a[0]-a[2], b[0]-b[2])
|
||||
));
|
||||
}
|
||||
|
||||
template <class ArtifactClassifier, template <typename> class ContourCombiner, int N>
|
||||
static bool hasDiagonalArtifact(ArtifactFinderData<ArtifactClassifier, ContourCombiner, N> &data, const Vector2 &v, const float *b, const float *c, const float *d) {
|
||||
const float *a = data.msd;
|
||||
float dm = median(d[0], d[1], d[2]);
|
||||
if (fabsf(data.psd-.5f) > fabsf(dm-.5f)) {
|
||||
float abc[3] = {
|
||||
a[0]-b[0]-c[0],
|
||||
a[1]-b[1]-c[1],
|
||||
a[2]-b[2]-c[2]
|
||||
};
|
||||
float lin[3] = {
|
||||
-a[0]-abc[0],
|
||||
-a[1]-abc[1],
|
||||
-a[2]-abc[2]
|
||||
};
|
||||
float quad[3] = {
|
||||
d[0]+abc[0],
|
||||
d[1]+abc[1],
|
||||
d[2]+abc[2]
|
||||
};
|
||||
double tEx[3] = {
|
||||
-.5*lin[0]/quad[0],
|
||||
-.5*lin[1]/quad[1],
|
||||
-.5*lin[2]/quad[2]
|
||||
};
|
||||
return (
|
||||
hasDiagonalArtifact(data, v, lin, quad, dm, a[1]-a[0], b[1]-b[0]+c[1]-c[0], d[1]-d[0], tEx[0], tEx[1]) ||
|
||||
hasDiagonalArtifact(data, v, lin, quad, dm, a[2]-a[1], b[2]-b[1]+c[2]-c[1], d[2]-d[1], tEx[1], tEx[2]) ||
|
||||
hasDiagonalArtifact(data, v, lin, quad, dm, a[0]-a[2], b[0]-b[2]+c[0]-c[2], d[0]-d[2], tEx[2], tEx[0])
|
||||
);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <template <typename> class ContourCombiner, int N, class ArtifactClassifier>
|
||||
static void msdfFindArtifacts(STENCIL_TYPE &stencil, const ArtifactClassifier &artifactClassifier, const BitmapConstRef<float, N> &sdf, const Shape &shape, const Projection &projection, double range) {
|
||||
if (ArtifactClassifier::observesProtected())
|
||||
flagProtected(stencil, sdf, shape, projection, range);
|
||||
Vector2 pDelta = projection.unprojectVector(Vector2(1));
|
||||
if (shape.inverseYAxis)
|
||||
pDelta.y = -pDelta.y;
|
||||
#ifdef MSDFGEN_USE_OPENMP
|
||||
#pragma omp parallel
|
||||
#endif
|
||||
{
|
||||
ArtifactFinderData<ArtifactClassifier, ContourCombiner, N> data(artifactClassifier,sdf, shape, range, pDelta);
|
||||
bool rightToLeft = false;
|
||||
#ifdef MSDFGEN_USE_OPENMP
|
||||
#pragma omp for
|
||||
#endif
|
||||
for (int y = 0; y < sdf.height; ++y) {
|
||||
int row = shape.inverseYAxis ? sdf.height-y-1 : y;
|
||||
for (int col = 0; col < sdf.width; ++col) {
|
||||
int x = rightToLeft ? sdf.width-col-1 : col;
|
||||
data.p = projection.unproject(Point2(x+.5, y+.5));
|
||||
data.sdfCoord = Point2(x+.5, row+.5);
|
||||
data.msd = sdf(x, row);
|
||||
data.psd = median(data.msd[0], data.msd[1], data.msd[2]);
|
||||
data.isProtected = stencil[STENCIL_INDEX(x, row)] != 0;
|
||||
const float *l = NULL, *b = NULL, *r = NULL, *t = NULL;
|
||||
stencil[STENCIL_INDEX(x, row)] = (
|
||||
(x > 0 && ((l = sdf(x-1, row)), hasLinearArtifact(data, Vector2(-1, 0), l))) ||
|
||||
(row > 0 && ((b = sdf(x, row-1)), hasLinearArtifact(data, Vector2(0, -1), b))) ||
|
||||
(x < sdf.width-1 && ((r = sdf(x+1, row)), hasLinearArtifact(data, Vector2(+1, 0), r))) ||
|
||||
(row < sdf.height-1 && ((t = sdf(x, row+1)), hasLinearArtifact(data, Vector2(0, +1), t))) ||
|
||||
(x > 0 && row > 0 && hasDiagonalArtifact(data, Vector2(-1, -1), l, b, sdf(x-1, row-1))) ||
|
||||
(x < sdf.width-1 && row > 0 && hasDiagonalArtifact(data, Vector2(+1, -1), r, b, sdf(x+1, row-1))) ||
|
||||
(x > 0 && row < sdf.height-1 && hasDiagonalArtifact(data, Vector2(-1, +1), l, t, sdf(x-1, row+1))) ||
|
||||
(x < sdf.width-1 && row < sdf.height-1 && hasDiagonalArtifact(data, Vector2(+1, +1), r, t, sdf(x+1, row+1)))
|
||||
);
|
||||
}
|
||||
rightToLeft = !rightToLeft;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <template <typename> class ContourCombiner, int N>
|
||||
static void msdfPatchArtifactsInner(const BitmapRef<float, N> &sdf, const Shape &shape, const Projection &projection, double range, const GeneratorConfig &generatorConfig, const ArtifactPatcherConfig &config) {
|
||||
if (config.mode == ArtifactPatcherConfig::DISABLED)
|
||||
return;
|
||||
STENCIL_TYPE stencil(sdf.width*sdf.height);
|
||||
switch (config.mode) {
|
||||
case ArtifactPatcherConfig::DISABLED:
|
||||
break;
|
||||
case ArtifactPatcherConfig::INDISCRIMINATE:
|
||||
msdfFindArtifacts<ContourCombiner, N>(stencil, IndiscriminateArtifactClassifier(config.minImproveRatio), sdf, shape, projection, range);
|
||||
break;
|
||||
case ArtifactPatcherConfig::EDGE_PRIORITY:
|
||||
msdfFindArtifacts<ContourCombiner, N>(stencil, EdgePriorityArtifactClassifier(config.minImproveRatio), sdf, shape, projection, range);
|
||||
break;
|
||||
case ArtifactPatcherConfig::EDGE_ONLY:
|
||||
msdfFindArtifacts<ContourCombiner, N>(stencil, EdgeOnlyArtifactClassifier(), sdf, shape, projection, range);
|
||||
break;
|
||||
}
|
||||
float *texel = sdf.pixels;
|
||||
for (int y = 0; y < sdf.height; ++y) {
|
||||
for (int x = 0; x < sdf.width; ++x) {
|
||||
if (stencil[STENCIL_INDEX(x, y)]) {
|
||||
float m = median(texel[0], texel[1], texel[2]);
|
||||
texel[0] = m, texel[1] = m, texel[2] = m;
|
||||
}
|
||||
texel += N;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void msdfPatchArtifacts(const BitmapRef<float, 3> &sdf, const Shape &shape, const Projection &projection, double range, const GeneratorConfig &generatorConfig, const ArtifactPatcherConfig &config) {
|
||||
if (generatorConfig.overlapSupport)
|
||||
msdfPatchArtifactsInner<OverlappingContourCombiner>(sdf, shape, projection, range);
|
||||
msdfPatchArtifactsInner<OverlappingContourCombiner>(sdf, shape, projection, range, generatorConfig, config);
|
||||
else
|
||||
msdfPatchArtifactsInner<SimpleContourCombiner>(sdf, shape, projection, range);
|
||||
msdfPatchArtifactsInner<SimpleContourCombiner>(sdf, shape, projection, range, generatorConfig, config);
|
||||
}
|
||||
|
||||
void msdfPatchArtifacts(const BitmapRef<float, 4> &sdf, const Shape &shape, const Projection &projection, double range, const GeneratorConfig &generatorConfig, const ArtifactPatcherConfig &config) {
|
||||
if (generatorConfig.overlapSupport)
|
||||
msdfPatchArtifactsInner<OverlappingContourCombiner>(sdf, shape, projection, range);
|
||||
msdfPatchArtifactsInner<OverlappingContourCombiner>(sdf, shape, projection, range, generatorConfig, config);
|
||||
else
|
||||
msdfPatchArtifactsInner<SimpleContourCombiner>(sdf, shape, projection, range);
|
||||
msdfPatchArtifactsInner<SimpleContourCombiner>(sdf, shape, projection, range, generatorConfig, config);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,38 +14,45 @@ class DistancePixelConversion;
|
|||
|
||||
template <>
|
||||
class DistancePixelConversion<double> {
|
||||
double invRange;
|
||||
public:
|
||||
typedef BitmapRef<float, 1> BitmapRefType;
|
||||
inline static void convert(float *pixels, double distance, double range) {
|
||||
*pixels = float(distance/range+.5);
|
||||
inline explicit DistancePixelConversion(double range) : invRange(1/range) { }
|
||||
inline void operator()(float *pixels, double distance) const {
|
||||
*pixels = float(invRange*distance+.5);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
class DistancePixelConversion<MultiDistance> {
|
||||
double invRange;
|
||||
public:
|
||||
typedef BitmapRef<float, 3> BitmapRefType;
|
||||
inline static void convert(float *pixels, const MultiDistance &distance, double range) {
|
||||
pixels[0] = float(distance.r/range+.5);
|
||||
pixels[1] = float(distance.g/range+.5);
|
||||
pixels[2] = float(distance.b/range+.5);
|
||||
inline explicit DistancePixelConversion(double range) : invRange(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);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
class DistancePixelConversion<MultiAndTrueDistance> {
|
||||
double invRange;
|
||||
public:
|
||||
typedef BitmapRef<float, 4> BitmapRefType;
|
||||
inline static void convert(float *pixels, const MultiAndTrueDistance &distance, double range) {
|
||||
pixels[0] = float(distance.r/range+.5);
|
||||
pixels[1] = float(distance.g/range+.5);
|
||||
pixels[2] = float(distance.b/range+.5);
|
||||
pixels[3] = float(distance.a/range+.5);
|
||||
inline explicit DistancePixelConversion(double range) : invRange(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);
|
||||
}
|
||||
};
|
||||
|
||||
template <class ContourCombiner>
|
||||
void generateDistanceField(const typename DistancePixelConversion<typename ContourCombiner::DistanceType>::BitmapRefType &output, const Shape &shape, const Projection &projection, double range) {
|
||||
DistancePixelConversion<typename ContourCombiner::DistanceType> distancePixelConversion(range);
|
||||
#ifdef MSDFGEN_USE_OPENMP
|
||||
#pragma omp parallel
|
||||
#endif
|
||||
|
|
@ -61,7 +68,7 @@ void generateDistanceField(const typename DistancePixelConversion<typename Conto
|
|||
int x = rightToLeft ? output.width-col-1 : col;
|
||||
Point2 p = projection.unproject(Point2(x+.5, y+.5));
|
||||
typename ContourCombiner::DistanceType distance = distanceFinder.distance(p);
|
||||
DistancePixelConversion<typename ContourCombiner::DistanceType>::convert(output(x, row), distance, range);
|
||||
distancePixelConversion(output(x, row), distance);
|
||||
}
|
||||
rightToLeft = !rightToLeft;
|
||||
}
|
||||
|
|
@ -87,8 +94,6 @@ void generateMSDF(const BitmapRef<float, 3> &output, const Shape &shape, const P
|
|||
generateDistanceField<OverlappingContourCombiner<MultiDistanceSelector> >(output, shape, projection, range);
|
||||
else
|
||||
generateDistanceField<SimpleContourCombiner<MultiDistanceSelector> >(output, shape, projection, range);
|
||||
if (artifactPatcherConfig.minImproveRatio > 0) // TEMPORARILY SERVES AS ERROR CORRECTION THRESHOLD
|
||||
msdfErrorCorrection(output, artifactPatcherConfig.minImproveRatio, projection, range);
|
||||
msdfPatchArtifacts(output, shape, projection, range, config, artifactPatcherConfig);
|
||||
}
|
||||
|
||||
|
|
@ -97,8 +102,6 @@ void generateMTSDF(const BitmapRef<float, 4> &output, const Shape &shape, const
|
|||
generateDistanceField<OverlappingContourCombiner<MultiAndTrueDistanceSelector> >(output, shape, projection, range);
|
||||
else
|
||||
generateDistanceField<SimpleContourCombiner<MultiAndTrueDistanceSelector> >(output, shape, projection, range);
|
||||
if (artifactPatcherConfig.minImproveRatio > 0) // TEMPORARILY SERVES AS ERROR CORRECTION THRESHOLD
|
||||
msdfErrorCorrection(output, artifactPatcherConfig.minImproveRatio, projection, range);
|
||||
msdfPatchArtifacts(output, shape, projection, range, config, artifactPatcherConfig);
|
||||
}
|
||||
|
||||
|
|
|
|||
14
main.cpp
14
main.cpp
|
|
@ -341,6 +341,8 @@ static const char *helpText =
|
|||
" -overlap\n"
|
||||
"\tSwitches to distance field generator with support for overlapping contours.\n"
|
||||
#endif
|
||||
" -patchartifacts <balanced / edge / distance / none>\n"
|
||||
"\tSelects the strategy used to patch the MSDF to prevent interpolation artifacts.\n"
|
||||
" -printmetrics\n"
|
||||
"\tPrints relevant metrics of the shape to the standard output.\n"
|
||||
" -pxrange <range>\n"
|
||||
|
|
@ -636,6 +638,16 @@ int main(int argc, const char * const *argv) {
|
|||
argPos += 2;
|
||||
continue;
|
||||
}
|
||||
ARG_CASE("-patchartifacts", 1) {
|
||||
if (!strcmp(argv[argPos+1], "none") || !strcmp(argv[argPos+1], "disabled")) artifactPatcherConfig.mode = ArtifactPatcherConfig::DISABLED;
|
||||
else if (!strcmp(argv[argPos+1], "distance") || !strcmp(argv[argPos+1], "indiscriminate") || !strcmp(argv[argPos+1], "all")) artifactPatcherConfig.mode = ArtifactPatcherConfig::INDISCRIMINATE;
|
||||
else if (!strcmp(argv[argPos+1], "balanced") || !strcmp(argv[argPos+1], "edgepriority") || !strcmp(argv[argPos+1], "auto")) artifactPatcherConfig.mode = ArtifactPatcherConfig::EDGE_PRIORITY;
|
||||
else if (!strcmp(argv[argPos+1], "edge") || !strcmp(argv[argPos+1], "edgeonly") || !strcmp(argv[argPos+1], "fast")) artifactPatcherConfig.mode = ArtifactPatcherConfig::EDGE_ONLY;
|
||||
else
|
||||
puts("Unknown artifact patcher strategy specified.");
|
||||
argPos += 2;
|
||||
continue;
|
||||
}
|
||||
ARG_CASE("-errorcorrection", 1) {
|
||||
double ect;
|
||||
if (!parseDouble(ect, argv[argPos+1]) && (ect >= 1 || ect == 0))
|
||||
|
|
@ -885,7 +897,7 @@ int main(int argc, const char * const *argv) {
|
|||
Bitmap<float, 1> sdf;
|
||||
Bitmap<float, 3> msdf;
|
||||
Bitmap<float, 4> mtsdf;
|
||||
artifactPatcherConfig.minImproveRatio = errorCorrectionThreshold; // TEMPORARILY SERVES AS ERROR CORRECTION THRESHOLD
|
||||
artifactPatcherConfig.minImproveRatio = errorCorrectionThreshold; // TEMPORARY
|
||||
switch (mode) {
|
||||
case SINGLE: {
|
||||
sdf = Bitmap<float, 1>(width, height);
|
||||
|
|
|
|||
Loading…
Reference in New Issue