msdfgen.cpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442
  1. #include "../msdfgen.h"
  2. #include "arithmetics.hpp"
  3. namespace msdfgen {
  4. struct MultiDistance {
  5. double r, g, b;
  6. double med;
  7. };
  8. static inline bool pixelClash(const FloatRGB &a, const FloatRGB &b, double threshold) {
  9. // Only consider pair where both are on the inside or both are on the outside
  10. bool aIn = (a.r > .5f)+(a.g > .5f)+(a.b > .5f) >= 2;
  11. bool bIn = (b.r > .5f)+(b.g > .5f)+(b.b > .5f) >= 2;
  12. if (aIn != bIn) return false;
  13. // If the change is 0 <-> 1 or 2 <-> 3 channels and not 1 <-> 1 or 2 <-> 2, it is not a clash
  14. if ((a.r > .5f && a.g > .5f && a.b > .5f) || (a.r < .5f && a.g < .5f && a.b < .5f)
  15. || (b.r > .5f && b.g > .5f && b.b > .5f) || (b.r < .5f && b.g < .5f && b.b < .5f))
  16. return false;
  17. // Find which color is which: _a, _b = the changing channels, _c = the remaining one
  18. float aa, ab, ba, bb, ac, bc;
  19. if ((a.r > .5f) != (b.r > .5f) && (a.r < .5f) != (b.r < .5f)) {
  20. aa = a.r, ba = b.r;
  21. if ((a.g > .5f) != (b.g > .5f) && (a.g < .5f) != (b.g < .5f)) {
  22. ab = a.g, bb = b.g;
  23. ac = a.b, bc = b.b;
  24. } else if ((a.b > .5f) != (b.b > .5f) && (a.b < .5f) != (b.b < .5f)) {
  25. ab = a.b, bb = b.b;
  26. ac = a.g, bc = b.g;
  27. } else
  28. return false; // this should never happen
  29. } else if ((a.g > .5f) != (b.g > .5f) && (a.g < .5f) != (b.g < .5f)
  30. && (a.b > .5f) != (b.b > .5f) && (a.b < .5f) != (b.b < .5f)) {
  31. aa = a.g, ba = b.g;
  32. ab = a.b, bb = b.b;
  33. ac = a.r, bc = b.r;
  34. } else
  35. return false;
  36. // Find if the channels are in fact discontinuous
  37. return (fabsf(aa-ba) >= threshold)
  38. && (fabsf(ab-bb) >= threshold)
  39. && fabsf(ac-.5f) >= fabsf(bc-.5f); // Out of the pair, only flag the pixel farther from a shape edge
  40. }
  41. void msdfErrorCorrection(Bitmap<FloatRGB> &output, const Vector2 &threshold) {
  42. std::vector<std::pair<int, int> > clashes;
  43. int w = output.width(), h = output.height();
  44. for (int y = 0; y < h; ++y)
  45. for (int x = 0; x < w; ++x) {
  46. if ((x > 0 && pixelClash(output(x, y), output(x-1, y), threshold.x))
  47. || (x < w-1 && pixelClash(output(x, y), output(x+1, y), threshold.x))
  48. || (y > 0 && pixelClash(output(x, y), output(x, y-1), threshold.y))
  49. || (y < h-1 && pixelClash(output(x, y), output(x, y+1), threshold.y)))
  50. clashes.push_back(std::make_pair(x, y));
  51. }
  52. for (std::vector<std::pair<int, int> >::const_iterator clash = clashes.begin(); clash != clashes.end(); ++clash) {
  53. FloatRGB &pixel = output(clash->first, clash->second);
  54. float med = median(pixel.r, pixel.g, pixel.b);
  55. pixel.r = med, pixel.g = med, pixel.b = med;
  56. }
  57. }
  58. void generateSDF(Bitmap<float> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate) {
  59. int contourCount = shape.contours.size();
  60. int w = output.width(), h = output.height();
  61. std::vector<int> windings;
  62. windings.reserve(contourCount);
  63. for (std::vector<Contour>::const_iterator contour = shape.contours.begin(); contour != shape.contours.end(); ++contour)
  64. windings.push_back(contour->winding());
  65. #ifdef MSDFGEN_USE_OPENMP
  66. #pragma omp parallel
  67. #endif
  68. {
  69. std::vector<double> contourSD;
  70. contourSD.resize(contourCount);
  71. #ifdef MSDFGEN_USE_OPENMP
  72. #pragma omp for
  73. #endif
  74. for (int y = 0; y < h; ++y) {
  75. int row = shape.inverseYAxis ? h-y-1 : y;
  76. for (int x = 0; x < w; ++x) {
  77. double dummy;
  78. Point2 p = Vector2(x+.5, y+.5)/scale-translate;
  79. double negDist = -SignedDistance::INFINITE.distance;
  80. double posDist = SignedDistance::INFINITE.distance;
  81. int winding = 0;
  82. std::vector<Contour>::const_iterator contour = shape.contours.begin();
  83. for (int i = 0; i < contourCount; ++i, ++contour) {
  84. SignedDistance minDistance;
  85. for (std::vector<EdgeHolder>::const_iterator edge = contour->edges.begin(); edge != contour->edges.end(); ++edge) {
  86. SignedDistance distance = (*edge)->signedDistance(p, dummy);
  87. if (distance < minDistance)
  88. minDistance = distance;
  89. }
  90. contourSD[i] = minDistance.distance;
  91. if (windings[i] > 0 && minDistance.distance >= 0 && fabs(minDistance.distance) < fabs(posDist))
  92. posDist = minDistance.distance;
  93. if (windings[i] < 0 && minDistance.distance <= 0 && fabs(minDistance.distance) < fabs(negDist))
  94. negDist = minDistance.distance;
  95. }
  96. double sd = SignedDistance::INFINITE.distance;
  97. if (posDist >= 0 && fabs(posDist) <= fabs(negDist)) {
  98. sd = posDist;
  99. winding = 1;
  100. for (int i = 0; i < contourCount; ++i)
  101. if (windings[i] > 0 && contourSD[i] > sd && fabs(contourSD[i]) < fabs(negDist))
  102. sd = contourSD[i];
  103. } else if (negDist <= 0 && fabs(negDist) <= fabs(posDist)) {
  104. sd = negDist;
  105. winding = -1;
  106. for (int i = 0; i < contourCount; ++i)
  107. if (windings[i] < 0 && contourSD[i] < sd && fabs(contourSD[i]) < fabs(posDist))
  108. sd = contourSD[i];
  109. }
  110. for (int i = 0; i < contourCount; ++i)
  111. if (windings[i] != winding && fabs(contourSD[i]) < fabs(sd))
  112. sd = contourSD[i];
  113. output(x, row) = float(sd/range+.5);
  114. }
  115. }
  116. }
  117. }
  118. void generatePseudoSDF(Bitmap<float> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate) {
  119. int contourCount = shape.contours.size();
  120. int w = output.width(), h = output.height();
  121. std::vector<int> windings;
  122. windings.reserve(contourCount);
  123. for (std::vector<Contour>::const_iterator contour = shape.contours.begin(); contour != shape.contours.end(); ++contour)
  124. windings.push_back(contour->winding());
  125. #ifdef MSDFGEN_USE_OPENMP
  126. #pragma omp parallel
  127. #endif
  128. {
  129. std::vector<double> contourSD;
  130. contourSD.resize(contourCount);
  131. #ifdef MSDFGEN_USE_OPENMP
  132. #pragma omp for
  133. #endif
  134. for (int y = 0; y < h; ++y) {
  135. int row = shape.inverseYAxis ? h-y-1 : y;
  136. for (int x = 0; x < w; ++x) {
  137. Point2 p = Vector2(x+.5, y+.5)/scale-translate;
  138. double sd = SignedDistance::INFINITE.distance;
  139. double negDist = -SignedDistance::INFINITE.distance;
  140. double posDist = SignedDistance::INFINITE.distance;
  141. int winding = 0;
  142. std::vector<Contour>::const_iterator contour = shape.contours.begin();
  143. for (int i = 0; i < contourCount; ++i, ++contour) {
  144. SignedDistance minDistance;
  145. const EdgeHolder *nearEdge = NULL;
  146. double nearParam = 0;
  147. for (std::vector<EdgeHolder>::const_iterator edge = contour->edges.begin(); edge != contour->edges.end(); ++edge) {
  148. double param;
  149. SignedDistance distance = (*edge)->signedDistance(p, param);
  150. if (distance < minDistance) {
  151. minDistance = distance;
  152. nearEdge = &*edge;
  153. nearParam = param;
  154. }
  155. }
  156. if (fabs(minDistance.distance) < fabs(sd)) {
  157. sd = minDistance.distance;
  158. winding = -windings[i];
  159. }
  160. if (nearEdge)
  161. (*nearEdge)->distanceToPseudoDistance(minDistance, p, nearParam);
  162. contourSD[i] = minDistance.distance;
  163. if (windings[i] > 0 && minDistance.distance >= 0 && fabs(minDistance.distance) < fabs(posDist))
  164. posDist = minDistance.distance;
  165. if (windings[i] < 0 && minDistance.distance <= 0 && fabs(minDistance.distance) < fabs(negDist))
  166. negDist = minDistance.distance;
  167. }
  168. double psd = SignedDistance::INFINITE.distance;
  169. if (posDist >= 0 && fabs(posDist) <= fabs(negDist)) {
  170. psd = posDist;
  171. winding = 1;
  172. for (int i = 0; i < contourCount; ++i)
  173. if (windings[i] > 0 && contourSD[i] > psd && fabs(contourSD[i]) < fabs(negDist))
  174. psd = contourSD[i];
  175. } else if (negDist <= 0 && fabs(negDist) <= fabs(posDist)) {
  176. psd = negDist;
  177. winding = -1;
  178. for (int i = 0; i < contourCount; ++i)
  179. if (windings[i] < 0 && contourSD[i] < psd && fabs(contourSD[i]) < fabs(posDist))
  180. psd = contourSD[i];
  181. }
  182. for (int i = 0; i < contourCount; ++i)
  183. if (windings[i] != winding && fabs(contourSD[i]) < fabs(psd))
  184. psd = contourSD[i];
  185. output(x, row) = float(psd/range+.5);
  186. }
  187. }
  188. }
  189. }
  190. void generateMSDF(Bitmap<FloatRGB> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, double edgeThreshold) {
  191. int contourCount = shape.contours.size();
  192. int w = output.width(), h = output.height();
  193. std::vector<int> windings;
  194. windings.reserve(contourCount);
  195. for (std::vector<Contour>::const_iterator contour = shape.contours.begin(); contour != shape.contours.end(); ++contour)
  196. windings.push_back(contour->winding());
  197. #ifdef MSDFGEN_USE_OPENMP
  198. #pragma omp parallel
  199. #endif
  200. {
  201. std::vector<MultiDistance> contourSD;
  202. contourSD.resize(contourCount);
  203. #ifdef MSDFGEN_USE_OPENMP
  204. #pragma omp for
  205. #endif
  206. for (int y = 0; y < h; ++y) {
  207. int row = shape.inverseYAxis ? h-y-1 : y;
  208. for (int x = 0; x < w; ++x) {
  209. Point2 p = Vector2(x+.5, y+.5)/scale-translate;
  210. struct EdgePoint {
  211. SignedDistance minDistance;
  212. const EdgeHolder *nearEdge;
  213. double nearParam;
  214. } sr, sg, sb;
  215. sr.nearEdge = sg.nearEdge = sb.nearEdge = NULL;
  216. sr.nearParam = sg.nearParam = sb.nearParam = 0;
  217. double d = fabs(SignedDistance::INFINITE.distance);
  218. double negDist = -SignedDistance::INFINITE.distance;
  219. double posDist = SignedDistance::INFINITE.distance;
  220. int winding = 0;
  221. std::vector<Contour>::const_iterator contour = shape.contours.begin();
  222. for (int i = 0; i < contourCount; ++i, ++contour) {
  223. EdgePoint r, g, b;
  224. r.nearEdge = g.nearEdge = b.nearEdge = NULL;
  225. r.nearParam = g.nearParam = b.nearParam = 0;
  226. for (std::vector<EdgeHolder>::const_iterator edge = contour->edges.begin(); edge != contour->edges.end(); ++edge) {
  227. double param;
  228. SignedDistance distance = (*edge)->signedDistance(p, param);
  229. if ((*edge)->color&RED && distance < r.minDistance) {
  230. r.minDistance = distance;
  231. r.nearEdge = &*edge;
  232. r.nearParam = param;
  233. }
  234. if ((*edge)->color&GREEN && distance < g.minDistance) {
  235. g.minDistance = distance;
  236. g.nearEdge = &*edge;
  237. g.nearParam = param;
  238. }
  239. if ((*edge)->color&BLUE && distance < b.minDistance) {
  240. b.minDistance = distance;
  241. b.nearEdge = &*edge;
  242. b.nearParam = param;
  243. }
  244. }
  245. if (r.minDistance < sr.minDistance)
  246. sr = r;
  247. if (g.minDistance < sg.minDistance)
  248. sg = g;
  249. if (b.minDistance < sb.minDistance)
  250. sb = b;
  251. double medMinDistance = fabs(median(r.minDistance.distance, g.minDistance.distance, b.minDistance.distance));
  252. if (medMinDistance < d) {
  253. d = medMinDistance;
  254. winding = -windings[i];
  255. }
  256. if (r.nearEdge)
  257. (*r.nearEdge)->distanceToPseudoDistance(r.minDistance, p, r.nearParam);
  258. if (g.nearEdge)
  259. (*g.nearEdge)->distanceToPseudoDistance(g.minDistance, p, g.nearParam);
  260. if (b.nearEdge)
  261. (*b.nearEdge)->distanceToPseudoDistance(b.minDistance, p, b.nearParam);
  262. medMinDistance = median(r.minDistance.distance, g.minDistance.distance, b.minDistance.distance);
  263. contourSD[i].r = r.minDistance.distance;
  264. contourSD[i].g = g.minDistance.distance;
  265. contourSD[i].b = b.minDistance.distance;
  266. contourSD[i].med = medMinDistance;
  267. if (windings[i] > 0 && medMinDistance >= 0 && fabs(medMinDistance) < fabs(posDist))
  268. posDist = medMinDistance;
  269. if (windings[i] < 0 && medMinDistance <= 0 && fabs(medMinDistance) < fabs(negDist))
  270. negDist = medMinDistance;
  271. }
  272. if (sr.nearEdge)
  273. (*sr.nearEdge)->distanceToPseudoDistance(sr.minDistance, p, sr.nearParam);
  274. if (sg.nearEdge)
  275. (*sg.nearEdge)->distanceToPseudoDistance(sg.minDistance, p, sg.nearParam);
  276. if (sb.nearEdge)
  277. (*sb.nearEdge)->distanceToPseudoDistance(sb.minDistance, p, sb.nearParam);
  278. MultiDistance msd;
  279. msd.r = msd.g = msd.b = msd.med = SignedDistance::INFINITE.distance;
  280. if (posDist >= 0 && fabs(posDist) <= fabs(negDist)) {
  281. msd.med = SignedDistance::INFINITE.distance;
  282. winding = 1;
  283. for (int i = 0; i < contourCount; ++i)
  284. if (windings[i] > 0 && contourSD[i].med > msd.med && fabs(contourSD[i].med) < fabs(negDist))
  285. msd = contourSD[i];
  286. } else if (negDist <= 0 && fabs(negDist) <= fabs(posDist)) {
  287. msd.med = -SignedDistance::INFINITE.distance;
  288. winding = -1;
  289. for (int i = 0; i < contourCount; ++i)
  290. if (windings[i] < 0 && contourSD[i].med < msd.med && fabs(contourSD[i].med) < fabs(posDist))
  291. msd = contourSD[i];
  292. }
  293. for (int i = 0; i < contourCount; ++i)
  294. if (windings[i] != winding && fabs(contourSD[i].med) < fabs(msd.med))
  295. msd = contourSD[i];
  296. if (median(sr.minDistance.distance, sg.minDistance.distance, sb.minDistance.distance) == msd.med) {
  297. msd.r = sr.minDistance.distance;
  298. msd.g = sg.minDistance.distance;
  299. msd.b = sb.minDistance.distance;
  300. }
  301. output(x, row).r = float(msd.r/range+.5);
  302. output(x, row).g = float(msd.g/range+.5);
  303. output(x, row).b = float(msd.b/range+.5);
  304. }
  305. }
  306. }
  307. if (edgeThreshold > 0)
  308. msdfErrorCorrection(output, edgeThreshold/(scale*range));
  309. }
  310. void generateSDF_legacy(Bitmap<float> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate) {
  311. int w = output.width(), h = output.height();
  312. #ifdef MSDFGEN_USE_OPENMP
  313. #pragma omp parallel for
  314. #endif
  315. for (int y = 0; y < h; ++y) {
  316. int row = shape.inverseYAxis ? h-y-1 : y;
  317. for (int x = 0; x < w; ++x) {
  318. double dummy;
  319. Point2 p = Vector2(x+.5, y+.5)/scale-translate;
  320. SignedDistance minDistance;
  321. for (std::vector<Contour>::const_iterator contour = shape.contours.begin(); contour != shape.contours.end(); ++contour)
  322. for (std::vector<EdgeHolder>::const_iterator edge = contour->edges.begin(); edge != contour->edges.end(); ++edge) {
  323. SignedDistance distance = (*edge)->signedDistance(p, dummy);
  324. if (distance < minDistance)
  325. minDistance = distance;
  326. }
  327. output(x, row) = float(minDistance.distance/range+.5);
  328. }
  329. }
  330. }
  331. void generatePseudoSDF_legacy(Bitmap<float> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate) {
  332. int w = output.width(), h = output.height();
  333. #ifdef MSDFGEN_USE_OPENMP
  334. #pragma omp parallel for
  335. #endif
  336. for (int y = 0; y < h; ++y) {
  337. int row = shape.inverseYAxis ? h-y-1 : y;
  338. for (int x = 0; x < w; ++x) {
  339. Point2 p = Vector2(x+.5, y+.5)/scale-translate;
  340. SignedDistance minDistance;
  341. const EdgeHolder *nearEdge = NULL;
  342. double nearParam = 0;
  343. for (std::vector<Contour>::const_iterator contour = shape.contours.begin(); contour != shape.contours.end(); ++contour)
  344. for (std::vector<EdgeHolder>::const_iterator edge = contour->edges.begin(); edge != contour->edges.end(); ++edge) {
  345. double param;
  346. SignedDistance distance = (*edge)->signedDistance(p, param);
  347. if (distance < minDistance) {
  348. minDistance = distance;
  349. nearEdge = &*edge;
  350. nearParam = param;
  351. }
  352. }
  353. if (nearEdge)
  354. (*nearEdge)->distanceToPseudoDistance(minDistance, p, nearParam);
  355. output(x, row) = float(minDistance.distance/range+.5);
  356. }
  357. }
  358. }
  359. void generateMSDF_legacy(Bitmap<FloatRGB> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate, double edgeThreshold) {
  360. int w = output.width(), h = output.height();
  361. #ifdef MSDFGEN_USE_OPENMP
  362. #pragma omp parallel for
  363. #endif
  364. for (int y = 0; y < h; ++y) {
  365. int row = shape.inverseYAxis ? h-y-1 : y;
  366. for (int x = 0; x < w; ++x) {
  367. Point2 p = Vector2(x+.5, y+.5)/scale-translate;
  368. struct {
  369. SignedDistance minDistance;
  370. const EdgeHolder *nearEdge;
  371. double nearParam;
  372. } r, g, b;
  373. r.nearEdge = g.nearEdge = b.nearEdge = NULL;
  374. r.nearParam = g.nearParam = b.nearParam = 0;
  375. for (std::vector<Contour>::const_iterator contour = shape.contours.begin(); contour != shape.contours.end(); ++contour)
  376. for (std::vector<EdgeHolder>::const_iterator edge = contour->edges.begin(); edge != contour->edges.end(); ++edge) {
  377. double param;
  378. SignedDistance distance = (*edge)->signedDistance(p, param);
  379. if ((*edge)->color&RED && distance < r.minDistance) {
  380. r.minDistance = distance;
  381. r.nearEdge = &*edge;
  382. r.nearParam = param;
  383. }
  384. if ((*edge)->color&GREEN && distance < g.minDistance) {
  385. g.minDistance = distance;
  386. g.nearEdge = &*edge;
  387. g.nearParam = param;
  388. }
  389. if ((*edge)->color&BLUE && distance < b.minDistance) {
  390. b.minDistance = distance;
  391. b.nearEdge = &*edge;
  392. b.nearParam = param;
  393. }
  394. }
  395. if (r.nearEdge)
  396. (*r.nearEdge)->distanceToPseudoDistance(r.minDistance, p, r.nearParam);
  397. if (g.nearEdge)
  398. (*g.nearEdge)->distanceToPseudoDistance(g.minDistance, p, g.nearParam);
  399. if (b.nearEdge)
  400. (*b.nearEdge)->distanceToPseudoDistance(b.minDistance, p, b.nearParam);
  401. output(x, row).r = float(r.minDistance.distance/range+.5);
  402. output(x, row).g = float(g.minDistance.distance/range+.5);
  403. output(x, row).b = float(b.minDistance.distance/range+.5);
  404. }
  405. }
  406. if (edgeThreshold > 0)
  407. msdfErrorCorrection(output, edgeThreshold/(scale*range));
  408. }
  409. }