index.js 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. 'use strict'
  2. module.exports = calcSDF
  3. var INF = 1e20
  4. function calcSDF(src, options) {
  5. if (!options) options = {}
  6. var cutoff = options.cutoff == null ? 0.25 : options.cutoff
  7. var radius = options.radius == null ? 8 : options.radius
  8. var channel = options.channel || 0
  9. var w, h, size, data, intData, stride, ctx, canvas, imgData, i, l
  10. // handle image container
  11. if (ArrayBuffer.isView(src) || Array.isArray(src)) {
  12. if (!options.width || !options.height) throw Error('For raw data width and height should be provided by options')
  13. w = options.width, h = options.height
  14. data = src
  15. if (!options.stride) stride = Math.floor(src.length / w / h)
  16. else stride = options.stride
  17. }
  18. else {
  19. if (window.HTMLCanvasElement && src instanceof window.HTMLCanvasElement) {
  20. canvas = src
  21. ctx = canvas.getContext('2d')
  22. w = canvas.width, h = canvas.height
  23. imgData = ctx.getImageData(0, 0, w, h)
  24. data = imgData.data
  25. stride = 4
  26. }
  27. else if (window.CanvasRenderingContext2D && src instanceof window.CanvasRenderingContext2D) {
  28. canvas = src.canvas
  29. ctx = src
  30. w = canvas.width, h = canvas.height
  31. imgData = ctx.getImageData(0, 0, w, h)
  32. data = imgData.data
  33. stride = 4
  34. }
  35. else if (window.ImageData && src instanceof window.ImageData) {
  36. imgData = src
  37. w = src.width, h = src.height
  38. data = imgData.data
  39. stride = 4
  40. }
  41. }
  42. size = Math.max(w, h)
  43. //convert int data to floats
  44. if ((window.Uint8ClampedArray && data instanceof window.Uint8ClampedArray) || (window.Uint8Array && data instanceof window.Uint8Array)) {
  45. intData = data
  46. data = Array(w*h)
  47. for (i = 0, l = Math.floor(intData.length / stride); i < l; i++) {
  48. data[i] = intData[i*stride + channel] / 255
  49. }
  50. }
  51. else {
  52. if (stride !== 1) throw Error('Raw data can have only 1 value per pixel')
  53. }
  54. // temporary arrays for the distance transform
  55. var gridOuter = Array(w * h)
  56. var gridInner = Array(w * h)
  57. var f = Array(size)
  58. var d = Array(size)
  59. var z = Array(size + 1)
  60. var v = Array(size)
  61. for (i = 0, l = w * h; i < l; i++) {
  62. var a = data[i]
  63. gridOuter[i] = a === 1 ? 0 : a === 0 ? INF : Math.pow(Math.max(0, 0.5 - a), 2)
  64. gridInner[i] = a === 1 ? INF : a === 0 ? 0 : Math.pow(Math.max(0, a - 0.5), 2)
  65. }
  66. edt(gridOuter, w, h, f, d, v, z)
  67. edt(gridInner, w, h, f, d, v, z)
  68. var dist = window.Float32Array ? new Float32Array(w * h) : new Array(w * h)
  69. for (i = 0, l = w*h; i < l; i++) {
  70. dist[i] = Math.min(Math.max(1 - ( (gridOuter[i] - gridInner[i]) / radius + cutoff), 0), 1)
  71. }
  72. return dist
  73. }
  74. // 2D Euclidean distance transform by Felzenszwalb & Huttenlocher https://cs.brown.edu/~pff/dt/
  75. function edt(data, width, height, f, d, v, z) {
  76. for (var x = 0; x < width; x++) {
  77. for (var y = 0; y < height; y++) {
  78. f[y] = data[y * width + x]
  79. }
  80. edt1d(f, d, v, z, height)
  81. for (y = 0; y < height; y++) {
  82. data[y * width + x] = d[y]
  83. }
  84. }
  85. for (y = 0; y < height; y++) {
  86. for (x = 0; x < width; x++) {
  87. f[x] = data[y * width + x]
  88. }
  89. edt1d(f, d, v, z, width)
  90. for (x = 0; x < width; x++) {
  91. data[y * width + x] = Math.sqrt(d[x])
  92. }
  93. }
  94. }
  95. // 1D squared distance transform
  96. function edt1d(f, d, v, z, n) {
  97. v[0] = 0;
  98. z[0] = -INF
  99. z[1] = +INF
  100. for (var q = 1, k = 0; q < n; q++) {
  101. var s = ((f[q] + q * q) - (f[v[k]] + v[k] * v[k])) / (2 * q - 2 * v[k])
  102. while (s <= z[k]) {
  103. k--
  104. s = ((f[q] + q * q) - (f[v[k]] + v[k] * v[k])) / (2 * q - 2 * v[k])
  105. }
  106. k++
  107. v[k] = q
  108. z[k] = s
  109. z[k + 1] = +INF
  110. }
  111. for (q = 0, k = 0; q < n; q++) {
  112. while (z[k + 1] < q) k++
  113. d[q] = (q - v[k]) * (q - v[k]) + f[v[k]]
  114. }
  115. }