msdf-artifact-patcher.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405
  1. #include "msdf-artifact-patcher.h"
  2. #include <cstring>
  3. #include <vector>
  4. #include <utility>
  5. #include "arithmetics.hpp"
  6. #include "equation-solver.h"
  7. #include "bitmap-interpolation.hpp"
  8. #include "edge-selectors.h"
  9. #include "contour-combiners.h"
  10. #include "ShapeDistanceFinder.h"
  11. namespace msdfgen {
  12. #define PROTECTION_RADIUS_TOLERANCE 1.001
  13. #ifdef MSDFGEN_USE_OPENMP
  14. // std::vector<bool> cannot be modified concurrently
  15. #define STENCIL_TYPE std::vector<char>
  16. #else
  17. #define STENCIL_TYPE std::vector<bool>
  18. #endif
  19. #define STENCIL_INDEX(x, y) (sdf.width*(y)+(x))
  20. template <class ArtifactClassifier, template <typename> class ContourCombiner, int N>
  21. struct ArtifactFinderData {
  22. ShapeDistanceFinder<ContourCombiner<PseudoDistanceSelector> > distanceFinder;
  23. const ArtifactClassifier artifactClassifier;
  24. const BitmapConstRef<float, N> sdf;
  25. const double invRange;
  26. const Vector2 pDelta;
  27. Point2 p, sdfCoord;
  28. const float *msd;
  29. float psd;
  30. bool isProtected;
  31. inline ArtifactFinderData(const ArtifactClassifier &artifactClassifier, const BitmapConstRef<float, N> &sdf, const Shape &shape, double range, const Vector2 &pDelta) :
  32. distanceFinder(shape), artifactClassifier(artifactClassifier), sdf(sdf), invRange(1/range), pDelta(pDelta) { }
  33. };
  34. class IndiscriminateArtifactClassifier {
  35. double minImproveRatio;
  36. public:
  37. inline static bool observesProtected() {
  38. return false;
  39. }
  40. inline static bool isEquivalent(float am, float bm) {
  41. return am == bm;
  42. }
  43. inline explicit IndiscriminateArtifactClassifier(double minImproveRatio) : minImproveRatio(minImproveRatio) { }
  44. inline bool isCandidate(float am, float bm, float xm, bool isProtected) const {
  45. return median(am, bm, xm) != xm;
  46. }
  47. inline bool isArtifact(float refSD, float newSD, float oldSD) const {
  48. return minImproveRatio*fabsf(newSD-refSD) < double(fabsf(oldSD-refSD));
  49. }
  50. };
  51. class EdgePriorityArtifactClassifier {
  52. double minImproveRatio;
  53. public:
  54. inline static bool observesProtected() {
  55. return true;
  56. }
  57. inline static bool isEquivalent(float am, float bm) {
  58. return am == bm;
  59. }
  60. inline explicit EdgePriorityArtifactClassifier(double minImproveRatio) : minImproveRatio(minImproveRatio) { }
  61. inline bool isCandidate(float am, float bm, float xm, bool isProtected) const {
  62. return (am > .5f && bm > .5f && xm < .5f) || (am < .5f && bm < .5f && xm > .5f) || (!isProtected && median(am, bm, xm) != xm);
  63. }
  64. inline bool isArtifact(float refSD, float newSD, float oldSD) const {
  65. float oldDelta = fabsf(oldSD-refSD);
  66. float newDelta = fabsf(newSD-refSD);
  67. return newDelta < oldDelta && (minImproveRatio*newDelta < double(oldDelta) || (refSD > .5f && newSD > .5f && oldSD < .5f) || (refSD < .5f && newSD < .5f && oldSD > .5f));
  68. }
  69. };
  70. class EdgeOnlyArtifactClassifier {
  71. public:
  72. inline static bool observesProtected() {
  73. return false;
  74. }
  75. inline static bool isEquivalent(float am, float bm) {
  76. return (am <= .5f && bm <= .5f) || (am >= .5f && bm >= .5f);
  77. }
  78. inline bool isCandidate(float am, float bm, float xm, bool isProtected) const {
  79. return (am > .5f && bm > .5f && xm < .5f) || (am < .5f && bm < .5f && xm > .5f);
  80. }
  81. inline bool isArtifact(float refSD, float newSD, float oldSD) const {
  82. return fabsf(newSD-refSD) <= fabsf(oldSD-refSD) && ((refSD > .5f && newSD > .5f && oldSD < .5f) || (refSD < .5f && newSD < .5f && oldSD > .5f));
  83. }
  84. };
  85. static float interpolatedMedian(const float *a, const float *b, double t) {
  86. return median(
  87. mix(a[0], b[0], t),
  88. mix(a[1], b[1], t),
  89. mix(a[2], b[2], t)
  90. );
  91. }
  92. static float interpolatedMedian(const float *a, const float *lin, const float *quad, double t) {
  93. return float(median(
  94. t*(t*quad[0]+lin[0])+a[0],
  95. t*(t*quad[1]+lin[1])+a[1],
  96. t*(t*quad[2]+lin[2])+a[2]
  97. ));
  98. }
  99. static bool edgeBetweenTexelsChannel(const float *a, const float *b, int channel) {
  100. float t = (a[channel]-.5f)/(a[channel]-b[channel]);
  101. if (t > 0.f && t < 1.f) {
  102. float c[3] = {
  103. mix(a[0], b[0], t),
  104. mix(a[1], b[1], t),
  105. mix(a[2], b[2], t)
  106. };
  107. return median(c[0], c[1], c[2]) == c[channel];
  108. }
  109. return false;
  110. }
  111. static bool edgeBetweenTexels(const float *a, const float *b) {
  112. return (
  113. edgeBetweenTexelsChannel(a, b, 0) ||
  114. edgeBetweenTexelsChannel(a, b, 1) ||
  115. edgeBetweenTexelsChannel(a, b, 2)
  116. );
  117. }
  118. template <int N>
  119. static void flagProtected(STENCIL_TYPE &stencil, const BitmapConstRef<float, N> &sdf, const Shape &shape, const Projection &projection, double range) {
  120. // Protect texels that are interpolated at corners
  121. for (std::vector<Contour>::const_iterator contour = shape.contours.begin(); contour != shape.contours.end(); ++contour)
  122. if (!contour->edges.empty()) {
  123. const EdgeSegment *prevEdge = contour->edges.back();
  124. for (std::vector<EdgeHolder>::const_iterator edge = contour->edges.begin(); edge != contour->edges.end(); ++edge) {
  125. int commonColor = prevEdge->color&(*edge)->color;
  126. if (!(commonColor&(commonColor-1))) { // Color switch between prevEdge and edge => a corner
  127. Point2 p = projection.project((*edge)->point(0));
  128. if (shape.inverseYAxis)
  129. p.y = sdf.height-p.y;
  130. int l = (int) floor(p.x-.5);
  131. int b = (int) floor(p.y-.5);
  132. int r = l+1;
  133. int t = b+1;
  134. if (l < sdf.width && b < sdf.height && r >= 0 && t >= 0) {
  135. if (l >= 0 && b >= 0)
  136. stencil[STENCIL_INDEX(l, b)] = true;
  137. if (r < sdf.width && b >= 0)
  138. stencil[STENCIL_INDEX(r, b)] = true;
  139. if (l >= 0 && t < sdf.height)
  140. stencil[STENCIL_INDEX(l, t)] = true;
  141. if (r < sdf.width && t < sdf.height)
  142. stencil[STENCIL_INDEX(r, t)] = true;
  143. }
  144. }
  145. prevEdge = *edge;
  146. }
  147. }
  148. // Protect texels that contribute to edges
  149. float radius;
  150. // Horizontal:
  151. radius = float(PROTECTION_RADIUS_TOLERANCE*projection.unprojectVector(Vector2(1/range, 0)).length());
  152. for (int y = 0; y < sdf.height; ++y) {
  153. const float *left = sdf(0, y);
  154. const float *right = sdf(1, y);
  155. for (int x = 0; x < sdf.width-1; ++x) {
  156. float lm = median(left[0], left[1], left[2]);
  157. float rm = median(right[0], right[1], right[2]);
  158. if (fabsf(lm-.5f)+fabsf(rm-.5f) < radius && edgeBetweenTexels(left, right)) {
  159. stencil[STENCIL_INDEX(x, y)] = true;
  160. stencil[STENCIL_INDEX(x+1, y)] = true;
  161. }
  162. left += N, right += N;
  163. }
  164. }
  165. // Vertical:
  166. radius = float(PROTECTION_RADIUS_TOLERANCE*projection.unprojectVector(Vector2(0, 1/range)).length());
  167. for (int y = 0; y < sdf.height-1; ++y) {
  168. const float *bottom = sdf(0, y);
  169. const float *top = sdf(0, y+1);
  170. for (int x = 0; x < sdf.width; ++x) {
  171. float bm = median(bottom[0], bottom[1], bottom[2]);
  172. float tm = median(top[0], top[1], top[2]);
  173. if (fabsf(bm-.5f)+fabsf(tm-.5f) < radius && edgeBetweenTexels(bottom, top)) {
  174. stencil[STENCIL_INDEX(x, y)] = true;
  175. stencil[STENCIL_INDEX(x, y+1)] = true;
  176. }
  177. bottom += N, top += N;
  178. }
  179. }
  180. // Diagonal:
  181. radius = float(PROTECTION_RADIUS_TOLERANCE*projection.unprojectVector(Vector2(1/range)).length());
  182. for (int y = 0; y < sdf.height-1; ++y) {
  183. const float *lb = sdf(0, y);
  184. const float *rb = sdf(1, y);
  185. const float *lt = sdf(0, y+1);
  186. const float *rt = sdf(1, y+1);
  187. for (int x = 0; x < sdf.width-1; ++x) {
  188. float mlb = median(lb[0], lb[1], lb[2]);
  189. float mrb = median(rb[0], rb[1], rb[2]);
  190. float mlt = median(lt[0], lt[1], lt[2]);
  191. float mrt = median(rt[0], rt[1], rt[2]);
  192. if (fabsf(mlb-.5f)+fabsf(mrt-.5f) < radius && edgeBetweenTexels(lb, rt)) {
  193. stencil[STENCIL_INDEX(x, y)] = true;
  194. stencil[STENCIL_INDEX(x+1, y+1)] = true;
  195. }
  196. if (fabsf(mrb-.5f)+fabsf(mlt-.5f) < radius && edgeBetweenTexels(rb, lt)) {
  197. stencil[STENCIL_INDEX(x+1, y)] = true;
  198. stencil[STENCIL_INDEX(x, y+1)] = true;
  199. }
  200. lb += N, rb += N, lt += N, rt += N;
  201. }
  202. }
  203. }
  204. template <class ArtifactClassifier, template <typename> class ContourCombiner, int N>
  205. static bool isArtifact(ArtifactFinderData<ArtifactClassifier, ContourCombiner, N> &data, const Vector2 &v) {
  206. Point2 sdfCoord = data.sdfCoord+v;
  207. float oldMSD[N], newMSD[3];
  208. interpolate(oldMSD, data.sdf, data.sdfCoord+v);
  209. double aWeight = (1-fabs(v.x))*(1-fabs(v.y));
  210. newMSD[0] = float(oldMSD[0]+aWeight*(data.psd-data.msd[0]));
  211. newMSD[1] = float(oldMSD[1]+aWeight*(data.psd-data.msd[1]));
  212. newMSD[2] = float(oldMSD[2]+aWeight*(data.psd-data.msd[2]));
  213. float oldPSD = median(oldMSD[0], oldMSD[1], oldMSD[2]);
  214. float newPSD = median(newMSD[0], newMSD[1], newMSD[2]);
  215. if (ArtifactClassifier::isEquivalent(oldPSD, newPSD))
  216. return false;
  217. float refPSD = float(data.invRange*data.distanceFinder.distance(data.p+v*data.pDelta)+.5);
  218. return data.artifactClassifier.isArtifact(refPSD, newPSD, oldPSD);
  219. }
  220. template <class ArtifactClassifier, template <typename> class ContourCombiner, int N>
  221. static bool hasLinearArtifact(ArtifactFinderData<ArtifactClassifier, ContourCombiner, N> &data, const Vector2 &v, const float *b, float bm, float dA, float dB) {
  222. double t = (double) dA/(dA-dB);
  223. if (t > 0 && t < 1) {
  224. float xm = interpolatedMedian(data.msd, b, t);
  225. return data.artifactClassifier.isCandidate(data.psd, bm, xm, data.isProtected) && isArtifact(data, t*v);
  226. }
  227. return false;
  228. }
  229. template <class ArtifactClassifier, template <typename> class ContourCombiner, int N>
  230. static bool hasDiagonalArtifact(ArtifactFinderData<ArtifactClassifier, ContourCombiner, N> &data, const Vector2 &v, const float *lin, const float *quad, float dm, float dA, float dBC, float dD, double tEx0, double tEx1) {
  231. const float *a = data.msd;
  232. double t[2];
  233. int solutions = solveQuadratic(t, dD-dBC+dA, dBC-dA-dA, dA);
  234. for (int i = 0; i < solutions; ++i) {
  235. if (t[i] > 0 && t[i] < 1) {
  236. float xm = interpolatedMedian(a, lin, quad, t[i]);
  237. bool candidate = data.artifactClassifier.isCandidate(data.psd, dm, xm, data.isProtected);
  238. float m[2];
  239. if (!candidate && tEx0 > 0 && tEx0 < 1) {
  240. m[0] = data.psd, m[1] = dm;
  241. m[tEx0 > t[i]] = interpolatedMedian(a, lin, quad, tEx0);
  242. candidate = data.artifactClassifier.isCandidate(m[0], m[1], xm, data.isProtected);
  243. }
  244. if (!candidate && tEx1 > 0 && tEx1 < 1) {
  245. m[0] = data.psd, m[1] = dm;
  246. m[tEx1 > t[i]] = interpolatedMedian(a, lin, quad, tEx1);
  247. candidate = data.artifactClassifier.isCandidate(m[0], m[1], xm, data.isProtected);
  248. }
  249. if (candidate && isArtifact(data, t[i]*v))
  250. return true;
  251. }
  252. }
  253. return false;
  254. }
  255. template <class ArtifactClassifier, template <typename> class ContourCombiner, int N>
  256. static bool hasLinearArtifact(ArtifactFinderData<ArtifactClassifier, ContourCombiner, N> &data, const Vector2 &v, const float *b) {
  257. const float *a = data.msd;
  258. float bm = median(b[0], b[1], b[2]);
  259. return (fabsf(data.psd-.5f) > fabsf(bm-.5f) && (
  260. hasLinearArtifact(data, v, b, bm, a[1]-a[0], b[1]-b[0]) ||
  261. hasLinearArtifact(data, v, b, bm, a[2]-a[1], b[2]-b[1]) ||
  262. hasLinearArtifact(data, v, b, bm, a[0]-a[2], b[0]-b[2])
  263. ));
  264. }
  265. template <class ArtifactClassifier, template <typename> class ContourCombiner, int N>
  266. static bool hasDiagonalArtifact(ArtifactFinderData<ArtifactClassifier, ContourCombiner, N> &data, const Vector2 &v, const float *b, const float *c, const float *d) {
  267. const float *a = data.msd;
  268. float dm = median(d[0], d[1], d[2]);
  269. if (fabsf(data.psd-.5f) > fabsf(dm-.5f)) {
  270. float abc[3] = {
  271. a[0]-b[0]-c[0],
  272. a[1]-b[1]-c[1],
  273. a[2]-b[2]-c[2]
  274. };
  275. float lin[3] = {
  276. -a[0]-abc[0],
  277. -a[1]-abc[1],
  278. -a[2]-abc[2]
  279. };
  280. float quad[3] = {
  281. d[0]+abc[0],
  282. d[1]+abc[1],
  283. d[2]+abc[2]
  284. };
  285. double tEx[3] = {
  286. -.5*lin[0]/quad[0],
  287. -.5*lin[1]/quad[1],
  288. -.5*lin[2]/quad[2]
  289. };
  290. return (
  291. hasDiagonalArtifact(data, v, lin, quad, dm, a[1]-a[0], b[1]-b[0]+c[1]-c[0], d[1]-d[0], tEx[0], tEx[1]) ||
  292. hasDiagonalArtifact(data, v, lin, quad, dm, a[2]-a[1], b[2]-b[1]+c[2]-c[1], d[2]-d[1], tEx[1], tEx[2]) ||
  293. hasDiagonalArtifact(data, v, lin, quad, dm, a[0]-a[2], b[0]-b[2]+c[0]-c[2], d[0]-d[2], tEx[2], tEx[0])
  294. );
  295. }
  296. return false;
  297. }
  298. template <template <typename> class ContourCombiner, int N, class ArtifactClassifier>
  299. static void msdfFindArtifacts(STENCIL_TYPE &stencil, const ArtifactClassifier &artifactClassifier, const BitmapConstRef<float, N> &sdf, const Shape &shape, const Projection &projection, double range) {
  300. if (ArtifactClassifier::observesProtected())
  301. flagProtected(stencil, sdf, shape, projection, range);
  302. Vector2 pDelta = projection.unprojectVector(Vector2(1));
  303. if (shape.inverseYAxis)
  304. pDelta.y = -pDelta.y;
  305. #ifdef MSDFGEN_USE_OPENMP
  306. #pragma omp parallel
  307. #endif
  308. {
  309. ArtifactFinderData<ArtifactClassifier, ContourCombiner, N> data(artifactClassifier,sdf, shape, range, pDelta);
  310. bool rightToLeft = false;
  311. #ifdef MSDFGEN_USE_OPENMP
  312. #pragma omp for
  313. #endif
  314. for (int y = 0; y < sdf.height; ++y) {
  315. int row = shape.inverseYAxis ? sdf.height-y-1 : y;
  316. for (int col = 0; col < sdf.width; ++col) {
  317. int x = rightToLeft ? sdf.width-col-1 : col;
  318. data.p = projection.unproject(Point2(x+.5, y+.5));
  319. data.sdfCoord = Point2(x+.5, row+.5);
  320. data.msd = sdf(x, row);
  321. data.psd = median(data.msd[0], data.msd[1], data.msd[2]);
  322. data.isProtected = stencil[STENCIL_INDEX(x, row)] != 0;
  323. const float *l = NULL, *b = NULL, *r = NULL, *t = NULL;
  324. stencil[STENCIL_INDEX(x, row)] = (
  325. (x > 0 && ((l = sdf(x-1, row)), hasLinearArtifact(data, Vector2(-1, 0), l))) ||
  326. (row > 0 && ((b = sdf(x, row-1)), hasLinearArtifact(data, Vector2(0, -1), b))) ||
  327. (x < sdf.width-1 && ((r = sdf(x+1, row)), hasLinearArtifact(data, Vector2(+1, 0), r))) ||
  328. (row < sdf.height-1 && ((t = sdf(x, row+1)), hasLinearArtifact(data, Vector2(0, +1), t))) ||
  329. (x > 0 && row > 0 && hasDiagonalArtifact(data, Vector2(-1, -1), l, b, sdf(x-1, row-1))) ||
  330. (x < sdf.width-1 && row > 0 && hasDiagonalArtifact(data, Vector2(+1, -1), r, b, sdf(x+1, row-1))) ||
  331. (x > 0 && row < sdf.height-1 && hasDiagonalArtifact(data, Vector2(-1, +1), l, t, sdf(x-1, row+1))) ||
  332. (x < sdf.width-1 && row < sdf.height-1 && hasDiagonalArtifact(data, Vector2(+1, +1), r, t, sdf(x+1, row+1)))
  333. );
  334. }
  335. rightToLeft = !rightToLeft;
  336. }
  337. }
  338. }
  339. template <template <typename> class ContourCombiner, int N>
  340. static void msdfPatchArtifactsInner(const BitmapRef<float, N> &sdf, const Shape &shape, const Projection &projection, double range, const GeneratorConfig &generatorConfig, const ArtifactPatcherConfig &config) {
  341. if (config.mode == ArtifactPatcherConfig::DISABLED)
  342. return;
  343. STENCIL_TYPE stencil(sdf.width*sdf.height);
  344. switch (config.mode) {
  345. case ArtifactPatcherConfig::DISABLED:
  346. break;
  347. case ArtifactPatcherConfig::INDISCRIMINATE:
  348. msdfFindArtifacts<ContourCombiner, N>(stencil, IndiscriminateArtifactClassifier(config.minImproveRatio), sdf, shape, projection, range);
  349. break;
  350. case ArtifactPatcherConfig::EDGE_PRIORITY:
  351. msdfFindArtifacts<ContourCombiner, N>(stencil, EdgePriorityArtifactClassifier(config.minImproveRatio), sdf, shape, projection, range);
  352. break;
  353. case ArtifactPatcherConfig::EDGE_ONLY:
  354. msdfFindArtifacts<ContourCombiner, N>(stencil, EdgeOnlyArtifactClassifier(), sdf, shape, projection, range);
  355. break;
  356. }
  357. float *texel = sdf.pixels;
  358. for (int y = 0; y < sdf.height; ++y) {
  359. for (int x = 0; x < sdf.width; ++x) {
  360. if (stencil[STENCIL_INDEX(x, y)]) {
  361. float m = median(texel[0], texel[1], texel[2]);
  362. texel[0] = m, texel[1] = m, texel[2] = m;
  363. }
  364. texel += N;
  365. }
  366. }
  367. }
  368. void msdfPatchArtifacts(const BitmapRef<float, 3> &sdf, const Shape &shape, const Projection &projection, double range, const GeneratorConfig &generatorConfig, const ArtifactPatcherConfig &config) {
  369. if (generatorConfig.overlapSupport)
  370. msdfPatchArtifactsInner<OverlappingContourCombiner>(sdf, shape, projection, range, generatorConfig, config);
  371. else
  372. msdfPatchArtifactsInner<SimpleContourCombiner>(sdf, shape, projection, range, generatorConfig, config);
  373. }
  374. void msdfPatchArtifacts(const BitmapRef<float, 4> &sdf, const Shape &shape, const Projection &projection, double range, const GeneratorConfig &generatorConfig, const ArtifactPatcherConfig &config) {
  375. if (generatorConfig.overlapSupport)
  376. msdfPatchArtifactsInner<OverlappingContourCombiner>(sdf, shape, projection, range, generatorConfig, config);
  377. else
  378. msdfPatchArtifactsInner<SimpleContourCombiner>(sdf, shape, projection, range, generatorConfig, config);
  379. }
  380. }