resize2.js 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. "use strict";
  2. /**
  3. * Copyright (c) 2015 Guyon Roche
  4. *
  5. * Permission is hereby granted, free of charge, to any person obtaining a copy
  6. * of this software and associated documentation files (the "Software"), to deal
  7. * in the Software without restriction, including without limitation the rights
  8. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9. * copies of the Software, and to permit persons to whom the Software is
  10. * furnished to do so, subject to the following conditions:</p>
  11. *
  12. * The above copyright notice and this permission notice shall be included in
  13. * all copies or substantial portions of the Software.
  14. *
  15. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  20. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  21. * THE SOFTWARE.
  22. */
  23. module.exports = {
  24. nearestNeighbor: function nearestNeighbor(src, dst) {
  25. var wSrc = src.width;
  26. var hSrc = src.height;
  27. var wDst = dst.width;
  28. var hDst = dst.height;
  29. var bufSrc = src.data;
  30. var bufDst = dst.data;
  31. for (var i = 0; i < hDst; i++) {
  32. for (var j = 0; j < wDst; j++) {
  33. var posDst = (i * wDst + j) * 4;
  34. var iSrc = Math.floor(i * hSrc / hDst);
  35. var jSrc = Math.floor(j * wSrc / wDst);
  36. var posSrc = (iSrc * wSrc + jSrc) * 4;
  37. bufDst[posDst++] = bufSrc[posSrc++];
  38. bufDst[posDst++] = bufSrc[posSrc++];
  39. bufDst[posDst++] = bufSrc[posSrc++];
  40. bufDst[posDst++] = bufSrc[posSrc++];
  41. }
  42. }
  43. },
  44. bilinearInterpolation: function bilinearInterpolation(src, dst) {
  45. var wSrc = src.width;
  46. var hSrc = src.height;
  47. var wDst = dst.width;
  48. var hDst = dst.height;
  49. var bufSrc = src.data;
  50. var bufDst = dst.data;
  51. var interpolate = function interpolate(k, kMin, vMin, kMax, vMax) {
  52. // special case - k is integer
  53. if (kMin === kMax) {
  54. return vMin;
  55. }
  56. return Math.round((k - kMin) * vMax + (kMax - k) * vMin);
  57. };
  58. var assign = function assign(pos, offset, x, xMin, xMax, y, yMin, yMax) {
  59. var posMin = (yMin * wSrc + xMin) * 4 + offset;
  60. var posMax = (yMin * wSrc + xMax) * 4 + offset;
  61. var vMin = interpolate(x, xMin, bufSrc[posMin], xMax, bufSrc[posMax]); // special case, y is integer
  62. if (yMax === yMin) {
  63. bufDst[pos + offset] = vMin;
  64. } else {
  65. posMin = (yMax * wSrc + xMin) * 4 + offset;
  66. posMax = (yMax * wSrc + xMax) * 4 + offset;
  67. var vMax = interpolate(x, xMin, bufSrc[posMin], xMax, bufSrc[posMax]);
  68. bufDst[pos + offset] = interpolate(y, yMin, vMin, yMax, vMax);
  69. }
  70. };
  71. for (var i = 0; i < hDst; i++) {
  72. for (var j = 0; j < wDst; j++) {
  73. var posDst = (i * wDst + j) * 4; // x & y in src coordinates
  74. var x = j * wSrc / wDst;
  75. var xMin = Math.floor(x);
  76. var xMax = Math.min(Math.ceil(x), wSrc - 1);
  77. var y = i * hSrc / hDst;
  78. var yMin = Math.floor(y);
  79. var yMax = Math.min(Math.ceil(y), hSrc - 1);
  80. assign(posDst, 0, x, xMin, xMax, y, yMin, yMax);
  81. assign(posDst, 1, x, xMin, xMax, y, yMin, yMax);
  82. assign(posDst, 2, x, xMin, xMax, y, yMin, yMax);
  83. assign(posDst, 3, x, xMin, xMax, y, yMin, yMax);
  84. }
  85. }
  86. },
  87. _interpolate2D: function _interpolate2D(src, dst, options, interpolate) {
  88. var bufSrc = src.data;
  89. var bufDst = dst.data;
  90. var wSrc = src.width;
  91. var hSrc = src.height;
  92. var wDst = dst.width;
  93. var hDst = dst.height; // when dst smaller than src/2, interpolate first to a multiple between 0.5 and 1.0 src, then sum squares
  94. var wM = Math.max(1, Math.floor(wSrc / wDst));
  95. var wDst2 = wDst * wM;
  96. var hM = Math.max(1, Math.floor(hSrc / hDst));
  97. var hDst2 = hDst * hM; // ===========================================================
  98. // Pass 1 - interpolate rows
  99. // buf1 has width of dst2 and height of src
  100. var buf1 = Buffer.alloc(wDst2 * hSrc * 4);
  101. for (var i = 0; i < hSrc; i++) {
  102. for (var j = 0; j < wDst2; j++) {
  103. // i in src coords, j in dst coords
  104. // calculate x in src coords
  105. // this interpolation requires 4 sample points and the two inner ones must be real
  106. // the outer points can be fudged for the edges.
  107. // therefore (wSrc-1)/wDst2
  108. var x = j * (wSrc - 1) / wDst2;
  109. var xPos = Math.floor(x);
  110. var t = x - xPos;
  111. var srcPos = (i * wSrc + xPos) * 4;
  112. var buf1Pos = (i * wDst2 + j) * 4;
  113. for (var k = 0; k < 4; k++) {
  114. var kPos = srcPos + k;
  115. var x0 = xPos > 0 ? bufSrc[kPos - 4] : 2 * bufSrc[kPos] - bufSrc[kPos + 4];
  116. var x1 = bufSrc[kPos];
  117. var x2 = bufSrc[kPos + 4];
  118. var x3 = xPos < wSrc - 2 ? bufSrc[kPos + 8] : 2 * bufSrc[kPos + 4] - bufSrc[kPos];
  119. buf1[buf1Pos + k] = interpolate(x0, x1, x2, x3, t);
  120. }
  121. }
  122. } // this._writeFile(wDst2, hSrc, buf1, "out/buf1.jpg");
  123. // ===========================================================
  124. // Pass 2 - interpolate columns
  125. // buf2 has width and height of dst2
  126. var buf2 = Buffer.alloc(wDst2 * hDst2 * 4);
  127. for (var _i = 0; _i < hDst2; _i++) {
  128. for (var _j = 0; _j < wDst2; _j++) {
  129. // i&j in dst2 coords
  130. // calculate y in buf1 coords
  131. // this interpolation requires 4 sample points and the two inner ones must be real
  132. // the outer points can be fudged for the edges.
  133. // therefore (hSrc-1)/hDst2
  134. var y = _i * (hSrc - 1) / hDst2;
  135. var yPos = Math.floor(y);
  136. var _t = y - yPos;
  137. var _buf1Pos = (yPos * wDst2 + _j) * 4;
  138. var buf2Pos = (_i * wDst2 + _j) * 4;
  139. for (var _k = 0; _k < 4; _k++) {
  140. var _kPos = _buf1Pos + _k;
  141. var y0 = yPos > 0 ? buf1[_kPos - wDst2 * 4] : 2 * buf1[_kPos] - buf1[_kPos + wDst2 * 4];
  142. var y1 = buf1[_kPos];
  143. var y2 = buf1[_kPos + wDst2 * 4];
  144. var y3 = yPos < hSrc - 2 ? buf1[_kPos + wDst2 * 8] : 2 * buf1[_kPos + wDst2 * 4] - buf1[_kPos];
  145. buf2[buf2Pos + _k] = interpolate(y0, y1, y2, y3, _t);
  146. }
  147. }
  148. } // this._writeFile(wDst2, hDst2, buf2, "out/buf2.jpg");
  149. // ===========================================================
  150. // Pass 3 - scale to dst
  151. var m = wM * hM;
  152. if (m > 1) {
  153. for (var _i2 = 0; _i2 < hDst; _i2++) {
  154. for (var _j2 = 0; _j2 < wDst; _j2++) {
  155. // i&j in dst bounded coords
  156. var r = 0;
  157. var g = 0;
  158. var b = 0;
  159. var a = 0;
  160. var realColors = 0;
  161. for (var _y = 0; _y < hM; _y++) {
  162. var _yPos = _i2 * hM + _y;
  163. for (var _x = 0; _x < wM; _x++) {
  164. var _xPos = _j2 * wM + _x;
  165. var xyPos = (_yPos * wDst2 + _xPos) * 4;
  166. var pixelAlpha = buf2[xyPos + 3];
  167. if (pixelAlpha) {
  168. r += buf2[xyPos];
  169. g += buf2[xyPos + 1];
  170. b += buf2[xyPos + 2];
  171. realColors++;
  172. }
  173. a += pixelAlpha;
  174. }
  175. }
  176. var pos = (_i2 * wDst + _j2) * 4;
  177. bufDst[pos] = realColors ? Math.round(r / realColors) : 0;
  178. bufDst[pos + 1] = realColors ? Math.round(g / realColors) : 0;
  179. bufDst[pos + 2] = realColors ? Math.round(b / realColors) : 0;
  180. bufDst[pos + 3] = Math.round(a / m);
  181. }
  182. }
  183. } else {
  184. // replace dst buffer with buf2
  185. dst.data = buf2;
  186. }
  187. },
  188. bicubicInterpolation: function bicubicInterpolation(src, dst, options) {
  189. var interpolateCubic = function interpolateCubic(x0, x1, x2, x3, t) {
  190. var a0 = x3 - x2 - x0 + x1;
  191. var a1 = x0 - x1 - a0;
  192. var a2 = x2 - x0;
  193. var a3 = x1;
  194. return Math.max(0, Math.min(255, a0 * (t * t * t) + a1 * (t * t) + a2 * t + a3));
  195. };
  196. return this._interpolate2D(src, dst, options, interpolateCubic);
  197. },
  198. hermiteInterpolation: function hermiteInterpolation(src, dst, options) {
  199. var interpolateHermite = function interpolateHermite(x0, x1, x2, x3, t) {
  200. var c0 = x1;
  201. var c1 = 0.5 * (x2 - x0);
  202. var c2 = x0 - 2.5 * x1 + 2 * x2 - 0.5 * x3;
  203. var c3 = 0.5 * (x3 - x0) + 1.5 * (x1 - x2);
  204. return Math.max(0, Math.min(255, Math.round(((c3 * t + c2) * t + c1) * t + c0)));
  205. };
  206. return this._interpolate2D(src, dst, options, interpolateHermite);
  207. },
  208. bezierInterpolation: function bezierInterpolation(src, dst, options) {
  209. // between 2 points y(n), y(n+1), use next points out, y(n-1), y(n+2)
  210. // to predict control points (a & b) to be placed at n+0.5
  211. // ya(n) = y(n) + (y(n+1)-y(n-1))/4
  212. // yb(n) = y(n+1) - (y(n+2)-y(n))/4
  213. // then use std bezier to interpolate [n,n+1)
  214. // y(n+t) = y(n)*(1-t)^3 + 3 * ya(n)*(1-t)^2*t + 3 * yb(n)*(1-t)*t^2 + y(n+1)*t^3
  215. // note the 3* factor for the two control points
  216. // for edge cases, can choose:
  217. // y(-1) = y(0) - 2*(y(1)-y(0))
  218. // y(w) = y(w-1) + 2*(y(w-1)-y(w-2))
  219. // but can go with y(-1) = y(0) and y(w) = y(w-1)
  220. var interpolateBezier = function interpolateBezier(x0, x1, x2, x3, t) {
  221. // x1, x2 are the knots, use x0 and x3 to calculate control points
  222. var cp1 = x1 + (x2 - x0) / 4;
  223. var cp2 = x2 - (x3 - x1) / 4;
  224. var nt = 1 - t;
  225. var c0 = x1 * nt * nt * nt;
  226. var c1 = 3 * cp1 * nt * nt * t;
  227. var c2 = 3 * cp2 * nt * t * t;
  228. var c3 = x2 * t * t * t;
  229. return Math.max(0, Math.min(255, Math.round(c0 + c1 + c2 + c3)));
  230. };
  231. return this._interpolate2D(src, dst, options, interpolateBezier);
  232. }
  233. };
  234. //# sourceMappingURL=resize2.js.map