2
0

approximate-sdf.cpp 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. #include "approximate-sdf.h"
  2. #include <cmath>
  3. #include <queue>
  4. #include "arithmetics.hpp"
  5. #define ESTSDF_MAX_DIST 1e24f // Cannot be FLT_MAX because it might be divided by range, which could be < 1
  6. namespace msdfgen {
  7. void approximateSDF(const BitmapRef<float, 1> &output, const Shape &shape, const SDFTransformation &transformation) {
  8. struct Entry {
  9. float absDist;
  10. int bitmapX, bitmapY;
  11. Point2 nearPoint;
  12. bool operator<(const Entry &other) const {
  13. return absDist > other.absDist;
  14. }
  15. } entry;
  16. float *firstRow = output.pixels;
  17. ptrdiff_t stride = output.width;
  18. if (shape.inverseYAxis) {
  19. firstRow += (output.height-1)*stride;
  20. stride = -stride;
  21. }
  22. #define ESTSDF_PIXEL_AT(x, y) ((firstRow+(y)*stride)[x])
  23. for (float *p = output.pixels, *end = output.pixels+output.width*output.height; p < end; ++p)
  24. *p = -ESTSDF_MAX_DIST;
  25. Vector2 invScale = transformation.unprojectVector(Vector2(1));
  26. DistanceMapping invDistanceMapping = transformation.distanceMapping.inverse();
  27. float dLimit = float(max(fabs(invDistanceMapping(0)), fabs(invDistanceMapping(1))));
  28. std::priority_queue<Entry> queue;
  29. double x[3], y[3];
  30. int dx[3], dy[3];
  31. // Horizontal scanlines
  32. for (int bitmapY = 0; bitmapY < output.height; ++bitmapY) {
  33. float *row = firstRow+bitmapY*stride;
  34. double y = transformation.unprojectY(bitmapY+.5);
  35. entry.bitmapY = bitmapY;
  36. for (std::vector<Contour>::const_iterator contour = shape.contours.begin(); contour != shape.contours.end(); ++contour) {
  37. for (std::vector<EdgeHolder>::const_iterator edge = contour->edges.begin(); edge != contour->edges.end(); ++edge) {
  38. int n = (*edge)->horizontalScanlineIntersections(x, dy, y);
  39. for (int i = 0; i < n; ++i) {
  40. double bitmapX = transformation.projectX(x[i]);
  41. double bitmapX0 = floor(bitmapX-.5)+.5;
  42. double bitmapX1 = bitmapX0+1;
  43. if (bitmapX1 > 0 && bitmapX0 < output.width) {
  44. float sd0 = float(dy[i]*invScale.x*(bitmapX0-bitmapX));
  45. float sd1 = float(dy[i]*invScale.x*(bitmapX1-bitmapX));
  46. if (sd0 == 0.f) {
  47. if (sd1 == 0.f)
  48. continue;
  49. sd0 = -.000001f*float(sign(sd1));
  50. }
  51. if (sd1 == 0.f)
  52. sd1 = -.000001f*float(sign(sd0));
  53. if (bitmapX0 > 0) {
  54. entry.absDist = fabsf(sd0);
  55. entry.bitmapX = int(bitmapX0);
  56. float &sd = row[entry.bitmapX];
  57. if (entry.absDist < fabsf(sd)) {
  58. sd = sd0;
  59. entry.nearPoint = Point2(x[i], y);
  60. queue.push(entry);
  61. } else if (sd == -sd0)
  62. sd = -ESTSDF_MAX_DIST;
  63. }
  64. if (bitmapX1 < output.width) {
  65. entry.absDist = fabsf(sd1);
  66. entry.bitmapX = int(bitmapX1);
  67. float &sd = row[entry.bitmapX];
  68. if (entry.absDist < fabsf(sd)) {
  69. sd = sd1;
  70. entry.nearPoint = Point2(x[i], y);
  71. queue.push(entry);
  72. } else if (sd == -sd1)
  73. sd = -ESTSDF_MAX_DIST;
  74. }
  75. }
  76. }
  77. }
  78. }
  79. }
  80. // Bake in distance signs
  81. for (int y = 0; y < output.height; ++y) {
  82. float *row = firstRow+y*stride;
  83. int x = 0;
  84. for (; x < output.width && row[x] == -ESTSDF_MAX_DIST; ++x);
  85. if (x < output.width) {
  86. bool flip = row[x] > 0;
  87. if (flip) {
  88. for (int i = 0; i < x; ++i)
  89. row[i] = ESTSDF_MAX_DIST;
  90. }
  91. for (; x < output.width; ++x) {
  92. if (row[x] != -ESTSDF_MAX_DIST)
  93. flip = row[x] > 0;
  94. else if (flip)
  95. row[x] = ESTSDF_MAX_DIST;
  96. }
  97. }
  98. }
  99. // Vertical scanlines
  100. for (int bitmapX = 0; bitmapX < output.width; ++bitmapX) {
  101. double x = transformation.unprojectX(bitmapX+.5);
  102. entry.bitmapX = bitmapX;
  103. for (std::vector<Contour>::const_iterator contour = shape.contours.begin(); contour != shape.contours.end(); ++contour) {
  104. for (std::vector<EdgeHolder>::const_iterator edge = contour->edges.begin(); edge != contour->edges.end(); ++edge) {
  105. int n = (*edge)->verticalScanlineIntersections(y, dx, x);
  106. for (int i = 0; i < n; ++i) {
  107. double bitmapY = transformation.projectY(y[i]);
  108. double bitmapY0 = floor(bitmapY-.5)+.5;
  109. double bitmapY1 = bitmapY0+1;
  110. if (bitmapY0 > 0 && bitmapY1 < output.height) {
  111. float sd0 = float(dx[i]*invScale.y*(bitmapY-bitmapY0));
  112. float sd1 = float(dx[i]*invScale.y*(bitmapY-bitmapY1));
  113. if (sd0 == 0.f) {
  114. if (sd1 == 0.f)
  115. continue;
  116. sd0 = -.000001f*float(sign(sd1));
  117. }
  118. if (sd1 == 0.f)
  119. sd1 = -.000001f*float(sign(sd0));
  120. if (bitmapY0 > 0) {
  121. entry.absDist = fabsf(sd0);
  122. entry.bitmapY = int(bitmapY0);
  123. float &sd = ESTSDF_PIXEL_AT(bitmapX, entry.bitmapY);
  124. if (entry.absDist < fabsf(sd)) {
  125. sd = sd0;
  126. entry.nearPoint = Point2(x, y[i]);
  127. queue.push(entry);
  128. }
  129. }
  130. if (bitmapY1 < output.height) {
  131. entry.absDist = fabsf(sd1);
  132. entry.bitmapY = int(bitmapY1);
  133. float &sd = ESTSDF_PIXEL_AT(bitmapX, entry.bitmapY);
  134. if (entry.absDist < fabsf(sd)) {
  135. sd = sd1;
  136. entry.nearPoint = Point2(x, y[i]);
  137. queue.push(entry);
  138. }
  139. }
  140. }
  141. }
  142. }
  143. }
  144. }
  145. if (queue.empty())
  146. return;
  147. while (!queue.empty()) {
  148. Entry entry = queue.top();
  149. queue.pop();
  150. Entry newEntry = entry;
  151. newEntry.bitmapX = entry.bitmapX-1;
  152. if (newEntry.bitmapX >= 0) {
  153. float &sd = ESTSDF_PIXEL_AT(newEntry.bitmapX, newEntry.bitmapY);
  154. if (fabsf(sd) == ESTSDF_MAX_DIST) {
  155. Point2 shapeCoord = transformation.unproject(Point2(newEntry.bitmapX+.5, newEntry.bitmapY+.5));
  156. newEntry.absDist = float((shapeCoord-entry.nearPoint).length());
  157. sd = float(sign(sd))*newEntry.absDist;
  158. if (newEntry.absDist < dLimit)
  159. queue.push(newEntry);
  160. }
  161. }
  162. newEntry.bitmapX = entry.bitmapX+1;
  163. if (newEntry.bitmapX < output.width) {
  164. float &sd = ESTSDF_PIXEL_AT(newEntry.bitmapX, newEntry.bitmapY);
  165. if (fabsf(sd) == ESTSDF_MAX_DIST) {
  166. Point2 shapeCoord = transformation.unproject(Point2(newEntry.bitmapX+.5, newEntry.bitmapY+.5));
  167. newEntry.absDist = float((shapeCoord-entry.nearPoint).length());
  168. sd = float(sign(sd))*newEntry.absDist;
  169. if (newEntry.absDist < dLimit)
  170. queue.push(newEntry);
  171. }
  172. }
  173. newEntry.bitmapX = entry.bitmapX;
  174. newEntry.bitmapY = entry.bitmapY-1;
  175. if (newEntry.bitmapY >= 0) {
  176. float &sd = ESTSDF_PIXEL_AT(newEntry.bitmapX, newEntry.bitmapY);
  177. if (fabsf(sd) == ESTSDF_MAX_DIST) {
  178. Point2 shapeCoord = transformation.unproject(Point2(newEntry.bitmapX+.5, newEntry.bitmapY+.5));
  179. newEntry.absDist = float((shapeCoord-entry.nearPoint).length());
  180. sd = float(sign(sd))*newEntry.absDist;
  181. if (newEntry.absDist < dLimit)
  182. queue.push(newEntry);
  183. }
  184. }
  185. newEntry.bitmapY = entry.bitmapY+1;
  186. if (newEntry.bitmapY < output.height) {
  187. float &sd = ESTSDF_PIXEL_AT(newEntry.bitmapX, newEntry.bitmapY);
  188. if (fabsf(sd) == ESTSDF_MAX_DIST) {
  189. Point2 shapeCoord = transformation.unproject(Point2(newEntry.bitmapX+.5, newEntry.bitmapY+.5));
  190. newEntry.absDist = float((shapeCoord-entry.nearPoint).length());
  191. sd = float(sign(sd))*newEntry.absDist;
  192. if (newEntry.absDist < dLimit)
  193. queue.push(newEntry);
  194. }
  195. }
  196. }
  197. for (float *p = output.pixels, *end = output.pixels+output.width*output.height; p < end; ++p)
  198. *p = transformation.distanceMapping(*p);
  199. }
  200. }