mirror of https://github.com/Chlumsky/msdfgen.git
Complete rework of MSDF error correction, merged with artifact patcher
This commit is contained in:
parent
37a1fff421
commit
5f27bdfb1c
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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" />
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -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) { }
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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());
|
||||
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
26
main.cpp
26
main.cpp
|
|
@ -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:;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Reference in New Issue