Complete rework of MSDF error correction, merged with artifact patcher

This commit is contained in:
Viktor Chlumský 2021-04-06 01:20:03 +02:00
parent 37a1fff421
commit 5f27bdfb1c
13 changed files with 537 additions and 267 deletions

View File

@ -57,13 +57,13 @@
## Version 1.3 (2016-12-07)
- Fixed `-reverseorder` switch.
- Fixed glyph loading to use the proper method of acquiring the outlines from FreeType.
- Fixed glyph loading to use the proper method of acquiring outlines from FreeType.
## Version 1.2 (2016-07-20)
- Added option to specify that shape vertices are listed in reverse order (`-reverseorder`).
- Added option to set a seed for the edge coloring heuristic (-seed \<n\>), which can be used to adjust the output.
- Fixed parsing of glyph contours starting that start with a curve control point.
- Fixed parsing of glyph contours that start with a curve control point.
## Version 1.1 (2016-05-08)

View File

@ -466,9 +466,9 @@
<ClInclude Include="core\equation-solver.h" />
<ClInclude Include="core\generator-config.h" />
<ClInclude Include="core\msdf-error-correction.h" />
<ClInclude Include="core\MSDFErrorCorrection.h" />
<ClInclude Include="core\Projection.h" />
<ClInclude Include="core\sdf-error-estimation.h" />
<ClInclude Include="core\msdf-artifact-patcher.h" />
<ClInclude Include="core\pixel-conversion.hpp" />
<ClInclude Include="core\rasterization.h" />
<ClInclude Include="core\render-sdf.h" />
@ -498,9 +498,9 @@
<ClCompile Include="core\EdgeHolder.cpp" />
<ClCompile Include="core\equation-solver.cpp" />
<ClCompile Include="core\msdf-error-correction.cpp" />
<ClCompile Include="core\MSDFErrorCorrection.cpp" />
<ClCompile Include="core\Projection.cpp" />
<ClCompile Include="core\sdf-error-estimation.cpp" />
<ClCompile Include="core\msdf-artifact-patcher.cpp" />
<ClCompile Include="core\rasterization.cpp" />
<ClCompile Include="core\render-sdf.cpp" />
<ClCompile Include="core\save-bmp.cpp" />

View File

@ -123,10 +123,10 @@
<ClInclude Include="core\Projection.h">
<Filter>Core</Filter>
</ClInclude>
<ClInclude Include="core\msdf-artifact-patcher.h">
<ClInclude Include="core\generator-config.h">
<Filter>Core</Filter>
</ClInclude>
<ClInclude Include="core\generator-config.h">
<ClInclude Include="core\MSDFErrorCorrection.h">
<Filter>Core</Filter>
</ClInclude>
</ItemGroup>
@ -209,10 +209,10 @@
<ClCompile Include="ext\resolve-shape-geometry.cpp">
<Filter>Extensions</Filter>
</ClCompile>
<ClCompile Include="core\msdf-artifact-patcher.cpp">
<ClCompile Include="core\Projection.cpp">
<Filter>Core</Filter>
</ClCompile>
<ClCompile Include="core\Projection.cpp">
<ClCompile Include="core\MSDFErrorCorrection.cpp">
<Filter>Core</Filter>
</ClCompile>
</ItemGroup>

View File

@ -0,0 +1,338 @@
#include "MSDFErrorCorrection.h"
#include <cstring>
#include "arithmetics.hpp"
#include "equation-solver.h"
#include "EdgeColor.h"
namespace msdfgen {
#define ARTIFACT_T_EPSILON .01
#define PROTECTION_RADIUS_TOLERANCE 1.001
MSDFErrorCorrection::MSDFErrorCorrection() { }
MSDFErrorCorrection::MSDFErrorCorrection(const BitmapRef<byte, 1> &stencil) : stencil(stencil) {
memset(stencil.pixels, 0, sizeof(byte)*stencil.width*stencil.height);
}
void MSDFErrorCorrection::protectCorners(const Shape &shape, const Projection &projection) {
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 the color changes from prevEdge to edge, this is a corner.
if (!(commonColor&(commonColor-1))) {
// Find the four texels that envelop the corner and mark them as protected.
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 r = l+1;
int t = b+1;
// Check that the positions are within bounds.
if (l < stencil.width && b < stencil.height && r >= 0 && t >= 0) {
if (l >= 0 && b >= 0)
*stencil(l, b) |= (byte) PROTECTED;
if (r < stencil.width && b >= 0)
*stencil(r, b) |= (byte) PROTECTED;
if (l >= 0 && t < stencil.height)
*stencil(l, t) |= (byte) PROTECTED;
if (r < stencil.width && t < stencil.height)
*stencil(r, t) |= (byte) PROTECTED;
}
}
prevEdge = *edge;
}
}
}
/// 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]);
if (t > 0 && t < 1) {
// Interpolate channel values at t.
float c[3] = {
mix(a[0], b[0], t),
mix(a[1], b[1], t),
mix(a[2], b[2], t)
};
// This is only an edge if the zero-distance channel is the median.
return median(c[0], c[1], c[2]) == c[channel];
}
return false;
}
/// Returns a bit mask of which channels contribute to an edge between the two texels a, b.
static int edgeBetweenTexels(const float *a, const float *b) {
return (
RED*edgeBetweenTexelsChannel(a, b, 0)+
GREEN*edgeBetweenTexelsChannel(a, b, 1)+
BLUE*edgeBetweenTexelsChannel(a, b, 2)
);
}
/// Marks texel as protected if one of its non-median channels is present in the channel mask.
static void protectExtremeChannels(byte *stencil, const float *msd, float m, int mask) {
if (
(mask&RED && msd[0] != m) ||
(mask&GREEN && msd[1] != m) ||
(mask&BLUE && msd[2] != m)
)
*stencil |= (byte) MSDFErrorCorrection::PROTECTED;
}
template <int N>
void MSDFErrorCorrection::protectEdges(const BitmapConstRef<float, N> &sdf, const Projection &projection, double range) {
float radius;
// Horizontal texel pairs
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) {
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) {
int mask = edgeBetweenTexels(left, right);
protectExtremeChannels(stencil(x, y), left, lm, mask);
protectExtremeChannels(stencil(x+1, y), right, rm, mask);
}
left += N, right += N;
}
}
// Vertical texel pairs
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) {
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) {
int mask = edgeBetweenTexels(bottom, top);
protectExtremeChannels(stencil(x, y), bottom, bm, mask);
protectExtremeChannels(stencil(x, y+1), top, tm, mask);
}
bottom += N, top += N;
}
}
// Diagonal texel pairs
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) {
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) {
int mask = edgeBetweenTexels(lb, rt);
protectExtremeChannels(stencil(x, y), lb, mlb, mask);
protectExtremeChannels(stencil(x+1, y+1), rt, mrt, mask);
}
if (fabsf(mrb-.5f)+fabsf(mlt-.5f) < radius) {
int mask = edgeBetweenTexels(rb, lt);
protectExtremeChannels(stencil(x+1, y), rb, mrb, mask);
protectExtremeChannels(stencil(x, y+1), lt, mlt, mask);
}
lb += N, rb += N, lt += N, rt += N;
}
}
}
void MSDFErrorCorrection::protectAll() {
byte *end = stencil.pixels+stencil.width*stencil.height;
for (byte *mask = stencil.pixels; mask < end; ++mask)
*mask |= (byte) PROTECTED;
}
/// Returns the median of the linear interpolation of texels a, b at t.
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)
);
}
/// 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) {
return float(median(
t*(t*q[0]+l[0])+a[0],
t*(t*q[1]+l[1])+a[1],
t*(t*q[2]+l[2])+a[2]
));
}
/// Determines if the interpolated median xm is an artifact.
static bool isArtifact(bool isProtected, double axSpan, double 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)) &&
// This is an artifact if the interpolated median is outside the range of possible values based on its distance from a, b.
!(xm >= am-axSpan && xm <= am+axSpan && xm >= bm-bxSpan && xm <= bm+bxSpan)
);
}
/// Checks if a linear interpolation artifact will occur at a point where two specific color channels are equal - such points have extreme median values.
static bool hasLinearArtifactInner(double span, bool isProtected, 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);
// Interpolate median at t and determine if it deviates too much from medians of a, b.
return t > ARTIFACT_T_EPSILON && t < 1-ARTIFACT_T_EPSILON && isArtifact(isProtected, t*span, (1-t)*span, am, bm, interpolatedMedian(a, b, t));
}
/// Checks if a bilinear interpolation artifact will occur at a point where two specific color channels are equal - such points have extreme median values.
static bool hasDiagonalArtifactInner(double span, bool isProtected, float am, float dm, const float *a, const float *l, const float *q, float dA, float dBC, float dD, double tEx0, double tEx1) {
// Find interpolation ratios t (0 < t[i] < 1) where two color channels are equal.
double 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) {
// Interpolate median xm at t.
float xm = interpolatedMedian(a, l, q, t[i]);
// Determine if xm deviates too much from medians of a, d.
if (isArtifact(isProtected, t[i]*span, (1-t[i])*span, am, dm, xm))
return true;
// Additionally, check xm against the interpolated medians at the local extremes tEx0, tEx1.
double tEnd[2];
float em[2];
// tEx0
if (tEx0 > 0 && tEx0 < 1) {
tEnd[0] = 0, tEnd[1] = 1;
em[0] = am, em[1] = dm;
tEnd[tEx0 > t[i]] = tEx0;
em[tEx0 > t[i]] = interpolatedMedian(a, l, q, tEx0);
if (isArtifact(isProtected, (t[i]-tEnd[0])*span, (tEnd[1]-t[i])*span, em[0], em[1], xm))
return true;
}
// tEx1
if (tEx1 > 0 && tEx1 < 1) {
tEnd[0] = 0, tEnd[1] = 1;
em[0] = am, em[1] = dm;
tEnd[tEx1 > t[i]] = tEx1;
em[tEx1 > t[i]] = interpolatedMedian(a, l, q, tEx1);
if (isArtifact(isProtected, (t[i]-tEnd[0])*span, (tEnd[1]-t[i])*span, em[0], em[1], xm))
return true;
}
}
}
return false;
}
/// Checks if a linear interpolation artifact will occur inbetween two horizontally or vertically adjacent texels a, b.
static bool hasLinearArtifact(double span, bool isProtected, float am, const float *a, const float *b) {
float bm = median(b[0], b[1], b[2]);
return (
// Out of the pair, only report artifacts for the texel further from the edge to minimize side effects.
fabsf(am-.5f) > fabsf(bm-.5f) && (
// Check points where each pair of color channels meets.
hasLinearArtifactInner(span, isProtected, am, bm, a, b, a[1]-a[0], b[1]-b[0]) ||
hasLinearArtifactInner(span, isProtected, am, bm, a, b, a[2]-a[1], b[2]-b[1]) ||
hasLinearArtifactInner(span, isProtected, am, bm, a, b, a[0]-a[2], b[0]-b[2])
)
);
}
/// Checks if a bilinear interpolation artifact will occur inbetween two diagonally adjacent texels a, d (with b, c forming the other diagonal).
static bool hasDiagonalArtifact(double span, bool isProtected, float am, const float *a, const float *b, const float *c, const float *d) {
float dm = median(d[0], d[1], d[2]);
// Out of the pair, only report artifacts for the texel further from the edge to minimize side effects.
if (fabsf(am-.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]
};
// Compute the linear terms for bilinear interpolation.
float l[3] = {
-a[0]-abc[0],
-a[1]-abc[1],
-a[2]-abc[2]
};
// Compute the quadratic terms for bilinear interpolation.
float q[3] = {
d[0]+abc[0],
d[1]+abc[1],
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]
};
// Check points where each pair of color channels meets.
return (
hasDiagonalArtifactInner(span, isProtected, am, dm, a, l, q, a[1]-a[0], b[1]-b[0]+c[1]-c[0], d[1]-d[0], tEx[0], tEx[1]) ||
hasDiagonalArtifactInner(span, isProtected, am, dm, a, l, q, a[2]-a[1], b[2]-b[1]+c[2]-c[1], d[2]-d[1], tEx[1], tEx[2]) ||
hasDiagonalArtifactInner(span, isProtected, am, dm, a, l, q, a[0]-a[2], b[0]-b[2]+c[0]-c[2], d[0]-d[2], tEx[2], tEx[0])
);
}
return false;
}
template <int N>
void MSDFErrorCorrection::findErrors(const BitmapConstRef<float, N> &sdf, const Projection &projection, double range, double threshold) {
// Compute the expected deltas between values of horizontally, vertically, and diagonally adjacent texels.
double hSpan = threshold*projection.unprojectVector(Vector2(1/range, 0)).length();
double vSpan = threshold*projection.unprojectVector(Vector2(0, 1/range)).length();
double dSpan = threshold*projection.unprojectVector(Vector2(1/range)).length();
// Inspect all texels.
for (int y = 0; y < sdf.height; ++y) {
for (int x = 0; x < sdf.width; ++x) {
const float *c = sdf(x, y);
float cm = median(c[0], c[1], c[2]);
bool isProtected = (*stencil(x, y)&PROTECTED) != 0;
const float *l = NULL, *b = NULL, *r = NULL, *t = NULL;
// Mark current texel c with the error flag if an artifact occurs when it's interpolated with any of its 8 neighbors.
*stencil(x, y) |= (byte) (ERROR*(
(x > 0 && ((l = sdf(x-1, y)), hasLinearArtifact(hSpan, isProtected, cm, c, l))) ||
(y > 0 && ((b = sdf(x, y-1)), hasLinearArtifact(vSpan, isProtected, cm, c, b))) ||
(x < sdf.width-1 && ((r = sdf(x+1, y)), hasLinearArtifact(hSpan, isProtected, cm, c, r))) ||
(y < sdf.height-1 && ((t = sdf(x, y+1)), hasLinearArtifact(vSpan, isProtected, cm, c, t))) ||
(x > 0 && y > 0 && hasDiagonalArtifact(dSpan, isProtected, cm, c, l, b, sdf(x-1, y-1))) ||
(x < sdf.width-1 && y > 0 && hasDiagonalArtifact(dSpan, isProtected, cm, c, r, b, sdf(x+1, y-1))) ||
(x > 0 && y < sdf.height-1 && hasDiagonalArtifact(dSpan, isProtected, cm, c, l, t, sdf(x-1, y+1))) ||
(x < sdf.width-1 && y < sdf.height-1 && hasDiagonalArtifact(dSpan, isProtected, cm, c, r, t, sdf(x+1, y+1)))
));
}
}
}
template <int N>
void MSDFErrorCorrection::apply(const BitmapRef<float, N> &sdf) const {
int texelCount = sdf.width*sdf.height;
const byte *mask = stencil.pixels;
float *texel = sdf.pixels;
for (int i = 0; i < texelCount; ++i) {
if (*mask&ERROR) {
// Set all color channels to the median.
float m = median(texel[0], texel[1], texel[2]);
texel[0] = m, texel[1] = m, texel[2] = m;
}
++mask;
texel += N;
}
}
BitmapConstRef<byte, 1> MSDFErrorCorrection::getStencil() const {
return stencil;
}
template void MSDFErrorCorrection::protectEdges(const BitmapConstRef<float, 3> &sdf, const Projection &projection, double range);
template void MSDFErrorCorrection::protectEdges(const BitmapConstRef<float, 4> &sdf, const Projection &projection, double range);
template void MSDFErrorCorrection::findErrors(const BitmapConstRef<float, 3> &sdf, const Projection &projection, double range, double threshold);
template void MSDFErrorCorrection::findErrors(const BitmapConstRef<float, 4> &sdf, const Projection &projection, double range, double threshold);
template void MSDFErrorCorrection::apply(const BitmapRef<float, 3> &sdf) const;
template void MSDFErrorCorrection::apply(const BitmapRef<float, 4> &sdf) const;
}

View File

@ -0,0 +1,45 @@
#pragma once
#include "Projection.h"
#include "Shape.h"
#include "BitmapRef.hpp"
namespace msdfgen {
/// Performs error correction on a computed MSDF to eliminate interpolation artifacts. This is a low-level class, you may want to use the API in msdf-error-correction.h instead.
class MSDFErrorCorrection {
public:
/// Stencil flags.
enum Flags {
/// Texel marked as potentially causing interpolation errors.
ERROR = 1,
/// Texel marked as protected. Protected texels are only given the error flag if they cause inversion artifacts.
PROTECTED = 2
};
MSDFErrorCorrection();
explicit MSDFErrorCorrection(const BitmapRef<byte, 1> &stencil);
/// Flags all texels that are interpolated at corners as protected.
void protectCorners(const Shape &shape, const Projection &projection);
/// Flags all texels that contribute to edges as protected.
template <int N>
void protectEdges(const BitmapConstRef<float, N> &sdf, const Projection &projection, double range);
/// Flags all texels as protected.
void protectAll();
/// Flags texels that are expected to cause interpolation artifacts.
template <int N>
void findErrors(const BitmapConstRef<float, N> &sdf, const Projection &projection, double range, double threshold);
/// Modifies the MSDF so that all texels with the error flag are converted to single-channel.
template <int N>
void apply(const BitmapRef<float, N> &sdf) const;
/// Returns the stencil in its current state (see Flags).
BitmapConstRef<byte, 1> getStencil() const;
private:
BitmapRef<byte, 1> stencil;
};
}

View File

@ -1,7 +1,9 @@
#pragma once
#define MSDFGEN_DEFAULT_ARTIFACT_PATCHER_MIN_IMPROVE_RATIO 1.001
#include <cstdlib>
#define MSDFGEN_DEFAULT_ERROR_CORRECTION_THRESHOLD 1.001
namespace msdfgen {
@ -13,19 +15,25 @@ struct GeneratorConfig {
inline explicit GeneratorConfig(bool overlapSupport = true) : overlapSupport(overlapSupport) { }
};
/// The configuration of the artifact patcher that processes the generated MSDF and fixes potential interpolation errors.
struct ArtifactPatcherConfig {
/// The mode of operation.
/// The configuration of the MSDF error correction pass.
struct ErrorCorrectionConfig {
/// Mode of operation.
enum Mode {
/// Skips error correction pass.
DISABLED,
/// Corrects all discontinuities of the distance field regardless if edges are adversely affected.
INDISCRIMINATE,
/// Corrects artifacts at edges and other discontinuous distances only if it does not affect edges or corners.
EDGE_PRIORITY,
/// Only corrects artifacts at edges.
EDGE_ONLY
} mode;
/// The minimum ratio of improvement required to patch a pixel. Must be greater than or equal to 1.
double minImproveRatio;
/// The minimum ratio between the actual and maximum expected distance delta to be considered an error.
double threshold;
/// An optional buffer to avoid dynamic allocation. Must have at least as many bytes as the MSDF has pixels.
byte *buffer;
inline explicit ArtifactPatcherConfig(Mode mode = EDGE_PRIORITY, double minImproveRatio = MSDFGEN_DEFAULT_ARTIFACT_PATCHER_MIN_IMPROVE_RATIO) : mode(mode), minImproveRatio(minImproveRatio) { }
inline explicit ErrorCorrectionConfig(Mode mode = EDGE_PRIORITY, double threshold = MSDFGEN_DEFAULT_ERROR_CORRECTION_THRESHOLD, byte *buffer = NULL) : mode(mode), threshold(threshold), buffer(buffer) { }
};
}

View File

@ -1,174 +0,0 @@
#include "msdf-artifact-patcher.h"
#include <cstring>
#include <vector>
#include <utility>
#include "arithmetics.hpp"
#include "equation-solver.h"
#include "bitmap-interpolation.hpp"
#include "edge-selectors.h"
#include "contour-combiners.h"
#include "ShapeDistanceFinder.h"
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;
}
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;
}
return found;
}
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 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 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;
}
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
// Horizontal:
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));
left += N, right += N;
}
}
// Vertical:
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]));
bottom += N, top += N;
}
}
// Diagonal:
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]));
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);
// 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));
}
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;
}
}
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);
else
msdfPatchArtifactsInner<SimpleContourCombiner>(sdf, shape, projection, range);
}
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);
else
msdfPatchArtifactsInner<SimpleContourCombiner>(sdf, shape, projection, range);
}
}

View File

@ -1,15 +0,0 @@
#pragma once
#include "Vector2.h"
#include "Shape.h"
#include "Projection.h"
#include "BitmapRef.hpp"
#include "generator-config.h"
namespace msdfgen {
void msdfPatchArtifacts(const BitmapRef<float, 3> &sdf, const Shape &shape, const Projection &projection, double range, const GeneratorConfig &generatorConfig = GeneratorConfig(), const ArtifactPatcherConfig &config = ArtifactPatcherConfig());
void msdfPatchArtifacts(const BitmapRef<float, 4> &sdf, const Shape &shape, const Projection &projection, double range, const GeneratorConfig &generatorConfig = GeneratorConfig(), const ArtifactPatcherConfig &config = ArtifactPatcherConfig());
}

View File

@ -3,9 +3,72 @@
#include <vector>
#include "arithmetics.hpp"
#include "Bitmap.h"
#include "MSDFErrorCorrection.h"
namespace msdfgen {
template <int N>
static void msdfErrorCorrectionInner(const BitmapRef<float, N> &sdf, const Shape &shape, const Projection &projection, double range, const ErrorCorrectionConfig &config) {
if (config.mode == ErrorCorrectionConfig::DISABLED)
return;
Bitmap<byte, 1> stencilBuffer;
if (!config.buffer)
stencilBuffer = Bitmap<byte, 1>(sdf.width, sdf.height);
BitmapRef<byte, 1> stencil;
stencil.pixels = config.buffer ? config.buffer : (byte *) stencilBuffer;
stencil.width = sdf.width, stencil.height = sdf.height;
MSDFErrorCorrection ec(stencil);
switch (config.mode) {
case ErrorCorrectionConfig::DISABLED:
case ErrorCorrectionConfig::INDISCRIMINATE:
break;
case ErrorCorrectionConfig::EDGE_PRIORITY:
ec.protectCorners(shape, projection);
ec.protectEdges<N>(sdf, projection, range);
break;
case ErrorCorrectionConfig::EDGE_ONLY:
ec.protectAll();
break;
}
ec.findErrors<N>(sdf, projection, range, config.threshold);
ec.apply(sdf);
}
template <int N>
static void msdfErrorCorrectionShapeless(const BitmapRef<float, N> &sdf, const Projection &projection, double range, double threshold, bool protectAll) {
Bitmap<byte, 1> stencilBuffer(sdf.width, sdf.height);
MSDFErrorCorrection ec(stencilBuffer);
if (protectAll)
ec.protectAll();
ec.findErrors<N>(sdf, projection, range, threshold);
ec.apply(sdf);
}
void msdfErrorCorrection(const BitmapRef<float, 3> &sdf, const Shape &shape, const Projection &projection, double range, const ErrorCorrectionConfig &config) {
msdfErrorCorrectionInner(sdf, shape, projection, range, config);
}
void msdfErrorCorrection(const BitmapRef<float, 4> &sdf, const Shape &shape, const Projection &projection, double range, const ErrorCorrectionConfig &config) {
msdfErrorCorrectionInner(sdf, shape, projection, range, config);
}
void msdfDistanceErrorCorrection(const BitmapRef<float, 3> &sdf, const Projection &projection, double range, double threshold) {
msdfErrorCorrectionShapeless(sdf, projection, range, threshold, false);
}
void msdfDistanceErrorCorrection(const BitmapRef<float, 4> &sdf, const Projection &projection, double range, double threshold) {
msdfErrorCorrectionShapeless(sdf, projection, range, threshold, false);
}
void msdfEdgeErrorCorrection(const BitmapRef<float, 3> &sdf, const Projection &projection, double range, double threshold) {
msdfErrorCorrectionShapeless(sdf, projection, range, threshold, true);
}
void msdfEdgeErrorCorrection(const BitmapRef<float, 4> &sdf, const Projection &projection, double range, double threshold) {
msdfErrorCorrectionShapeless(sdf, projection, range, threshold, true);
}
// Legacy version
inline static bool detectClash(const float *a, const float *b, double 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];
@ -29,7 +92,7 @@ inline static bool detectClash(const float *a, const float *b, double threshold)
}
template <int N>
void msdfErrorCorrectionInner(const BitmapRef<float, N> &output, const Vector2 &threshold) {
static void msdfErrorCorrectionInner_legacy(const BitmapRef<float, N> &output, const Vector2 &threshold) {
std::vector<std::pair<int, int> > clashes;
int w = output.width, h = output.height;
for (int y = 0; y < h; ++y)
@ -67,18 +130,11 @@ void msdfErrorCorrectionInner(const BitmapRef<float, N> &output, const Vector2 &
#endif
}
void msdfErrorCorrection(const BitmapRef<float, 3> &output, const Vector2 &threshold) {
msdfErrorCorrectionInner(output, threshold);
void msdfErrorCorrection_legacy(const BitmapRef<float, 3> &output, const Vector2 &threshold) {
msdfErrorCorrectionInner_legacy(output, threshold);
}
void msdfErrorCorrection(const BitmapRef<float, 4> &output, const Vector2 &threshold) {
msdfErrorCorrectionInner(output, threshold);
}
void msdfErrorCorrection(const BitmapRef<float, 3> &output, double threshold, const Projection &projection, double range) {
msdfErrorCorrectionInner(output, projection.unprojectVector(Vector2(threshold/range)));
}
void msdfErrorCorrection(const BitmapRef<float, 4> &output, double threshold, const Projection &projection, double range) {
msdfErrorCorrectionInner(output, projection.unprojectVector(Vector2(threshold/range)));
void msdfErrorCorrection_legacy(const BitmapRef<float, 4> &output, const Vector2 &threshold) {
msdfErrorCorrectionInner_legacy(output, threshold);
}
}

View File

@ -3,18 +3,26 @@
#include "Vector2.h"
#include "Projection.h"
#include "Shape.h"
#include "BitmapRef.hpp"
#define MSDFGEN_DEFAULT_ERROR_CORRECTION_THRESHOLD 1.001
#include "generator-config.h"
namespace msdfgen {
/// Attempts to patch multi-channel signed distance field values that may cause interpolation artifacts. (Already called by generateMSDF)
void msdfErrorCorrection(const BitmapRef<float, 3> &output, const Vector2 &threshold);
void msdfErrorCorrection(const BitmapRef<float, 4> &output, const Vector2 &threshold);
/// 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 ErrorCorrectionConfig &config = ErrorCorrectionConfig());
void msdfErrorCorrection(const BitmapRef<float, 4> &sdf, const Shape &shape, const Projection &projection, double range, const ErrorCorrectionConfig &config = ErrorCorrectionConfig());
// Alternate API - threshold specified in pixels
void msdfErrorCorrection(const BitmapRef<float, 3> &output, double threshold, const Projection &projection, double range);
void msdfErrorCorrection(const BitmapRef<float, 4> &output, double threshold, const Projection &projection, double range);
/// Applies the error correction to all discontiunous distances (INDISCRIMINATE mode). Does not need shape or translation.
void msdfDistanceErrorCorrection(const BitmapRef<float, 3> &sdf, const Projection &projection, double range, double threshold = MSDFGEN_DEFAULT_ERROR_CORRECTION_THRESHOLD);
void msdfDistanceErrorCorrection(const BitmapRef<float, 4> &sdf, const Projection &projection, double range, double threshold = MSDFGEN_DEFAULT_ERROR_CORRECTION_THRESHOLD);
/// Applies the error correction to edges only (EDGE_ONLY mode). Does not need shape or translation.
void msdfEdgeErrorCorrection(const BitmapRef<float, 3> &sdf, const Projection &projection, double range, double threshold = MSDFGEN_DEFAULT_ERROR_CORRECTION_THRESHOLD);
void msdfEdgeErrorCorrection(const BitmapRef<float, 4> &sdf, const Projection &projection, double range, double threshold = MSDFGEN_DEFAULT_ERROR_CORRECTION_THRESHOLD);
/// The original version of the error correction algorithm.
void msdfErrorCorrection_legacy(const BitmapRef<float, 3> &output, const Vector2 &threshold);
void msdfErrorCorrection_legacy(const BitmapRef<float, 4> &output, const Vector2 &threshold);
}

View File

@ -5,7 +5,6 @@
#include "edge-selectors.h"
#include "contour-combiners.h"
#include "ShapeDistanceFinder.h"
#include "msdf-artifact-patcher.h"
namespace msdfgen {
@ -14,38 +13,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 +67,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;
}
@ -82,24 +88,20 @@ void generatePseudoSDF(const BitmapRef<float, 1> &output, const Shape &shape, co
generateDistanceField<SimpleContourCombiner<PseudoDistanceSelector> >(output, shape, projection, range);
}
void generateMSDF(const BitmapRef<float, 3> &output, const Shape &shape, const Projection &projection, double range, const GeneratorConfig &config, const ArtifactPatcherConfig &artifactPatcherConfig) {
void generateMSDF(const BitmapRef<float, 3> &output, const Shape &shape, const Projection &projection, double range, const GeneratorConfig &config, const ErrorCorrectionConfig &errorCorrectionConfig) {
if (config.overlapSupport)
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);
msdfErrorCorrection(output, shape, projection, range, errorCorrectionConfig);
}
void generateMTSDF(const BitmapRef<float, 4> &output, const Shape &shape, const Projection &projection, double range, const GeneratorConfig &config, const ArtifactPatcherConfig &artifactPatcherConfig) {
void generateMTSDF(const BitmapRef<float, 4> &output, const Shape &shape, const Projection &projection, double range, const GeneratorConfig &config, const ErrorCorrectionConfig &errorCorrectionConfig) {
if (config.overlapSupport)
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);
msdfErrorCorrection(output, shape, projection, range, errorCorrectionConfig);
}
// Legacy API
@ -112,12 +114,12 @@ void generatePseudoSDF(const BitmapRef<float, 1> &output, const Shape &shape, do
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, double errorMinImproveRatio, bool overlapSupport) {
generateMSDF(output, shape, Projection(scale, translate), range, GeneratorConfig(overlapSupport), ArtifactPatcherConfig(ArtifactPatcherConfig::EDGE_PRIORITY, errorMinImproveRatio));
void generateMSDF(const BitmapRef<float, 3> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, double errorCorrectionThreshold, bool overlapSupport) {
generateMSDF(output, shape, Projection(scale, translate), range, GeneratorConfig(overlapSupport), ErrorCorrectionConfig(ErrorCorrectionConfig::EDGE_PRIORITY, errorCorrectionThreshold));
}
void generateMTSDF(const BitmapRef<float, 4> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, double errorMinImproveRatio, bool overlapSupport) {
generateMTSDF(output, shape, Projection(scale, translate), range, GeneratorConfig(overlapSupport), ArtifactPatcherConfig(ArtifactPatcherConfig::EDGE_PRIORITY, errorMinImproveRatio));
void generateMTSDF(const BitmapRef<float, 4> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, double errorCorrectionThreshold, bool overlapSupport) {
generateMTSDF(output, shape, Projection(scale, translate), range, GeneratorConfig(overlapSupport), ErrorCorrectionConfig(ErrorCorrectionConfig::EDGE_PRIORITY, errorCorrectionThreshold));
}
// Legacy version
@ -222,7 +224,7 @@ void generateMSDF_legacy(const BitmapRef<float, 3> &output, const Shape &shape,
}
if (edgeThreshold > 0)
msdfErrorCorrection(output, edgeThreshold/(scale*range));
msdfErrorCorrection(output, shape, Projection(scale, translate), range, ErrorCorrectionConfig(ErrorCorrectionConfig::EDGE_PRIORITY, edgeThreshold));
}
void generateMTSDF_legacy(const BitmapRef<float, 4> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, double edgeThreshold) {
@ -280,7 +282,7 @@ void generateMTSDF_legacy(const BitmapRef<float, 4> &output, const Shape &shape,
}
if (edgeThreshold > 0)
msdfErrorCorrection(output, edgeThreshold/(scale*range));
msdfErrorCorrection(output, shape, Projection(scale, translate), range, ErrorCorrectionConfig(ErrorCorrectionConfig::EDGE_PRIORITY, edgeThreshold));
}
}

View File

@ -427,8 +427,7 @@ int main(int argc, const char * const *argv) {
Vector2 scale = 1;
bool scaleSpecified = false;
double angleThreshold = DEFAULT_ANGLE_THRESHOLD;
double errorCorrectionThreshold = MSDFGEN_DEFAULT_ERROR_CORRECTION_THRESHOLD;
ArtifactPatcherConfig artifactPatcherConfig;
ErrorCorrectionConfig errorCorrectionConfig;
float outputDistanceShift = 0.f;
const char *edgeAssignment = NULL;
bool yFlip = false;
@ -640,7 +639,10 @@ int main(int argc, const char * const *argv) {
double ect;
if (!parseDouble(ect, argv[argPos+1]) && (ect >= 1 || ect == 0))
ABORT("Invalid error correction threshold. Use -errorcorrection <threshold> with a real number greater than or equal to 1 or 0 to disable.");
errorCorrectionThreshold = ect;
if (ect <= 0)
errorCorrectionConfig.mode = ErrorCorrectionConfig::DISABLED;
else
errorCorrectionConfig.threshold = ect;
argPos += 2;
continue;
}
@ -885,7 +887,9 @@ 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
ErrorCorrectionConfig initialErrorCorrectionConfig(errorCorrectionConfig);
if (scanlinePass)
initialErrorCorrectionConfig.mode = ErrorCorrectionConfig::DISABLED;
switch (mode) {
case SINGLE: {
sdf = Bitmap<float, 1>(width, height);
@ -910,9 +914,9 @@ int main(int argc, const char * const *argv) {
parseColoring(shape, edgeAssignment);
msdf = Bitmap<float, 3>(width, height);
if (legacyMode)
generateMSDF_legacy(msdf, shape, range, scale, translate, scanlinePass ? 0 : errorCorrectionThreshold);
generateMSDF_legacy(msdf, shape, range, scale, translate, scanlinePass || errorCorrectionConfig.mode == ErrorCorrectionConfig::DISABLED ? 0 : errorCorrectionConfig.threshold);
else
generateMSDF(msdf, shape, projection, range, generatorConfig, artifactPatcherConfig);
generateMSDF(msdf, shape, projection, range, generatorConfig, initialErrorCorrectionConfig);
break;
}
case MULTI_AND_TRUE: {
@ -922,9 +926,9 @@ int main(int argc, const char * const *argv) {
parseColoring(shape, edgeAssignment);
mtsdf = Bitmap<float, 4>(width, height);
if (legacyMode)
generateMTSDF_legacy(mtsdf, shape, range, scale, translate, scanlinePass ? 0 : errorCorrectionThreshold);
generateMTSDF_legacy(mtsdf, shape, range, scale, translate, scanlinePass || errorCorrectionConfig.mode == ErrorCorrectionConfig::DISABLED ? 0 : errorCorrectionConfig.threshold);
else
generateMTSDF(mtsdf, shape, projection, range, generatorConfig, artifactPatcherConfig);
generateMTSDF(mtsdf, shape, projection, range, generatorConfig, initialErrorCorrectionConfig);
break;
}
default:;
@ -959,13 +963,11 @@ int main(int argc, const char * const *argv) {
break;
case MULTI:
distanceSignCorrection(msdf, shape, projection, fillRule);
if (errorCorrectionThreshold > 0)
msdfErrorCorrection(msdf, errorCorrectionThreshold/(scale*range));
msdfErrorCorrection(msdf, shape, projection, range, errorCorrectionConfig);
break;
case MULTI_AND_TRUE:
distanceSignCorrection(mtsdf, shape, projection, fillRule);
if (errorCorrectionThreshold > 0)
msdfErrorCorrection(mtsdf, errorCorrectionThreshold/(scale*range));
msdfErrorCorrection(msdf, shape, projection, range, errorCorrectionConfig);
break;
default:;
}

View File

@ -45,16 +45,16 @@ void generateSDF(const BitmapRef<float, 1> &output, const Shape &shape, const Pr
void generatePseudoSDF(const BitmapRef<float, 1> &output, const Shape &shape, const Projection &projection, double range, const GeneratorConfig &config = GeneratorConfig());
/// Generates a multi-channel signed distance field. Edge colors must be assigned first! (See edgeColoringSimple)
void generateMSDF(const BitmapRef<float, 3> &output, const Shape &shape, const Projection &projection, double range, const GeneratorConfig &config = GeneratorConfig(), const ArtifactPatcherConfig &artifactPatcherConfig = ArtifactPatcherConfig());
void generateMSDF(const BitmapRef<float, 3> &output, const Shape &shape, const Projection &projection, double range, const GeneratorConfig &config = GeneratorConfig(), const ErrorCorrectionConfig &errorCorrectionConfig = ErrorCorrectionConfig());
/// 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 GeneratorConfig &config = GeneratorConfig(), const ArtifactPatcherConfig &artifactPatcherConfig = ArtifactPatcherConfig());
void generateMTSDF(const BitmapRef<float, 4> &output, const Shape &shape, const Projection &projection, double range, const GeneratorConfig &config = GeneratorConfig(), const ErrorCorrectionConfig &errorCorrectionConfig = ErrorCorrectionConfig());
// 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, double errorMinImproveRatio = MSDFGEN_DEFAULT_ARTIFACT_PATCHER_MIN_IMPROVE_RATIO, bool overlapSupport = true);
void generateMTSDF(const BitmapRef<float, 4> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, double errorMinImproveRatio = MSDFGEN_DEFAULT_ARTIFACT_PATCHER_MIN_IMPROVE_RATIO, bool overlapSupport = true);
void generateMSDF(const BitmapRef<float, 3> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, double errorCorrectionThreshold = MSDFGEN_DEFAULT_ERROR_CORRECTION_THRESHOLD, bool overlapSupport = true);
void generateMTSDF(const BitmapRef<float, 4> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, double errorCorrectionThreshold = MSDFGEN_DEFAULT_ERROR_CORRECTION_THRESHOLD, 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);