Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/thirdparty/msdfgen/core/MSDFErrorCorrection.cpp
9903 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 BitmapConstRef<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
if (shape.inverseYAxis)
93
texelSize.y = -texelSize.y;
94
}
95
inline ArtifactClassifier classifier(const Vector2 &direction, double span) {
96
return ArtifactClassifier(this, direction, span);
97
}
98
private:
99
ShapeDistanceFinder<ContourCombiner<PerpendicularDistanceSelector> > distanceFinder;
100
BitmapConstRef<float, N> sdf;
101
DistanceMapping distanceMapping;
102
Vector2 texelSize;
103
double minImproveRatio;
104
};
105
106
MSDFErrorCorrection::MSDFErrorCorrection() { }
107
108
MSDFErrorCorrection::MSDFErrorCorrection(const BitmapRef<byte, 1> &stencil, const SDFTransformation &transformation) : stencil(stencil), transformation(transformation) {
109
minDeviationRatio = ErrorCorrectionConfig::defaultMinDeviationRatio;
110
minImproveRatio = ErrorCorrectionConfig::defaultMinImproveRatio;
111
memset(stencil.pixels, 0, sizeof(byte)*stencil.width*stencil.height);
112
}
113
114
void MSDFErrorCorrection::setMinDeviationRatio(double minDeviationRatio) {
115
this->minDeviationRatio = minDeviationRatio;
116
}
117
118
void MSDFErrorCorrection::setMinImproveRatio(double minImproveRatio) {
119
this->minImproveRatio = minImproveRatio;
120
}
121
122
void MSDFErrorCorrection::protectCorners(const Shape &shape) {
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
if (shape.inverseYAxis)
135
b = stencil.height-b-2;
136
int r = l+1;
137
int t = b+1;
138
// Check that the positions are within bounds.
139
if (l < stencil.width && b < stencil.height && r >= 0 && t >= 0) {
140
if (l >= 0 && b >= 0)
141
*stencil(l, b) |= (byte) PROTECTED;
142
if (r < stencil.width && b >= 0)
143
*stencil(r, b) |= (byte) PROTECTED;
144
if (l >= 0 && t < stencil.height)
145
*stencil(l, t) |= (byte) PROTECTED;
146
if (r < stencil.width && t < stencil.height)
147
*stencil(r, t) |= (byte) PROTECTED;
148
}
149
}
150
prevEdge = *edge;
151
}
152
}
153
}
154
155
/// Determines if the channel contributes to an edge between the two texels a, b.
156
static bool edgeBetweenTexelsChannel(const float *a, const float *b, int channel) {
157
// Find interpolation ratio t (0 < t < 1) where an edge is expected (mix(a[channel], b[channel], t) == 0.5).
158
double t = (a[channel]-.5)/(a[channel]-b[channel]);
159
if (t > 0 && t < 1) {
160
// Interpolate channel values at t.
161
float c[3] = {
162
mix(a[0], b[0], t),
163
mix(a[1], b[1], t),
164
mix(a[2], b[2], t)
165
};
166
// This is only an edge if the zero-distance channel is the median.
167
return median(c[0], c[1], c[2]) == c[channel];
168
}
169
return false;
170
}
171
172
/// Returns a bit mask of which channels contribute to an edge between the two texels a, b.
173
static int edgeBetweenTexels(const float *a, const float *b) {
174
return (
175
RED*edgeBetweenTexelsChannel(a, b, 0)+
176
GREEN*edgeBetweenTexelsChannel(a, b, 1)+
177
BLUE*edgeBetweenTexelsChannel(a, b, 2)
178
);
179
}
180
181
/// Marks texel as protected if one of its non-median channels is present in the channel mask.
182
static void protectExtremeChannels(byte *stencil, const float *msd, float m, int mask) {
183
if (
184
(mask&RED && msd[0] != m) ||
185
(mask&GREEN && msd[1] != m) ||
186
(mask&BLUE && msd[2] != m)
187
)
188
*stencil |= (byte) MSDFErrorCorrection::PROTECTED;
189
}
190
191
template <int N>
192
void MSDFErrorCorrection::protectEdges(const BitmapConstRef<float, N> &sdf) {
193
float radius;
194
// Horizontal texel pairs
195
radius = float(PROTECTION_RADIUS_TOLERANCE*transformation.unprojectVector(Vector2(transformation.distanceMapping(DistanceMapping::Delta(1)), 0)).length());
196
for (int y = 0; y < sdf.height; ++y) {
197
const float *left = sdf(0, y);
198
const float *right = sdf(1, y);
199
for (int x = 0; x < sdf.width-1; ++x) {
200
float lm = median(left[0], left[1], left[2]);
201
float rm = median(right[0], right[1], right[2]);
202
if (fabsf(lm-.5f)+fabsf(rm-.5f) < radius) {
203
int mask = edgeBetweenTexels(left, right);
204
protectExtremeChannels(stencil(x, y), left, lm, mask);
205
protectExtremeChannels(stencil(x+1, y), right, rm, mask);
206
}
207
left += N, right += N;
208
}
209
}
210
// Vertical texel pairs
211
radius = float(PROTECTION_RADIUS_TOLERANCE*transformation.unprojectVector(Vector2(0, transformation.distanceMapping(DistanceMapping::Delta(1)))).length());
212
for (int y = 0; y < sdf.height-1; ++y) {
213
const float *bottom = sdf(0, y);
214
const float *top = sdf(0, y+1);
215
for (int x = 0; x < sdf.width; ++x) {
216
float bm = median(bottom[0], bottom[1], bottom[2]);
217
float tm = median(top[0], top[1], top[2]);
218
if (fabsf(bm-.5f)+fabsf(tm-.5f) < radius) {
219
int mask = edgeBetweenTexels(bottom, top);
220
protectExtremeChannels(stencil(x, y), bottom, bm, mask);
221
protectExtremeChannels(stencil(x, y+1), top, tm, mask);
222
}
223
bottom += N, top += N;
224
}
225
}
226
// Diagonal texel pairs
227
radius = float(PROTECTION_RADIUS_TOLERANCE*transformation.unprojectVector(Vector2(transformation.distanceMapping(DistanceMapping::Delta(1)))).length());
228
for (int y = 0; y < sdf.height-1; ++y) {
229
const float *lb = sdf(0, y);
230
const float *rb = sdf(1, y);
231
const float *lt = sdf(0, y+1);
232
const float *rt = sdf(1, y+1);
233
for (int x = 0; x < sdf.width-1; ++x) {
234
float mlb = median(lb[0], lb[1], lb[2]);
235
float mrb = median(rb[0], rb[1], rb[2]);
236
float mlt = median(lt[0], lt[1], lt[2]);
237
float mrt = median(rt[0], rt[1], rt[2]);
238
if (fabsf(mlb-.5f)+fabsf(mrt-.5f) < radius) {
239
int mask = edgeBetweenTexels(lb, rt);
240
protectExtremeChannels(stencil(x, y), lb, mlb, mask);
241
protectExtremeChannels(stencil(x+1, y+1), rt, mrt, mask);
242
}
243
if (fabsf(mrb-.5f)+fabsf(mlt-.5f) < radius) {
244
int mask = edgeBetweenTexels(rb, lt);
245
protectExtremeChannels(stencil(x+1, y), rb, mrb, mask);
246
protectExtremeChannels(stencil(x, y+1), lt, mlt, mask);
247
}
248
lb += N, rb += N, lt += N, rt += N;
249
}
250
}
251
}
252
253
void MSDFErrorCorrection::protectAll() {
254
byte *end = stencil.pixels+stencil.width*stencil.height;
255
for (byte *mask = stencil.pixels; mask < end; ++mask)
256
*mask |= (byte) PROTECTED;
257
}
258
259
/// Returns the median of the linear interpolation of texels a, b at t.
260
static float interpolatedMedian(const float *a, const float *b, double t) {
261
return median(
262
mix(a[0], b[0], t),
263
mix(a[1], b[1], t),
264
mix(a[2], b[2], t)
265
);
266
}
267
/// Returns the median of the bilinear interpolation with the given constant, linear, and quadratic terms at t.
268
static float interpolatedMedian(const float *a, const float *l, const float *q, double t) {
269
return float(median(
270
t*(t*q[0]+l[0])+a[0],
271
t*(t*q[1]+l[1])+a[1],
272
t*(t*q[2]+l[2])+a[2]
273
));
274
}
275
276
/// Determines if the interpolated median xm is an artifact.
277
static bool isArtifact(bool isProtected, double axSpan, double bxSpan, float am, float bm, float xm) {
278
return (
279
// For protected texels, only report an artifact if it would cause fill inversion (change between positive and negative distance).
280
(!isProtected || (am > .5f && bm > .5f && xm <= .5f) || (am < .5f && bm < .5f && xm >= .5f)) &&
281
// This is an artifact if the interpolated median is outside the range of possible values based on its distance from a, b.
282
!(xm >= am-axSpan && xm <= am+axSpan && xm >= bm-bxSpan && xm <= bm+bxSpan)
283
);
284
}
285
286
/// Checks if a linear interpolation artifact will occur at a point where two specific color channels are equal - such points have extreme median values.
287
template <class ArtifactClassifier>
288
static bool hasLinearArtifactInner(const ArtifactClassifier &artifactClassifier, float am, float bm, const float *a, const float *b, float dA, float dB) {
289
// Find interpolation ratio t (0 < t < 1) where two color channels are equal (mix(dA, dB, t) == 0).
290
double t = (double) dA/(dA-dB);
291
if (t > ARTIFACT_T_EPSILON && t < 1-ARTIFACT_T_EPSILON) {
292
// Interpolate median at t and let the classifier decide if its value indicates an artifact.
293
float xm = interpolatedMedian(a, b, t);
294
return artifactClassifier.evaluate(t, xm, artifactClassifier.rangeTest(0, 1, t, am, bm, xm));
295
}
296
return false;
297
}
298
299
/// Checks if a bilinear interpolation artifact will occur at a point where two specific color channels are equal - such points have extreme median values.
300
template <class ArtifactClassifier>
301
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) {
302
// Find interpolation ratios t (0 < t[i] < 1) where two color channels are equal.
303
double t[2];
304
int solutions = solveQuadratic(t, dD-dBC+dA, dBC-dA-dA, dA);
305
for (int i = 0; i < solutions; ++i) {
306
// Solutions t[i] == 0 and t[i] == 1 are singularities and occur very often because two channels are usually equal at texels.
307
if (t[i] > ARTIFACT_T_EPSILON && t[i] < 1-ARTIFACT_T_EPSILON) {
308
// Interpolate median xm at t.
309
float xm = interpolatedMedian(a, l, q, t[i]);
310
// Determine if xm deviates too much from medians of a, d.
311
int rangeFlags = artifactClassifier.rangeTest(0, 1, t[i], am, dm, xm);
312
// Additionally, check xm against the interpolated medians at the local extremes tEx0, tEx1.
313
double tEnd[2];
314
float em[2];
315
// tEx0
316
if (tEx0 > 0 && tEx0 < 1) {
317
tEnd[0] = 0, tEnd[1] = 1;
318
em[0] = am, em[1] = dm;
319
tEnd[tEx0 > t[i]] = tEx0;
320
em[tEx0 > t[i]] = interpolatedMedian(a, l, q, tEx0);
321
rangeFlags |= artifactClassifier.rangeTest(tEnd[0], tEnd[1], t[i], em[0], em[1], xm);
322
}
323
// tEx1
324
if (tEx1 > 0 && tEx1 < 1) {
325
tEnd[0] = 0, tEnd[1] = 1;
326
em[0] = am, em[1] = dm;
327
tEnd[tEx1 > t[i]] = tEx1;
328
em[tEx1 > t[i]] = interpolatedMedian(a, l, q, tEx1);
329
rangeFlags |= artifactClassifier.rangeTest(tEnd[0], tEnd[1], t[i], em[0], em[1], xm);
330
}
331
if (artifactClassifier.evaluate(t[i], xm, rangeFlags))
332
return true;
333
}
334
}
335
return false;
336
}
337
338
/// Checks if a linear interpolation artifact will occur inbetween two horizontally or vertically adjacent texels a, b.
339
template <class ArtifactClassifier>
340
static bool hasLinearArtifact(const ArtifactClassifier &artifactClassifier, float am, const float *a, const float *b) {
341
float bm = median(b[0], b[1], b[2]);
342
return (
343
// Out of the pair, only report artifacts for the texel further from the edge to minimize side effects.
344
fabsf(am-.5f) >= fabsf(bm-.5f) && (
345
// Check points where each pair of color channels meets.
346
hasLinearArtifactInner(artifactClassifier, am, bm, a, b, a[1]-a[0], b[1]-b[0]) ||
347
hasLinearArtifactInner(artifactClassifier, am, bm, a, b, a[2]-a[1], b[2]-b[1]) ||
348
hasLinearArtifactInner(artifactClassifier, am, bm, a, b, a[0]-a[2], b[0]-b[2])
349
)
350
);
351
}
352
353
/// Checks if a bilinear interpolation artifact will occur inbetween two diagonally adjacent texels a, d (with b, c forming the other diagonal).
354
template <class ArtifactClassifier>
355
static bool hasDiagonalArtifact(const ArtifactClassifier &artifactClassifier, float am, const float *a, const float *b, const float *c, const float *d) {
356
float dm = median(d[0], d[1], d[2]);
357
// Out of the pair, only report artifacts for the texel further from the edge to minimize side effects.
358
if (fabsf(am-.5f) >= fabsf(dm-.5f)) {
359
float abc[3] = {
360
a[0]-b[0]-c[0],
361
a[1]-b[1]-c[1],
362
a[2]-b[2]-c[2]
363
};
364
// Compute the linear terms for bilinear interpolation.
365
float l[3] = {
366
-a[0]-abc[0],
367
-a[1]-abc[1],
368
-a[2]-abc[2]
369
};
370
// Compute the quadratic terms for bilinear interpolation.
371
float q[3] = {
372
d[0]+abc[0],
373
d[1]+abc[1],
374
d[2]+abc[2]
375
};
376
// 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).
377
double tEx[3] = {
378
-.5*l[0]/q[0],
379
-.5*l[1]/q[1],
380
-.5*l[2]/q[2]
381
};
382
// Check points where each pair of color channels meets.
383
return (
384
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]) ||
385
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]) ||
386
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])
387
);
388
}
389
return false;
390
}
391
392
template <int N>
393
void MSDFErrorCorrection::findErrors(const BitmapConstRef<float, N> &sdf) {
394
// Compute the expected deltas between values of horizontally, vertically, and diagonally adjacent texels.
395
double hSpan = minDeviationRatio*transformation.unprojectVector(Vector2(transformation.distanceMapping(DistanceMapping::Delta(1)), 0)).length();
396
double vSpan = minDeviationRatio*transformation.unprojectVector(Vector2(0, transformation.distanceMapping(DistanceMapping::Delta(1)))).length();
397
double dSpan = minDeviationRatio*transformation.unprojectVector(Vector2(transformation.distanceMapping(DistanceMapping::Delta(1)))).length();
398
// Inspect all texels.
399
for (int y = 0; y < sdf.height; ++y) {
400
for (int x = 0; x < sdf.width; ++x) {
401
const float *c = sdf(x, y);
402
float cm = median(c[0], c[1], c[2]);
403
bool protectedFlag = (*stencil(x, y)&PROTECTED) != 0;
404
const float *l = NULL, *b = NULL, *r = NULL, *t = NULL;
405
// Mark current texel c with the error flag if an artifact occurs when it's interpolated with any of its 8 neighbors.
406
*stencil(x, y) |= (byte) (ERROR*(
407
(x > 0 && ((l = sdf(x-1, y)), hasLinearArtifact(BaseArtifactClassifier(hSpan, protectedFlag), cm, c, l))) ||
408
(y > 0 && ((b = sdf(x, y-1)), hasLinearArtifact(BaseArtifactClassifier(vSpan, protectedFlag), cm, c, b))) ||
409
(x < sdf.width-1 && ((r = sdf(x+1, y)), hasLinearArtifact(BaseArtifactClassifier(hSpan, protectedFlag), cm, c, r))) ||
410
(y < sdf.height-1 && ((t = sdf(x, y+1)), hasLinearArtifact(BaseArtifactClassifier(vSpan, protectedFlag), cm, c, t))) ||
411
(x > 0 && y > 0 && hasDiagonalArtifact(BaseArtifactClassifier(dSpan, protectedFlag), cm, c, l, b, sdf(x-1, y-1))) ||
412
(x < sdf.width-1 && y > 0 && hasDiagonalArtifact(BaseArtifactClassifier(dSpan, protectedFlag), cm, c, r, b, sdf(x+1, y-1))) ||
413
(x > 0 && y < sdf.height-1 && hasDiagonalArtifact(BaseArtifactClassifier(dSpan, protectedFlag), cm, c, l, t, sdf(x-1, y+1))) ||
414
(x < sdf.width-1 && y < sdf.height-1 && hasDiagonalArtifact(BaseArtifactClassifier(dSpan, protectedFlag), cm, c, r, t, sdf(x+1, y+1)))
415
));
416
}
417
}
418
}
419
420
template <template <typename> class ContourCombiner, int N>
421
void MSDFErrorCorrection::findErrors(const BitmapConstRef<float, N> &sdf, const Shape &shape) {
422
// Compute the expected deltas between values of horizontally, vertically, and diagonally adjacent texels.
423
double hSpan = minDeviationRatio*transformation.unprojectVector(Vector2(transformation.distanceMapping(DistanceMapping::Delta(1)), 0)).length();
424
double vSpan = minDeviationRatio*transformation.unprojectVector(Vector2(0, transformation.distanceMapping(DistanceMapping::Delta(1)))).length();
425
double dSpan = minDeviationRatio*transformation.unprojectVector(Vector2(transformation.distanceMapping(DistanceMapping::Delta(1)))).length();
426
#ifdef MSDFGEN_USE_OPENMP
427
#pragma omp parallel
428
#endif
429
{
430
ShapeDistanceChecker<ContourCombiner, N> shapeDistanceChecker(sdf, shape, transformation, transformation.distanceMapping, minImproveRatio);
431
bool rightToLeft = false;
432
// Inspect all texels.
433
#ifdef MSDFGEN_USE_OPENMP
434
#pragma omp for
435
#endif
436
for (int y = 0; y < sdf.height; ++y) {
437
int row = shape.inverseYAxis ? sdf.height-y-1 : y;
438
for (int col = 0; col < sdf.width; ++col) {
439
int x = rightToLeft ? sdf.width-col-1 : col;
440
if ((*stencil(x, row)&ERROR))
441
continue;
442
const float *c = sdf(x, row);
443
shapeDistanceChecker.shapeCoord = transformation.unproject(Point2(x+.5, y+.5));
444
shapeDistanceChecker.sdfCoord = Point2(x+.5, row+.5);
445
shapeDistanceChecker.msd = c;
446
shapeDistanceChecker.protectedFlag = (*stencil(x, row)&PROTECTED) != 0;
447
float cm = median(c[0], c[1], c[2]);
448
const float *l = NULL, *b = NULL, *r = NULL, *t = NULL;
449
// Mark current texel c with the error flag if an artifact occurs when it's interpolated with any of its 8 neighbors.
450
*stencil(x, row) |= (byte) (ERROR*(
451
(x > 0 && ((l = sdf(x-1, row)), hasLinearArtifact(shapeDistanceChecker.classifier(Vector2(-1, 0), hSpan), cm, c, l))) ||
452
(row > 0 && ((b = sdf(x, row-1)), hasLinearArtifact(shapeDistanceChecker.classifier(Vector2(0, -1), vSpan), cm, c, b))) ||
453
(x < sdf.width-1 && ((r = sdf(x+1, row)), hasLinearArtifact(shapeDistanceChecker.classifier(Vector2(+1, 0), hSpan), cm, c, r))) ||
454
(row < sdf.height-1 && ((t = sdf(x, row+1)), hasLinearArtifact(shapeDistanceChecker.classifier(Vector2(0, +1), vSpan), cm, c, t))) ||
455
(x > 0 && row > 0 && hasDiagonalArtifact(shapeDistanceChecker.classifier(Vector2(-1, -1), dSpan), cm, c, l, b, sdf(x-1, row-1))) ||
456
(x < sdf.width-1 && row > 0 && hasDiagonalArtifact(shapeDistanceChecker.classifier(Vector2(+1, -1), dSpan), cm, c, r, b, sdf(x+1, row-1))) ||
457
(x > 0 && row < sdf.height-1 && hasDiagonalArtifact(shapeDistanceChecker.classifier(Vector2(-1, +1), dSpan), cm, c, l, t, sdf(x-1, row+1))) ||
458
(x < sdf.width-1 && row < sdf.height-1 && hasDiagonalArtifact(shapeDistanceChecker.classifier(Vector2(+1, +1), dSpan), cm, c, r, t, sdf(x+1, row+1)))
459
));
460
}
461
}
462
}
463
}
464
465
template <int N>
466
void MSDFErrorCorrection::apply(const BitmapRef<float, N> &sdf) const {
467
int texelCount = sdf.width*sdf.height;
468
const byte *mask = stencil.pixels;
469
float *texel = sdf.pixels;
470
for (int i = 0; i < texelCount; ++i) {
471
if (*mask&ERROR) {
472
// Set all color channels to the median.
473
float m = median(texel[0], texel[1], texel[2]);
474
texel[0] = m, texel[1] = m, texel[2] = m;
475
}
476
++mask;
477
texel += N;
478
}
479
}
480
481
BitmapConstRef<byte, 1> MSDFErrorCorrection::getStencil() const {
482
return stencil;
483
}
484
485
template void MSDFErrorCorrection::protectEdges(const BitmapConstRef<float, 3> &sdf);
486
template void MSDFErrorCorrection::protectEdges(const BitmapConstRef<float, 4> &sdf);
487
template void MSDFErrorCorrection::findErrors(const BitmapConstRef<float, 3> &sdf);
488
template void MSDFErrorCorrection::findErrors(const BitmapConstRef<float, 4> &sdf);
489
template void MSDFErrorCorrection::findErrors<SimpleContourCombiner>(const BitmapConstRef<float, 3> &sdf, const Shape &shape);
490
template void MSDFErrorCorrection::findErrors<SimpleContourCombiner>(const BitmapConstRef<float, 4> &sdf, const Shape &shape);
491
template void MSDFErrorCorrection::findErrors<OverlappingContourCombiner>(const BitmapConstRef<float, 3> &sdf, const Shape &shape);
492
template void MSDFErrorCorrection::findErrors<OverlappingContourCombiner>(const BitmapConstRef<float, 4> &sdf, const Shape &shape);
493
template void MSDFErrorCorrection::apply(const BitmapRef<float, 3> &sdf) const;
494
template void MSDFErrorCorrection::apply(const BitmapRef<float, 4> &sdf) const;
495
496
}
497
498