msdf-edge-artifact-patcher.cpp 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. #include "msdf-edge-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. static bool isHotspot(float am, float bm, float xm) {
  13. return (am > .5f && bm > .5f && xm < .5f) || (am < .5f && bm < .5f && xm > .5f);
  14. // A much more aggressive version for the entire distance field (not just edges): return median(am, bm, xm) != xm;
  15. }
  16. static int findLinearChannelHotspots(double t[1], const float *a, const float *b, float dA, float dB) {
  17. int found = 0;
  18. double x = (double) dA/(dA-dB);
  19. if (x > 0 && x < 1) {
  20. float am = median(a[0], a[1], a[2]);
  21. float bm = median(b[0], b[1], b[2]);
  22. float xm = median(
  23. mix(a[0], b[0], x),
  24. mix(a[1], b[1], x),
  25. mix(a[2], b[2], x)
  26. );
  27. if (isHotspot(am, bm, xm))
  28. t[found++] = x;
  29. }
  30. return found;
  31. }
  32. static int findDiagonalChannelHotspots(double t[2], const float *a, const float *b, const float *c, const float *d, float dA, float dB, float dC, float dD) {
  33. int found = 0;
  34. double x[2];
  35. int solutions = solveQuadratic(x, (dD-dC)-(dB-dA), dC+dB-2*dA, dA);
  36. for (int i = 0; i < solutions; ++i)
  37. if (x[i] > 0 && x[i] < 1) {
  38. float am = median(a[0], a[1], a[2]);
  39. float bm = median(b[0], b[1], b[2]);
  40. float xm = median(
  41. mix(mix(a[0], b[0], x[i]), mix(c[0], d[0], x[i]), x[i]),
  42. mix(mix(a[1], b[1], x[i]), mix(c[1], d[1], x[i]), x[i]),
  43. mix(mix(a[2], b[2], x[i]), mix(c[2], d[2], x[i]), x[i])
  44. );
  45. if (isHotspot(am, bm, xm))
  46. t[found++] = x[i];
  47. }
  48. return found;
  49. }
  50. static int findLinearHotspots(double t[3], const float *a, const float *b) {
  51. int found = 0;
  52. found += findLinearChannelHotspots(t+found, a, b, a[1]-a[0], b[1]-b[0]);
  53. found += findLinearChannelHotspots(t+found, a, b, a[2]-a[1], b[2]-b[1]);
  54. found += findLinearChannelHotspots(t+found, a, b, a[0]-a[2], b[0]-b[2]);
  55. return found;
  56. }
  57. static int findDiagonalHotspots(double t[6], const float *a, const float *b, const float *c, const float *d) {
  58. int found = 0;
  59. found += findDiagonalChannelHotspots(t+found, a, b, c, d, a[1]-a[0], b[1]-b[0], c[1]-c[0], d[1]-d[0]);
  60. found += findDiagonalChannelHotspots(t+found, a, b, c, d, a[2]-a[1], b[2]-b[1], c[2]-c[1], d[2]-d[1]);
  61. found += findDiagonalChannelHotspots(t+found, a, b, c, d, a[0]-a[2], b[0]-b[2], c[0]-c[2], d[0]-d[2]);
  62. return found;
  63. }
  64. template <int N>
  65. void findHotspots(std::vector<Point2> &hotspots, const BitmapConstRef<float, N> &sdf) {
  66. // All hotspots intersect either the horizontal, vertical, or diagonal line that connects neighboring texels
  67. // Horizontal:
  68. for (int y = 0; y < sdf.height; ++y) {
  69. const float *left = sdf(0, y);
  70. const float *right = sdf(1, y);
  71. for (int x = 0; x < sdf.width-1; ++x) {
  72. double t[3];
  73. int found = findLinearHotspots(t, left, right);
  74. for (int i = 0; i < found; ++i)
  75. hotspots.push_back(Point2(x+.5+t[i], y+.5));
  76. left += N, right += N;
  77. }
  78. }
  79. // Vertical:
  80. for (int y = 0; y < sdf.height-1; ++y) {
  81. const float *bottom = sdf(0, y);
  82. const float *top = sdf(0, y+1);
  83. for (int x = 0; x < sdf.width; ++x) {
  84. double t[3];
  85. int found = findLinearHotspots(t, bottom, top);
  86. for (int i = 0; i < found; ++i)
  87. hotspots.push_back(Point2(x+.5, y+.5+t[i]));
  88. bottom += N, top += N;
  89. }
  90. }
  91. // Diagonal:
  92. for (int y = 0; y < sdf.height-1; ++y) {
  93. const float *lb = sdf(0, y);
  94. const float *rb = sdf(1, y);
  95. const float *lt = sdf(0, y+1);
  96. const float *rt = sdf(1, y+1);
  97. for (int x = 0; x < sdf.width-1; ++x) {
  98. double t[6];
  99. int found = 0;
  100. found = findDiagonalHotspots(t, lb, rb, lt, rt);
  101. for (int i = 0; i < found; ++i)
  102. hotspots.push_back(Point2(x+.5+t[i], y+.5+t[i]));
  103. found = findDiagonalHotspots(t, lt, rt, lb, rb);
  104. for (int i = 0; i < found; ++i)
  105. hotspots.push_back(Point2(x+.5+t[i], y+1.5-t[i]));
  106. lb += N, rb += N, lt += N, rt += N;
  107. }
  108. }
  109. }
  110. template <template <typename> class ContourCombiner, int N>
  111. static void msdfPatchEdgeArtifactsInner(const BitmapRef<float, N> &sdf, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate) {
  112. ShapeDistanceFinder<ContourCombiner<PseudoDistanceSelector> > distanceFinder(shape);
  113. std::vector<Point2> hotspots;
  114. findHotspots(hotspots, BitmapConstRef<float, N>(sdf));
  115. std::vector<std::pair<int, int> > artifacts;
  116. artifacts.reserve(hotspots.size());
  117. for (std::vector<Point2>::const_iterator hotspot = hotspots.begin(); hotspot != hotspots.end(); ++hotspot) {
  118. Point2 pos = *hotspot/scale-translate;
  119. double actualDistance = distanceFinder.distance(pos);
  120. float sd = float(actualDistance/range+.5);
  121. // Store hotspot's closest texel's current color
  122. float *subject = sdf((int) hotspot->x, (int) hotspot->y);
  123. float texel[N];
  124. memcpy(texel, subject, N*sizeof(float));
  125. // Sample signed distance at hotspot
  126. float msd[N];
  127. interpolate(msd, BitmapConstRef<float, N>(sdf), *hotspot);
  128. float oldSsd = median(msd[0], msd[1], msd[2]);
  129. // Flatten hotspot's closest texel
  130. float med = median(subject[0], subject[1], subject[2]);
  131. subject[0] = med, subject[1] = med, subject[2] = med;
  132. // Sample signed distance at hotspot after flattening
  133. interpolate(msd, BitmapConstRef<float, N>(sdf), *hotspot);
  134. float newSsd = median(msd[0], msd[1], msd[2]);
  135. // Revert modified texel
  136. memcpy(subject, texel, N*sizeof(float));
  137. // Consider hotspot an artifact if flattening improved the sample
  138. if (fabsf(newSsd-sd) < fabsf(oldSsd-sd))
  139. artifacts.push_back(std::make_pair((int) hotspot->x, (int) hotspot->y));
  140. }
  141. for (std::vector<std::pair<int, int> >::const_iterator artifact = artifacts.begin(); artifact != artifacts.end(); ++artifact) {
  142. float *pixel = sdf(artifact->first, artifact->second);
  143. float med = median(pixel[0], pixel[1], pixel[2]);
  144. pixel[0] = med, pixel[1] = med, pixel[2] = med;
  145. }
  146. }
  147. void msdfPatchEdgeArtifacts(const BitmapRef<float, 3> &sdf, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, bool overlapSupport) {
  148. if (overlapSupport)
  149. msdfPatchEdgeArtifactsInner<OverlappingContourCombiner>(sdf, shape, range, scale, translate);
  150. else
  151. msdfPatchEdgeArtifactsInner<SimpleContourCombiner>(sdf, shape, range, scale, translate);
  152. }
  153. void msdfPatchEdgeArtifacts(const BitmapRef<float, 4> &sdf, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, bool overlapSupport) {
  154. if (overlapSupport)
  155. msdfPatchEdgeArtifactsInner<OverlappingContourCombiner>(sdf, shape, range, scale, translate);
  156. else
  157. msdfPatchEdgeArtifactsInner<SimpleContourCombiner>(sdf, shape, range, scale, translate);
  158. }
  159. }