Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/thirdparty/msdfgen/core/MSDFErrorCorrection.cpp
20836 views
1
2
#include "MSDFErrorCorrection.h"
3
4
#include <cstring>
5
#include "arithmetics.hpp"
6
#include "equation-solver.h"
7
#include "EdgeColor.h"
8
#include "bitmap-interpolation.hpp"
9
#include "edge-selectors.h"
10
#include "contour-combiners.h"
11
#include "ShapeDistanceFinder.h"
12
#include "generator-config.h"
13
14
namespace msdfgen {
15
16
#define ARTIFACT_T_EPSILON .01
17
#define PROTECTION_RADIUS_TOLERANCE 1.001
18
19
#define CLASSIFIER_FLAG_CANDIDATE 0x01
20
#define CLASSIFIER_FLAG_ARTIFACT 0x02
21
22
MSDFGEN_PUBLIC const double ErrorCorrectionConfig::defaultMinDeviationRatio = 1.11111111111111111;
23
MSDFGEN_PUBLIC const double ErrorCorrectionConfig::defaultMinImproveRatio = 1.11111111111111111;
24
25
/// The base artifact classifier recognizes artifacts based on the contents of the SDF alone.
26
class BaseArtifactClassifier {
27
public:
28
inline BaseArtifactClassifier(double span, bool protectedFlag) : span(span), protectedFlag(protectedFlag) { }
29
/// Evaluates if the median value xm interpolated at xt in the range between am at at and bm at bt indicates an artifact.
30
inline int rangeTest(double at, double bt, double xt, float am, float bm, float xm) const {
31
// For protected texels, only consider inversion artifacts (interpolated median has different sign than boundaries). For the rest, it is sufficient that the interpolated median is outside its boundaries.
32
if ((am > .5f && bm > .5f && xm <= .5f) || (am < .5f && bm < .5f && xm >= .5f) || (!protectedFlag && median(am, bm, xm) != xm)) {
33
double axSpan = (xt-at)*span, bxSpan = (bt-xt)*span;
34
// Check if the interpolated median's value is in the expected range based on its distance (span) from boundaries a, b.
35
if (!(xm >= am-axSpan && xm <= am+axSpan && xm >= bm-bxSpan && xm <= bm+bxSpan))
36
return CLASSIFIER_FLAG_CANDIDATE|CLASSIFIER_FLAG_ARTIFACT;
37
return CLASSIFIER_FLAG_CANDIDATE;
38
}
39
return 0;
40
}
41
/// Returns true if the combined results of the tests performed on the median value m interpolated at t indicate an artifact.
42
inline bool evaluate(double t, float m, int flags) const {
43
return (flags&2) != 0;
44
}
45
private:
46
double span;
47
bool protectedFlag;
48
};
49
50
/// The shape distance checker evaluates the exact shape distance to find additional artifacts at a significant performance cost.
51
template <template <typename> class ContourCombiner, int N>
52
class ShapeDistanceChecker {
53
public:
54
class ArtifactClassifier : public BaseArtifactClassifier {
55
public:
56
inline ArtifactClassifier(ShapeDistanceChecker *parent, const Vector2 &direction, double span) : BaseArtifactClassifier(span, parent->protectedFlag), parent(parent), direction(direction) { }
57
/// Returns true if the combined results of the tests performed on the median value m interpolated at t indicate an artifact.
58
inline bool evaluate(double t, float m, int flags) const {
59
if (flags&CLASSIFIER_FLAG_CANDIDATE) {
60
// Skip expensive distance evaluation if the point has already been classified as an artifact by the base classifier.
61
if (flags&CLASSIFIER_FLAG_ARTIFACT)
62
return true;
63
Vector2 tVector = t*direction;
64
float oldMSD[N], newMSD[3];
65
// Compute the color that would be currently interpolated at the artifact candidate's position.
66
Point2 sdfCoord = parent->sdfCoord+tVector;
67
interpolate(oldMSD, parent->sdf, sdfCoord);
68
// Compute the color that would be interpolated at the artifact candidate's position if error correction was applied on the current texel.
69
double aWeight = (1-fabs(tVector.x))*(1-fabs(tVector.y));
70
float aPSD = median(parent->msd[0], parent->msd[1], parent->msd[2]);
71
newMSD[0] = float(oldMSD[0]+aWeight*(aPSD-parent->msd[0]));
72
newMSD[1] = float(oldMSD[1]+aWeight*(aPSD-parent->msd[1]));
73
newMSD[2] = float(oldMSD[2]+aWeight*(aPSD-parent->msd[2]));
74
// Compute the evaluated distance (interpolated median) before and after error correction, as well as the exact shape distance.
75
float oldPSD = median(oldMSD[0], oldMSD[1], oldMSD[2]);
76
float newPSD = median(newMSD[0], newMSD[1], newMSD[2]);
77
float refPSD = float(parent->distanceMapping(parent->distanceFinder.distance(parent->shapeCoord+tVector*parent->texelSize)));
78
// Compare the differences of the exact distance and the before and after distances.
79
return parent->minImproveRatio*fabsf(newPSD-refPSD) < double(fabsf(oldPSD-refPSD));
80
}
81
return false;
82
}
83
private:
84
ShapeDistanceChecker *parent;
85
Vector2 direction;
86
};
87
Point2 shapeCoord, sdfCoord;
88
const float *msd;
89
bool protectedFlag;
90
inline ShapeDistanceChecker(const BitmapConstSection<float, N> &sdf, const Shape &shape, const Projection &projection, DistanceMapping distanceMapping, double minImproveRatio) : distanceFinder(shape), sdf(sdf), distanceMapping(distanceMapping), minImproveRatio(minImproveRatio) {
91
texelSize = projection.unprojectVector(Vector2(1));
92
}
93
inline ArtifactClassifier classifier(const Vector2 &direction, double span) {
94
return ArtifactClassifier(this, direction, span);
95
}
96
private:
97
ShapeDistanceFinder<ContourCombiner<PerpendicularDistanceSelector> > distanceFinder;
98
BitmapConstSection<float, N> sdf;
99
DistanceMapping distanceMapping;
100
Vector2 texelSize;
101
double minImproveRatio;
102
};
103
104
MSDFErrorCorrection::MSDFErrorCorrection() { }
105
106
MSDFErrorCorrection::MSDFErrorCorrection(const BitmapSection<byte, 1> &stencil, const SDFTransformation &transformation) : stencil(stencil), transformation(transformation) {
107
minDeviationRatio = ErrorCorrectionConfig::defaultMinDeviationRatio;
108
minImproveRatio = ErrorCorrectionConfig::defaultMinImproveRatio;
109
for (int y = 0; y < stencil.height; ++y)
110
memset(stencil(0, y), 0, sizeof(byte)*stencil.width);
111
}
112
113
void MSDFErrorCorrection::setMinDeviationRatio(double minDeviationRatio) {
114
this->minDeviationRatio = minDeviationRatio;
115
}
116
117
void MSDFErrorCorrection::setMinImproveRatio(double minImproveRatio) {
118
this->minImproveRatio = minImproveRatio;
119
}
120
121
void MSDFErrorCorrection::protectCorners(const Shape &shape) {
122
stencil.reorient(shape.getYAxisOrientation());
123
for (std::vector<Contour>::const_iterator contour = shape.contours.begin(); contour != shape.contours.end(); ++contour)
124
if (!contour->edges.empty()) {
125
const EdgeSegment *prevEdge = contour->edges.back();
126
for (std::vector<EdgeHolder>::const_iterator edge = contour->edges.begin(); edge != contour->edges.end(); ++edge) {
127
int commonColor = prevEdge->color&(*edge)->color;
128
// If the color changes from prevEdge to edge, this is a corner.
129
if (!(commonColor&(commonColor-1))) {
130
// Find the four texels that envelop the corner and mark them as protected.
131
Point2 p = transformation.project((*edge)->point(0));
132
int l = (int) floor(p.x-.5);
133
int b = (int) floor(p.y-.5);
134
int r = l+1;
135
int t = b+1;
136
// Check that the positions are within bounds.
137
if (l < stencil.width && b < stencil.height && r >= 0 && t >= 0) {
138
if (l >= 0 && b >= 0)
139
*stencil(l, b) |= (byte) PROTECTED;
140
if (r < stencil.width && b >= 0)
141
*stencil(r, b) |= (byte) PROTECTED;
142
if (l >= 0 && t < stencil.height)
143
*stencil(l, t) |= (byte) PROTECTED;
144
if (r < stencil.width && t < stencil.height)
145
*stencil(r, t) |= (byte) PROTECTED;
146
}
147
}
148
prevEdge = *edge;
149
}
150
}
151
}
152
153
/// Determines if the channel contributes to an edge between the two texels a, b.
154
static bool edgeBetweenTexelsChannel(const float *a, const float *b, int channel) {
155
// Find interpolation ratio t (0 < t < 1) where an edge is expected (mix(a[channel], b[channel], t) == 0.5).
156
double t = (a[channel]-.5)/(a[channel]-b[channel]);
157
if (t > 0 && t < 1) {
158
// Interpolate channel values at t.
159
float c[3] = {
160
mix(a[0], b[0], t),
161
mix(a[1], b[1], t),
162
mix(a[2], b[2], t)
163
};
164
// This is only an edge if the zero-distance channel is the median.
165
return median(c[0], c[1], c[2]) == c[channel];
166
}
167
return false;
168
}
169
170
/// Returns a bit mask of which channels contribute to an edge between the two texels a, b.
171
static int edgeBetweenTexels(const float *a, const float *b) {
172
return (
173
RED*edgeBetweenTexelsChannel(a, b, 0)+
174
GREEN*edgeBetweenTexelsChannel(a, b, 1)+
175
BLUE*edgeBetweenTexelsChannel(a, b, 2)
176
);
177
}
178
179
/// Marks texel as protected if one of its non-median channels is present in the channel mask.
180
static void protectExtremeChannels(byte *stencil, const float *msd, float m, int mask) {
181
if (
182
(mask&RED && msd[0] != m) ||
183
(mask&GREEN && msd[1] != m) ||
184
(mask&BLUE && msd[2] != m)
185
)
186
*stencil |= (byte) MSDFErrorCorrection::PROTECTED;
187
}
188
189
template <int N>
190
void MSDFErrorCorrection::protectEdges(const BitmapConstSection<float, N> &sdf) {
191
float radius;
192
stencil.reorient(sdf.yOrientation);
193
// Horizontal texel pairs
194
radius = float(PROTECTION_RADIUS_TOLERANCE*transformation.unprojectVector(Vector2(transformation.distanceMapping(DistanceMapping::Delta(1)), 0)).length());
195
for (int y = 0; y < sdf.height; ++y) {
196
const float *left = sdf(0, y);
197
const float *right = sdf(1, y);
198
for (int x = 0; x < sdf.width-1; ++x) {
199
float lm = median(left[0], left[1], left[2]);
200
float rm = median(right[0], right[1], right[2]);
201
if (fabsf(lm-.5f)+fabsf(rm-.5f) < radius) {
202
int mask = edgeBetweenTexels(left, right);
203
protectExtremeChannels(stencil(x, y), left, lm, mask);
204
protectExtremeChannels(stencil(x+1, y), right, rm, mask);
205
}
206
left += N, right += N;
207
}
208
}
209
// Vertical texel pairs
210
radius = float(PROTECTION_RADIUS_TOLERANCE*transformation.unprojectVector(Vector2(0, transformation.distanceMapping(DistanceMapping::Delta(1)))).length());
211
for (int y = 0; y < sdf.height-1; ++y) {
212
const float *bottom = sdf(0, y);
213
const float *top = sdf(0, y+1);
214
for (int x = 0; x < sdf.width; ++x) {
215
float bm = median(bottom[0], bottom[1], bottom[2]);
216
float tm = median(top[0], top[1], top[2]);
217
if (fabsf(bm-.5f)+fabsf(tm-.5f) < radius) {
218
int mask = edgeBetweenTexels(bottom, top);
219
protectExtremeChannels(stencil(x, y), bottom, bm, mask);
220
protectExtremeChannels(stencil(x, y+1), top, tm, mask);
221
}
222
bottom += N, top += N;
223
}
224
}
225
// Diagonal texel pairs
226
radius = float(PROTECTION_RADIUS_TOLERANCE*transformation.unprojectVector(Vector2(transformation.distanceMapping(DistanceMapping::Delta(1)))).length());
227
for (int y = 0; y < sdf.height-1; ++y) {
228
const float *lb = sdf(0, y);
229
const float *rb = sdf(1, y);
230
const float *lt = sdf(0, y+1);
231
const float *rt = sdf(1, y+1);
232
for (int x = 0; x < sdf.width-1; ++x) {
233
float mlb = median(lb[0], lb[1], lb[2]);
234
float mrb = median(rb[0], rb[1], rb[2]);
235
float mlt = median(lt[0], lt[1], lt[2]);
236
float mrt = median(rt[0], rt[1], rt[2]);
237
if (fabsf(mlb-.5f)+fabsf(mrt-.5f) < radius) {
238
int mask = edgeBetweenTexels(lb, rt);
239
protectExtremeChannels(stencil(x, y), lb, mlb, mask);
240
protectExtremeChannels(stencil(x+1, y+1), rt, mrt, mask);
241
}
242
if (fabsf(mrb-.5f)+fabsf(mlt-.5f) < radius) {
243
int mask = edgeBetweenTexels(rb, lt);
244
protectExtremeChannels(stencil(x+1, y), rb, mrb, mask);
245
protectExtremeChannels(stencil(x, y+1), lt, mlt, mask);
246
}
247
lb += N, rb += N, lt += N, rt += N;
248
}
249
}
250
}
251
252
void MSDFErrorCorrection::protectAll() {
253
for (int y = 0; y < stencil.height; ++y) {
254
byte *mask = stencil(0, y);
255
for (int x = 0; x < stencil.width; ++x)
256
*mask++ |= (byte) PROTECTED;
257
}
258
}
259
260
/// Returns the median of the linear interpolation of texels a, b at t.
261
static float interpolatedMedian(const float *a, const float *b, double t) {
262
return median(
263
mix(a[0], b[0], t),
264
mix(a[1], b[1], t),
265
mix(a[2], b[2], t)
266
);
267
}
268
/// Returns the median of the bilinear interpolation with the given constant, linear, and quadratic terms at t.
269
static float interpolatedMedian(const float *a, const float *l, const float *q, double t) {
270
return float(median(
271
t*(t*q[0]+l[0])+a[0],
272
t*(t*q[1]+l[1])+a[1],
273
t*(t*q[2]+l[2])+a[2]
274
));
275
}
276
277
/// Checks if a linear interpolation artifact will occur at a point where two specific color channels are equal - such points have extreme median values.
278
template <class ArtifactClassifier>
279
static bool hasLinearArtifactInner(const ArtifactClassifier &artifactClassifier, float am, float bm, const float *a, const float *b, float dA, float dB) {
280
// Find interpolation ratio t (0 < t < 1) where two color channels are equal (mix(dA, dB, t) == 0).
281
double t = (double) dA/(dA-dB);
282
if (t > ARTIFACT_T_EPSILON && t < 1-ARTIFACT_T_EPSILON) {
283
// Interpolate median at t and let the classifier decide if its value indicates an artifact.
284
float xm = interpolatedMedian(a, b, t);
285
return artifactClassifier.evaluate(t, xm, artifactClassifier.rangeTest(0, 1, t, am, bm, xm));
286
}
287
return false;
288
}
289
290
/// Checks if a bilinear interpolation artifact will occur at a point where two specific color channels are equal - such points have extreme median values.
291
template <class ArtifactClassifier>
292
static bool hasDiagonalArtifactInner(const ArtifactClassifier &artifactClassifier, float am, float dm, const float *a, const float *l, const float *q, float dA, float dBC, float dD, double tEx0, double tEx1) {
293
// Find interpolation ratios t (0 < t[i] < 1) where two color channels are equal.
294
double t[2];
295
int solutions = solveQuadratic(t, dD-dBC+dA, dBC-dA-dA, dA);
296
for (int i = 0; i < solutions; ++i) {
297
// Solutions t[i] == 0 and t[i] == 1 are singularities and occur very often because two channels are usually equal at texels.
298
if (t[i] > ARTIFACT_T_EPSILON && t[i] < 1-ARTIFACT_T_EPSILON) {
299
// Interpolate median xm at t.
300
float xm = interpolatedMedian(a, l, q, t[i]);
301
// Determine if xm deviates too much from medians of a, d.
302
int rangeFlags = artifactClassifier.rangeTest(0, 1, t[i], am, dm, xm);
303
// Additionally, check xm against the interpolated medians at the local extremes tEx0, tEx1.
304
double tEnd[2];
305
float em[2];
306
// tEx0
307
if (tEx0 > 0 && tEx0 < 1) {
308
tEnd[0] = 0, tEnd[1] = 1;
309
em[0] = am, em[1] = dm;
310
tEnd[tEx0 > t[i]] = tEx0;
311
em[tEx0 > t[i]] = interpolatedMedian(a, l, q, tEx0);
312
rangeFlags |= artifactClassifier.rangeTest(tEnd[0], tEnd[1], t[i], em[0], em[1], xm);
313
}
314
// tEx1
315
if (tEx1 > 0 && tEx1 < 1) {
316
tEnd[0] = 0, tEnd[1] = 1;
317
em[0] = am, em[1] = dm;
318
tEnd[tEx1 > t[i]] = tEx1;
319
em[tEx1 > t[i]] = interpolatedMedian(a, l, q, tEx1);
320
rangeFlags |= artifactClassifier.rangeTest(tEnd[0], tEnd[1], t[i], em[0], em[1], xm);
321
}
322
if (artifactClassifier.evaluate(t[i], xm, rangeFlags))
323
return true;
324
}
325
}
326
return false;
327
}
328
329
/// Checks if a linear interpolation artifact will occur inbetween two horizontally or vertically adjacent texels a, b.
330
template <class ArtifactClassifier>
331
static bool hasLinearArtifact(const ArtifactClassifier &artifactClassifier, float am, const float *a, const float *b) {
332
float bm = median(b[0], b[1], b[2]);
333
return (
334
// Out of the pair, only report artifacts for the texel further from the edge to minimize side effects.
335
fabsf(am-.5f) >= fabsf(bm-.5f) && (
336
// Check points where each pair of color channels meets.
337
hasLinearArtifactInner(artifactClassifier, am, bm, a, b, a[1]-a[0], b[1]-b[0]) ||
338
hasLinearArtifactInner(artifactClassifier, am, bm, a, b, a[2]-a[1], b[2]-b[1]) ||
339
hasLinearArtifactInner(artifactClassifier, am, bm, a, b, a[0]-a[2], b[0]-b[2])
340
)
341
);
342
}
343
344
/// Checks if a bilinear interpolation artifact will occur inbetween two diagonally adjacent texels a, d (with b, c forming the other diagonal).
345
template <class ArtifactClassifier>
346
static bool hasDiagonalArtifact(const ArtifactClassifier &artifactClassifier, float am, const float *a, const float *b, const float *c, const float *d) {
347
float dm = median(d[0], d[1], d[2]);
348
// Out of the pair, only report artifacts for the texel further from the edge to minimize side effects.
349
if (fabsf(am-.5f) >= fabsf(dm-.5f)) {
350
float abc[3] = {
351
a[0]-b[0]-c[0],
352
a[1]-b[1]-c[1],
353
a[2]-b[2]-c[2]
354
};
355
// Compute the linear terms for bilinear interpolation.
356
float l[3] = {
357
-a[0]-abc[0],
358
-a[1]-abc[1],
359
-a[2]-abc[2]
360
};
361
// Compute the quadratic terms for bilinear interpolation.
362
float q[3] = {
363
d[0]+abc[0],
364
d[1]+abc[1],
365
d[2]+abc[2]
366
};
367
// 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).
368
double tEx[3] = {
369
-.5*l[0]/q[0],
370
-.5*l[1]/q[1],
371
-.5*l[2]/q[2]
372
};
373
// Check points where each pair of color channels meets.
374
return (
375
hasDiagonalArtifactInner(artifactClassifier, 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]) ||
376
hasDiagonalArtifactInner(artifactClassifier, 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]) ||
377
hasDiagonalArtifactInner(artifactClassifier, 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])
378
);
379
}
380
return false;
381
}
382
383
template <int N>
384
void MSDFErrorCorrection::findErrors(const BitmapConstSection<float, N> &sdf) {
385
stencil.reorient(sdf.yOrientation);
386
// Compute the expected deltas between values of horizontally, vertically, and diagonally adjacent texels.
387
double hSpan = minDeviationRatio*transformation.unprojectVector(Vector2(transformation.distanceMapping(DistanceMapping::Delta(1)), 0)).length();
388
double vSpan = minDeviationRatio*transformation.unprojectVector(Vector2(0, transformation.distanceMapping(DistanceMapping::Delta(1)))).length();
389
double dSpan = minDeviationRatio*transformation.unprojectVector(Vector2(transformation.distanceMapping(DistanceMapping::Delta(1)))).length();
390
// Inspect all texels.
391
for (int y = 0; y < sdf.height; ++y) {
392
for (int x = 0; x < sdf.width; ++x) {
393
const float *c = sdf(x, y);
394
float cm = median(c[0], c[1], c[2]);
395
bool protectedFlag = (*stencil(x, y)&PROTECTED) != 0;
396
const float *l = NULL, *b = NULL, *r = NULL, *t = NULL;
397
// Mark current texel c with the error flag if an artifact occurs when it's interpolated with any of its 8 neighbors.
398
*stencil(x, y) |= (byte) (ERROR*(
399
(x > 0 && ((l = sdf(x-1, y)), hasLinearArtifact(BaseArtifactClassifier(hSpan, protectedFlag), cm, c, l))) ||
400
(y > 0 && ((b = sdf(x, y-1)), hasLinearArtifact(BaseArtifactClassifier(vSpan, protectedFlag), cm, c, b))) ||
401
(x < sdf.width-1 && ((r = sdf(x+1, y)), hasLinearArtifact(BaseArtifactClassifier(hSpan, protectedFlag), cm, c, r))) ||
402
(y < sdf.height-1 && ((t = sdf(x, y+1)), hasLinearArtifact(BaseArtifactClassifier(vSpan, protectedFlag), cm, c, t))) ||
403
(x > 0 && y > 0 && hasDiagonalArtifact(BaseArtifactClassifier(dSpan, protectedFlag), cm, c, l, b, sdf(x-1, y-1))) ||
404
(x < sdf.width-1 && y > 0 && hasDiagonalArtifact(BaseArtifactClassifier(dSpan, protectedFlag), cm, c, r, b, sdf(x+1, y-1))) ||
405
(x > 0 && y < sdf.height-1 && hasDiagonalArtifact(BaseArtifactClassifier(dSpan, protectedFlag), cm, c, l, t, sdf(x-1, y+1))) ||
406
(x < sdf.width-1 && y < sdf.height-1 && hasDiagonalArtifact(BaseArtifactClassifier(dSpan, protectedFlag), cm, c, r, t, sdf(x+1, y+1)))
407
));
408
}
409
}
410
}
411
412
template <template <typename> class ContourCombiner, int N>
413
void MSDFErrorCorrection::findErrors(BitmapConstSection<float, N> sdf, const Shape &shape) {
414
sdf.reorient(shape.getYAxisOrientation());
415
stencil.reorient(sdf.yOrientation);
416
// Compute the expected deltas between values of horizontally, vertically, and diagonally adjacent texels.
417
double hSpan = minDeviationRatio*transformation.unprojectVector(Vector2(transformation.distanceMapping(DistanceMapping::Delta(1)), 0)).length();
418
double vSpan = minDeviationRatio*transformation.unprojectVector(Vector2(0, transformation.distanceMapping(DistanceMapping::Delta(1)))).length();
419
double dSpan = minDeviationRatio*transformation.unprojectVector(Vector2(transformation.distanceMapping(DistanceMapping::Delta(1)))).length();
420
#ifdef MSDFGEN_USE_OPENMP
421
#pragma omp parallel
422
#endif
423
{
424
ShapeDistanceChecker<ContourCombiner, N> shapeDistanceChecker(sdf, shape, transformation, transformation.distanceMapping, minImproveRatio);
425
int xDirection = 1;
426
// Inspect all texels.
427
#ifdef MSDFGEN_USE_OPENMP
428
#pragma omp for
429
#endif
430
for (int y = 0; y < sdf.height; ++y) {
431
int x = xDirection < 0 ? sdf.width-1 : 0;
432
for (int col = 0; col < sdf.width; ++col, x += xDirection) {
433
if ((*stencil(x, y)&ERROR))
434
continue;
435
const float *c = sdf(x, y);
436
shapeDistanceChecker.shapeCoord = transformation.unproject(Point2(x+.5, y+.5));
437
shapeDistanceChecker.sdfCoord = Point2(x+.5, y+.5);
438
shapeDistanceChecker.msd = c;
439
shapeDistanceChecker.protectedFlag = (*stencil(x, y)&PROTECTED) != 0;
440
float cm = median(c[0], c[1], c[2]);
441
const float *l = NULL, *b = NULL, *r = NULL, *t = NULL;
442
// Mark current texel c with the error flag if an artifact occurs when it's interpolated with any of its 8 neighbors.
443
*stencil(x, y) |= (byte) (ERROR*(
444
(x > 0 && ((l = sdf(x-1, y)), hasLinearArtifact(shapeDistanceChecker.classifier(Vector2(-1, 0), hSpan), cm, c, l))) ||
445
(y > 0 && ((b = sdf(x, y-1)), hasLinearArtifact(shapeDistanceChecker.classifier(Vector2(0, -1), vSpan), cm, c, b))) ||
446
(x < sdf.width-1 && ((r = sdf(x+1, y)), hasLinearArtifact(shapeDistanceChecker.classifier(Vector2(+1, 0), hSpan), cm, c, r))) ||
447
(y < sdf.height-1 && ((t = sdf(x, y+1)), hasLinearArtifact(shapeDistanceChecker.classifier(Vector2(0, +1), vSpan), cm, c, t))) ||
448
(x > 0 && y > 0 && hasDiagonalArtifact(shapeDistanceChecker.classifier(Vector2(-1, -1), dSpan), cm, c, l, b, sdf(x-1, y-1))) ||
449
(x < sdf.width-1 && y > 0 && hasDiagonalArtifact(shapeDistanceChecker.classifier(Vector2(+1, -1), dSpan), cm, c, r, b, sdf(x+1, y-1))) ||
450
(x > 0 && y < sdf.height-1 && hasDiagonalArtifact(shapeDistanceChecker.classifier(Vector2(-1, +1), dSpan), cm, c, l, t, sdf(x-1, y+1))) ||
451
(x < sdf.width-1 && y < sdf.height-1 && hasDiagonalArtifact(shapeDistanceChecker.classifier(Vector2(+1, +1), dSpan), cm, c, r, t, sdf(x+1, y+1)))
452
));
453
}
454
xDirection = -xDirection;
455
}
456
}
457
}
458
459
template <int N>
460
void MSDFErrorCorrection::apply(BitmapSection<float, N> sdf) const {
461
sdf.reorient(stencil.yOrientation);
462
const byte *stencilRow = stencil.pixels;
463
float *rowStart = sdf.pixels;
464
for (int y = 0; y < sdf.height; ++y) {
465
const byte *mask = stencilRow;
466
float *pixel = rowStart;
467
for (int x = 0; x < sdf.width; ++x) {
468
if (*mask&ERROR) {
469
// Set all color channels to the median.
470
float m = median(pixel[0], pixel[1], pixel[2]);
471
pixel[0] = m, pixel[1] = m, pixel[2] = m;
472
}
473
++mask;
474
pixel += N;
475
}
476
stencilRow += stencil.rowStride;
477
rowStart += sdf.rowStride;
478
}
479
}
480
481
BitmapConstSection<byte, 1> MSDFErrorCorrection::getStencil() const {
482
return stencil;
483
}
484
485
template void MSDFErrorCorrection::protectEdges(const BitmapConstSection<float, 3> &sdf);
486
template void MSDFErrorCorrection::protectEdges(const BitmapConstSection<float, 4> &sdf);
487
template void MSDFErrorCorrection::findErrors(const BitmapConstSection<float, 3> &sdf);
488
template void MSDFErrorCorrection::findErrors(const BitmapConstSection<float, 4> &sdf);
489
template void MSDFErrorCorrection::findErrors<SimpleContourCombiner>(BitmapConstSection<float, 3> sdf, const Shape &shape);
490
template void MSDFErrorCorrection::findErrors<SimpleContourCombiner>(BitmapConstSection<float, 4> sdf, const Shape &shape);
491
template void MSDFErrorCorrection::findErrors<OverlappingContourCombiner>(BitmapConstSection<float, 3> sdf, const Shape &shape);
492
template void MSDFErrorCorrection::findErrors<OverlappingContourCombiner>(BitmapConstSection<float, 4> sdf, const Shape &shape);
493
template void MSDFErrorCorrection::apply(BitmapSection<float, 3> sdf) const;
494
template void MSDFErrorCorrection::apply(BitmapSection<float, 4> sdf) const;
495
496
}
497
498