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
14namespace 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
22const double ErrorCorrectionConfig::defaultMinDeviationRatio = 1.11111111111111111;
23const double ErrorCorrectionConfig::defaultMinImproveRatio = 1.11111111111111111;
24
25/// The base artifact classifier recognizes artifacts based on the contents of the SDF alone.
26class BaseArtifactClassifier {
27public:
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 }
45private:
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.
51template <template <typename> class ContourCombiner, int N>
52class ShapeDistanceChecker {
53public:
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->invRange*parent->distanceFinder.distance(parent->shapeCoord+tVector*parent->texelSize)+.5);
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, double invRange, double minImproveRatio) : distanceFinder(shape), sdf(sdf), invRange(invRange), 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 }
96private:
97 ShapeDistanceFinder<ContourCombiner<PseudoDistanceSelector> > distanceFinder;
98 BitmapConstRef<float, N> sdf;
99 double invRange;
100 Vector2 texelSize;
101 double minImproveRatio;
102};
103
104MSDFErrorCorrection::MSDFErrorCorrection() { }
105
106MSDFErrorCorrection::MSDFErrorCorrection(const BitmapRef<byte, 1> &stencil, const Projection &projection, double range) : stencil(stencil), projection(projection) {
107 invRange = 1/range;
108 minDeviationRatio = ErrorCorrectionConfig::defaultMinDeviationRatio;
109 minImproveRatio = ErrorCorrectionConfig::defaultMinImproveRatio;
110 memset(stencil.pixels, 0, sizeof(byte)*stencil.width*stencil.height);
111}
112
113void MSDFErrorCorrection::setMinDeviationRatio(double minDeviationRatio) {
114 this->minDeviationRatio = minDeviationRatio;
115}
116
117void MSDFErrorCorrection::setMinImproveRatio(double minImproveRatio) {
118 this->minImproveRatio = minImproveRatio;
119}
120
121void MSDFErrorCorrection::protectCorners(const Shape &shape) {
122 for (std::vector<Contour>::const_iterator contour = shape.contours.begin(); contour != shape.contours.end(); ++contour)
123 if (!contour->edges.empty()) {
124 const EdgeSegment *prevEdge = contour->edges.back();
125 for (std::vector<EdgeHolder>::const_iterator edge = contour->edges.begin(); edge != contour->edges.end(); ++edge) {
126 int commonColor = prevEdge->color&(*edge)->color;
127 // If the color changes from prevEdge to edge, this is a corner.
128 if (!(commonColor&(commonColor-1))) {
129 // Find the four texels that envelop the corner and mark them as protected.
130 Point2 p = projection.project((*edge)->point(0));
131 if (shape.inverseYAxis)
132 p.y = stencil.height-p.y;
133 int l = (int) floor(p.x-.5);
134 int b = (int) floor(p.y-.5);
135 int r = l+1;
136 int t = b+1;
137 // Check that the positions are within bounds.
138 if (l < stencil.width && b < stencil.height && r >= 0 && t >= 0) {
139 if (l >= 0 && b >= 0)
140 *stencil(l, b) |= (byte) PROTECTED;
141 if (r < stencil.width && b >= 0)
142 *stencil(r, b) |= (byte) PROTECTED;
143 if (l >= 0 && t < stencil.height)
144 *stencil(l, t) |= (byte) PROTECTED;
145 if (r < stencil.width && t < stencil.height)
146 *stencil(r, t) |= (byte) PROTECTED;
147 }
148 }
149 prevEdge = *edge;
150 }
151 }
152}
153
154/// Determines if the channel contributes to an edge between the two texels a, b.
155static bool edgeBetweenTexelsChannel(const float *a, const float *b, int channel) {
156 // Find interpolation ratio t (0 < t < 1) where an edge is expected (mix(a[channel], b[channel], t) == 0.5).
157 double t = (a[channel]-.5)/(a[channel]-b[channel]);
158 if (t > 0 && t < 1) {
159 // Interpolate channel values at t.
160 float c[3] = {
161 mix(a[0], b[0], t),
162 mix(a[1], b[1], t),
163 mix(a[2], b[2], t)
164 };
165 // This is only an edge if the zero-distance channel is the median.
166 return median(c[0], c[1], c[2]) == c[channel];
167 }
168 return false;
169}
170
171/// Returns a bit mask of which channels contribute to an edge between the two texels a, b.
172static int edgeBetweenTexels(const float *a, const float *b) {
173 return (
174 RED*edgeBetweenTexelsChannel(a, b, 0)+
175 GREEN*edgeBetweenTexelsChannel(a, b, 1)+
176 BLUE*edgeBetweenTexelsChannel(a, b, 2)
177 );
178}
179
180/// Marks texel as protected if one of its non-median channels is present in the channel mask.
181static void protectExtremeChannels(byte *stencil, const float *msd, float m, int mask) {
182 if (
183 (mask&RED && msd[0] != m) ||
184 (mask&GREEN && msd[1] != m) ||
185 (mask&BLUE && msd[2] != m)
186 )
187 *stencil |= (byte) MSDFErrorCorrection::PROTECTED;
188}
189
190template <int N>
191void MSDFErrorCorrection::protectEdges(const BitmapConstRef<float, N> &sdf) {
192 float radius;
193 // Horizontal texel pairs
194 radius = float(PROTECTION_RADIUS_TOLERANCE*projection.unprojectVector(Vector2(invRange, 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*projection.unprojectVector(Vector2(0, invRange)).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*projection.unprojectVector(Vector2(invRange)).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
252void MSDFErrorCorrection::protectAll() {
253 byte *end = stencil.pixels+stencil.width*stencil.height;
254 for (byte *mask = stencil.pixels; mask < end; ++mask)
255 *mask |= (byte) PROTECTED;
256}
257
258/// Returns the median of the linear interpolation of texels a, b at t.
259static float interpolatedMedian(const float *a, const float *b, double t) {
260 return median(
261 mix(a[0], b[0], t),
262 mix(a[1], b[1], t),
263 mix(a[2], b[2], t)
264 );
265}
266/// Returns the median of the bilinear interpolation with the given constant, linear, and quadratic terms at t.
267static float interpolatedMedian(const float *a, const float *l, const float *q, double t) {
268 return float(median(
269 t*(t*q[0]+l[0])+a[0],
270 t*(t*q[1]+l[1])+a[1],
271 t*(t*q[2]+l[2])+a[2]
272 ));
273}
274
275/// Determines if the interpolated median xm is an artifact.
276static bool isArtifact(bool isProtected, double axSpan, double bxSpan, float am, float bm, float xm) {
277 return (
278 // For protected texels, only report an artifact if it would cause fill inversion (change between positive and negative distance).
279 (!isProtected || (am > .5f && bm > .5f && xm <= .5f) || (am < .5f && bm < .5f && xm >= .5f)) &&
280 // This is an artifact if the interpolated median is outside the range of possible values based on its distance from a, b.
281 !(xm >= am-axSpan && xm <= am+axSpan && xm >= bm-bxSpan && xm <= bm+bxSpan)
282 );
283}
284
285/// Checks if a linear interpolation artifact will occur at a point where two specific color channels are equal - such points have extreme median values.
286template <class ArtifactClassifier>
287static bool hasLinearArtifactInner(const ArtifactClassifier &artifactClassifier, float am, float bm, const float *a, const float *b, float dA, float dB) {
288 // Find interpolation ratio t (0 < t < 1) where two color channels are equal (mix(dA, dB, t) == 0).
289 double t = (double) dA/(dA-dB);
290 if (t > ARTIFACT_T_EPSILON && t < 1-ARTIFACT_T_EPSILON) {
291 // Interpolate median at t and let the classifier decide if its value indicates an artifact.
292 float xm = interpolatedMedian(a, b, t);
293 return artifactClassifier.evaluate(t, xm, artifactClassifier.rangeTest(0, 1, t, am, bm, xm));
294 }
295 return false;
296}
297
298/// Checks if a bilinear interpolation artifact will occur at a point where two specific color channels are equal - such points have extreme median values.
299template <class ArtifactClassifier>
300static 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) {
301 // Find interpolation ratios t (0 < t[i] < 1) where two color channels are equal.
302 double t[2];
303 int solutions = solveQuadratic(t, dD-dBC+dA, dBC-dA-dA, dA);
304 for (int i = 0; i < solutions; ++i) {
305 // Solutions t[i] == 0 and t[i] == 1 are singularities and occur very often because two channels are usually equal at texels.
306 if (t[i] > ARTIFACT_T_EPSILON && t[i] < 1-ARTIFACT_T_EPSILON) {
307 // Interpolate median xm at t.
308 float xm = interpolatedMedian(a, l, q, t[i]);
309 // Determine if xm deviates too much from medians of a, d.
310 int rangeFlags = artifactClassifier.rangeTest(0, 1, t[i], am, dm, xm);
311 // Additionally, check xm against the interpolated medians at the local extremes tEx0, tEx1.
312 double tEnd[2];
313 float em[2];
314 // tEx0
315 if (tEx0 > 0 && tEx0 < 1) {
316 tEnd[0] = 0, tEnd[1] = 1;
317 em[0] = am, em[1] = dm;
318 tEnd[tEx0 > t[i]] = tEx0;
319 em[tEx0 > t[i]] = interpolatedMedian(a, l, q, tEx0);
320 rangeFlags |= artifactClassifier.rangeTest(tEnd[0], tEnd[1], t[i], am, dm, xm);
321 }
322 // tEx1
323 if (tEx1 > 0 && tEx1 < 1) {
324 tEnd[0] = 0, tEnd[1] = 1;
325 em[0] = am, em[1] = dm;
326 tEnd[tEx1 > t[i]] = tEx1;
327 em[tEx1 > t[i]] = interpolatedMedian(a, l, q, tEx1);
328 rangeFlags |= artifactClassifier.rangeTest(tEnd[0], tEnd[1], t[i], am, dm, xm);
329 }
330 if (artifactClassifier.evaluate(t[i], xm, rangeFlags))
331 return true;
332 }
333 }
334 return false;
335}
336
337/// Checks if a linear interpolation artifact will occur inbetween two horizontally or vertically adjacent texels a, b.
338template <class ArtifactClassifier>
339static bool hasLinearArtifact(const ArtifactClassifier &artifactClassifier, float am, const float *a, const float *b) {
340 float bm = median(b[0], b[1], b[2]);
341 return (
342 // Out of the pair, only report artifacts for the texel further from the edge to minimize side effects.
343 fabsf(am-.5f) >= fabsf(bm-.5f) && (
344 // Check points where each pair of color channels meets.
345 hasLinearArtifactInner(artifactClassifier, am, bm, a, b, a[1]-a[0], b[1]-b[0]) ||
346 hasLinearArtifactInner(artifactClassifier, am, bm, a, b, a[2]-a[1], b[2]-b[1]) ||
347 hasLinearArtifactInner(artifactClassifier, am, bm, a, b, a[0]-a[2], b[0]-b[2])
348 )
349 );
350}
351
352/// Checks if a bilinear interpolation artifact will occur inbetween two diagonally adjacent texels a, d (with b, c forming the other diagonal).
353template <class ArtifactClassifier>
354static bool hasDiagonalArtifact(const ArtifactClassifier &artifactClassifier, float am, const float *a, const float *b, const float *c, const float *d) {
355 float dm = median(d[0], d[1], d[2]);
356 // Out of the pair, only report artifacts for the texel further from the edge to minimize side effects.
357 if (fabsf(am-.5f) >= fabsf(dm-.5f)) {
358 float abc[3] = {
359 a[0]-b[0]-c[0],
360 a[1]-b[1]-c[1],
361 a[2]-b[2]-c[2]
362 };
363 // Compute the linear terms for bilinear interpolation.
364 float l[3] = {
365 -a[0]-abc[0],
366 -a[1]-abc[1],
367 -a[2]-abc[2]
368 };
369 // Compute the quadratic terms for bilinear interpolation.
370 float q[3] = {
371 d[0]+abc[0],
372 d[1]+abc[1],
373 d[2]+abc[2]
374 };
375 // 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).
376 double tEx[3] = {
377 -.5*l[0]/q[0],
378 -.5*l[1]/q[1],
379 -.5*l[2]/q[2]
380 };
381 // Check points where each pair of color channels meets.
382 return (
383 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]) ||
384 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]) ||
385 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])
386 );
387 }
388 return false;
389}
390
391template <int N>
392void MSDFErrorCorrection::findErrors(const BitmapConstRef<float, N> &sdf) {
393 // Compute the expected deltas between values of horizontally, vertically, and diagonally adjacent texels.
394 double hSpan = minDeviationRatio*projection.unprojectVector(Vector2(invRange, 0)).length();
395 double vSpan = minDeviationRatio*projection.unprojectVector(Vector2(0, invRange)).length();
396 double dSpan = minDeviationRatio*projection.unprojectVector(Vector2(invRange)).length();
397 // Inspect all texels.
398 for (int y = 0; y < sdf.height; ++y) {
399 for (int x = 0; x < sdf.width; ++x) {
400 const float *c = sdf(x, y);
401 float cm = median(c[0], c[1], c[2]);
402 bool protectedFlag = (*stencil(x, y)&PROTECTED) != 0;
403 const float *l = NULL, *b = NULL, *r = NULL, *t = NULL;
404 // Mark current texel c with the error flag if an artifact occurs when it's interpolated with any of its 8 neighbors.
405 *stencil(x, y) |= (byte) (ERROR*(
406 (x > 0 && ((l = sdf(x-1, y)), hasLinearArtifact(BaseArtifactClassifier(hSpan, protectedFlag), cm, c, l))) ||
407 (y > 0 && ((b = sdf(x, y-1)), hasLinearArtifact(BaseArtifactClassifier(vSpan, protectedFlag), cm, c, b))) ||
408 (x < sdf.width-1 && ((r = sdf(x+1, y)), hasLinearArtifact(BaseArtifactClassifier(hSpan, protectedFlag), cm, c, r))) ||
409 (y < sdf.height-1 && ((t = sdf(x, y+1)), hasLinearArtifact(BaseArtifactClassifier(vSpan, protectedFlag), cm, c, t))) ||
410 (x > 0 && y > 0 && hasDiagonalArtifact(BaseArtifactClassifier(dSpan, protectedFlag), cm, c, l, b, sdf(x-1, y-1))) ||
411 (x < sdf.width-1 && y > 0 && hasDiagonalArtifact(BaseArtifactClassifier(dSpan, protectedFlag), cm, c, r, b, sdf(x+1, y-1))) ||
412 (x > 0 && y < sdf.height-1 && hasDiagonalArtifact(BaseArtifactClassifier(dSpan, protectedFlag), cm, c, l, t, sdf(x-1, y+1))) ||
413 (x < sdf.width-1 && y < sdf.height-1 && hasDiagonalArtifact(BaseArtifactClassifier(dSpan, protectedFlag), cm, c, r, t, sdf(x+1, y+1)))
414 ));
415 }
416 }
417}
418
419template <template <typename> class ContourCombiner, int N>
420void MSDFErrorCorrection::findErrors(const BitmapConstRef<float, N> &sdf, const Shape &shape) {
421 // Compute the expected deltas between values of horizontally, vertically, and diagonally adjacent texels.
422 double hSpan = minDeviationRatio*projection.unprojectVector(Vector2(invRange, 0)).length();
423 double vSpan = minDeviationRatio*projection.unprojectVector(Vector2(0, invRange)).length();
424 double dSpan = minDeviationRatio*projection.unprojectVector(Vector2(invRange)).length();
425#ifdef MSDFGEN_USE_OPENMP
426 #pragma omp parallel
427#endif
428 {
429 ShapeDistanceChecker<ContourCombiner, N> shapeDistanceChecker(sdf, shape, projection, invRange, minImproveRatio);
430 bool rightToLeft = false;
431 // Inspect all texels.
432#ifdef MSDFGEN_USE_OPENMP
433 #pragma omp for
434#endif
435 for (int y = 0; y < sdf.height; ++y) {
436 int row = shape.inverseYAxis ? sdf.height-y-1 : y;
437 for (int col = 0; col < sdf.width; ++col) {
438 int x = rightToLeft ? sdf.width-col-1 : col;
439 if ((*stencil(x, row)&ERROR))
440 continue;
441 const float *c = sdf(x, row);
442 shapeDistanceChecker.shapeCoord = projection.unproject(Point2(x+.5, y+.5));
443 shapeDistanceChecker.sdfCoord = Point2(x+.5, row+.5);
444 shapeDistanceChecker.msd = c;
445 shapeDistanceChecker.protectedFlag = (*stencil(x, row)&PROTECTED) != 0;
446 float cm = median(c[0], c[1], c[2]);
447 const float *l = NULL, *b = NULL, *r = NULL, *t = NULL;
448 // Mark current texel c with the error flag if an artifact occurs when it's interpolated with any of its 8 neighbors.
449 *stencil(x, row) |= (byte) (ERROR*(
450 (x > 0 && ((l = sdf(x-1, row)), hasLinearArtifact(shapeDistanceChecker.classifier(Vector2(-1, 0), hSpan), cm, c, l))) ||
451 (row > 0 && ((b = sdf(x, row-1)), hasLinearArtifact(shapeDistanceChecker.classifier(Vector2(0, -1), vSpan), cm, c, b))) ||
452 (x < sdf.width-1 && ((r = sdf(x+1, row)), hasLinearArtifact(shapeDistanceChecker.classifier(Vector2(+1, 0), hSpan), cm, c, r))) ||
453 (row < sdf.height-1 && ((t = sdf(x, row+1)), hasLinearArtifact(shapeDistanceChecker.classifier(Vector2(0, +1), vSpan), cm, c, t))) ||
454 (x > 0 && row > 0 && hasDiagonalArtifact(shapeDistanceChecker.classifier(Vector2(-1, -1), dSpan), cm, c, l, b, sdf(x-1, row-1))) ||
455 (x < sdf.width-1 && row > 0 && hasDiagonalArtifact(shapeDistanceChecker.classifier(Vector2(+1, -1), dSpan), cm, c, r, b, sdf(x+1, row-1))) ||
456 (x > 0 && row < sdf.height-1 && hasDiagonalArtifact(shapeDistanceChecker.classifier(Vector2(-1, +1), dSpan), cm, c, l, t, sdf(x-1, row+1))) ||
457 (x < sdf.width-1 && row < sdf.height-1 && hasDiagonalArtifact(shapeDistanceChecker.classifier(Vector2(+1, +1), dSpan), cm, c, r, t, sdf(x+1, row+1)))
458 ));
459 }
460 }
461 }
462}
463
464template <int N>
465void MSDFErrorCorrection::apply(const BitmapRef<float, N> &sdf) const {
466 int texelCount = sdf.width*sdf.height;
467 const byte *mask = stencil.pixels;
468 float *texel = sdf.pixels;
469 for (int i = 0; i < texelCount; ++i) {
470 if (*mask&ERROR) {
471 // Set all color channels to the median.
472 float m = median(texel[0], texel[1], texel[2]);
473 texel[0] = m, texel[1] = m, texel[2] = m;
474 }
475 ++mask;
476 texel += N;
477 }
478}
479
480BitmapConstRef<byte, 1> MSDFErrorCorrection::getStencil() const {
481 return stencil;
482}
483
484template void MSDFErrorCorrection::protectEdges(const BitmapConstRef<float, 3> &sdf);
485template void MSDFErrorCorrection::protectEdges(const BitmapConstRef<float, 4> &sdf);
486template void MSDFErrorCorrection::findErrors(const BitmapConstRef<float, 3> &sdf);
487template void MSDFErrorCorrection::findErrors(const BitmapConstRef<float, 4> &sdf);
488template void MSDFErrorCorrection::findErrors<SimpleContourCombiner>(const BitmapConstRef<float, 3> &sdf, const Shape &shape);
489template void MSDFErrorCorrection::findErrors<SimpleContourCombiner>(const BitmapConstRef<float, 4> &sdf, const Shape &shape);
490template void MSDFErrorCorrection::findErrors<OverlappingContourCombiner>(const BitmapConstRef<float, 3> &sdf, const Shape &shape);
491template void MSDFErrorCorrection::findErrors<OverlappingContourCombiner>(const BitmapConstRef<float, 4> &sdf, const Shape &shape);
492template void MSDFErrorCorrection::apply(const BitmapRef<float, 3> &sdf) const;
493template void MSDFErrorCorrection::apply(const BitmapRef<float, 4> &sdf) const;
494
495}
496