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