resize.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464
  1. "use strict";
  2. require("core-js/modules/es.array.iterator");
  3. require("core-js/modules/es.array-buffer.slice");
  4. require("core-js/modules/es.object.to-string");
  5. require("core-js/modules/es.typed-array.float32-array");
  6. require("core-js/modules/es.typed-array.float64-array");
  7. require("core-js/modules/es.typed-array.uint8-array");
  8. require("core-js/modules/es.typed-array.copy-within");
  9. require("core-js/modules/es.typed-array.every");
  10. require("core-js/modules/es.typed-array.fill");
  11. require("core-js/modules/es.typed-array.filter");
  12. require("core-js/modules/es.typed-array.find");
  13. require("core-js/modules/es.typed-array.find-index");
  14. require("core-js/modules/es.typed-array.for-each");
  15. require("core-js/modules/es.typed-array.includes");
  16. require("core-js/modules/es.typed-array.index-of");
  17. require("core-js/modules/es.typed-array.iterator");
  18. require("core-js/modules/es.typed-array.join");
  19. require("core-js/modules/es.typed-array.last-index-of");
  20. require("core-js/modules/es.typed-array.map");
  21. require("core-js/modules/es.typed-array.reduce");
  22. require("core-js/modules/es.typed-array.reduce-right");
  23. require("core-js/modules/es.typed-array.reverse");
  24. require("core-js/modules/es.typed-array.set");
  25. require("core-js/modules/es.typed-array.slice");
  26. require("core-js/modules/es.typed-array.some");
  27. require("core-js/modules/es.typed-array.sort");
  28. require("core-js/modules/es.typed-array.subarray");
  29. require("core-js/modules/es.typed-array.to-locale-string");
  30. require("core-js/modules/es.typed-array.to-string");
  31. // JavaScript Image Resizer (c) 2012 - Grant Galitz
  32. // Released to public domain 29 July 2013: https://github.com/grantgalitz/JS-Image-Resizer/issues/4
  33. function Resize(widthOriginal, heightOriginal, targetWidth, targetHeight, blendAlpha, interpolationPass, resizeCallback) {
  34. this.widthOriginal = Math.abs(Math.floor(widthOriginal) || 0);
  35. this.heightOriginal = Math.abs(Math.floor(heightOriginal) || 0);
  36. this.targetWidth = Math.abs(Math.floor(targetWidth) || 0);
  37. this.targetHeight = Math.abs(Math.floor(targetHeight) || 0);
  38. this.colorChannels = blendAlpha ? 4 : 3;
  39. this.interpolationPass = Boolean(interpolationPass);
  40. this.resizeCallback = typeof resizeCallback === 'function' ? resizeCallback : function () {};
  41. this.targetWidthMultipliedByChannels = this.targetWidth * this.colorChannels;
  42. this.originalWidthMultipliedByChannels = this.widthOriginal * this.colorChannels;
  43. this.originalHeightMultipliedByChannels = this.heightOriginal * this.colorChannels;
  44. this.widthPassResultSize = this.targetWidthMultipliedByChannels * this.heightOriginal;
  45. this.finalResultSize = this.targetWidthMultipliedByChannels * this.targetHeight;
  46. this.initialize();
  47. }
  48. Resize.prototype.initialize = function () {
  49. // Perform some checks:
  50. if (this.widthOriginal > 0 && this.heightOriginal > 0 && this.targetWidth > 0 && this.targetHeight > 0) {
  51. this.configurePasses();
  52. } else {
  53. throw new Error('Invalid settings specified for the resizer.');
  54. }
  55. };
  56. Resize.prototype.configurePasses = function () {
  57. if (this.widthOriginal === this.targetWidth) {
  58. // Bypass the width resizer pass:
  59. this.resizeWidth = this.bypassResizer;
  60. } else {
  61. // Setup the width resizer pass:
  62. this.ratioWeightWidthPass = this.widthOriginal / this.targetWidth;
  63. if (this.ratioWeightWidthPass < 1 && this.interpolationPass) {
  64. this.initializeFirstPassBuffers(true);
  65. this.resizeWidth = this.colorChannels === 4 ? this.resizeWidthInterpolatedRGBA : this.resizeWidthInterpolatedRGB;
  66. } else {
  67. this.initializeFirstPassBuffers(false);
  68. this.resizeWidth = this.colorChannels === 4 ? this.resizeWidthRGBA : this.resizeWidthRGB;
  69. }
  70. }
  71. if (this.heightOriginal === this.targetHeight) {
  72. // Bypass the height resizer pass:
  73. this.resizeHeight = this.bypassResizer;
  74. } else {
  75. // Setup the height resizer pass:
  76. this.ratioWeightHeightPass = this.heightOriginal / this.targetHeight;
  77. if (this.ratioWeightHeightPass < 1 && this.interpolationPass) {
  78. this.initializeSecondPassBuffers(true);
  79. this.resizeHeight = this.resizeHeightInterpolated;
  80. } else {
  81. this.initializeSecondPassBuffers(false);
  82. this.resizeHeight = this.colorChannels === 4 ? this.resizeHeightRGBA : this.resizeHeightRGB;
  83. }
  84. }
  85. };
  86. Resize.prototype._resizeWidthInterpolatedRGBChannels = function (buffer, fourthChannel) {
  87. var channelsNum = fourthChannel ? 4 : 3;
  88. var ratioWeight = this.ratioWeightWidthPass;
  89. var outputBuffer = this.widthBuffer;
  90. var weight = 0;
  91. var finalOffset = 0;
  92. var pixelOffset = 0;
  93. var firstWeight = 0;
  94. var secondWeight = 0;
  95. var targetPosition; // Handle for only one interpolation input being valid for start calculation:
  96. for (targetPosition = 0; weight < 1 / 3; targetPosition += channelsNum, weight += ratioWeight) {
  97. for (finalOffset = targetPosition, pixelOffset = 0; finalOffset < this.widthPassResultSize; pixelOffset += this.originalWidthMultipliedByChannels, finalOffset += this.targetWidthMultipliedByChannels) {
  98. outputBuffer[finalOffset] = buffer[pixelOffset];
  99. outputBuffer[finalOffset + 1] = buffer[pixelOffset + 1];
  100. outputBuffer[finalOffset + 2] = buffer[pixelOffset + 2];
  101. if (fourthChannel) outputBuffer[finalOffset + 3] = buffer[pixelOffset + 3];
  102. }
  103. } // Adjust for overshoot of the last pass's counter:
  104. weight -= 1 / 3;
  105. var interpolationWidthSourceReadStop;
  106. for (interpolationWidthSourceReadStop = this.widthOriginal - 1; weight < interpolationWidthSourceReadStop; targetPosition += channelsNum, weight += ratioWeight) {
  107. // Calculate weightings:
  108. secondWeight = weight % 1;
  109. firstWeight = 1 - secondWeight; // Interpolate:
  110. for (finalOffset = targetPosition, pixelOffset = Math.floor(weight) * channelsNum; finalOffset < this.widthPassResultSize; pixelOffset += this.originalWidthMultipliedByChannels, finalOffset += this.targetWidthMultipliedByChannels) {
  111. outputBuffer[finalOffset + 0] = buffer[pixelOffset + 0] * firstWeight + buffer[pixelOffset + channelsNum + 0] * secondWeight;
  112. outputBuffer[finalOffset + 1] = buffer[pixelOffset + 1] * firstWeight + buffer[pixelOffset + channelsNum + 1] * secondWeight;
  113. outputBuffer[finalOffset + 2] = buffer[pixelOffset + 2] * firstWeight + buffer[pixelOffset + channelsNum + 2] * secondWeight;
  114. if (fourthChannel) outputBuffer[finalOffset + 3] = buffer[pixelOffset + 3] * firstWeight + buffer[pixelOffset + channelsNum + 3] * secondWeight;
  115. }
  116. } // Handle for only one interpolation input being valid for end calculation:
  117. for (interpolationWidthSourceReadStop = this.originalWidthMultipliedByChannels - channelsNum; targetPosition < this.targetWidthMultipliedByChannels; targetPosition += channelsNum) {
  118. for (finalOffset = targetPosition, pixelOffset = interpolationWidthSourceReadStop; finalOffset < this.widthPassResultSize; pixelOffset += this.originalWidthMultipliedByChannels, finalOffset += this.targetWidthMultipliedByChannels) {
  119. outputBuffer[finalOffset] = buffer[pixelOffset];
  120. outputBuffer[finalOffset + 1] = buffer[pixelOffset + 1];
  121. outputBuffer[finalOffset + 2] = buffer[pixelOffset + 2];
  122. if (fourthChannel) outputBuffer[finalOffset + 3] = buffer[pixelOffset + 3];
  123. }
  124. }
  125. return outputBuffer;
  126. };
  127. Resize.prototype._resizeWidthRGBChannels = function (buffer, fourthChannel) {
  128. var channelsNum = fourthChannel ? 4 : 3;
  129. var ratioWeight = this.ratioWeightWidthPass;
  130. var ratioWeightDivisor = 1 / ratioWeight;
  131. var nextLineOffsetOriginalWidth = this.originalWidthMultipliedByChannels - channelsNum + 1;
  132. var nextLineOffsetTargetWidth = this.targetWidthMultipliedByChannels - channelsNum + 1;
  133. var output = this.outputWidthWorkBench;
  134. var outputBuffer = this.widthBuffer;
  135. var trustworthyColorsCount = this.outputWidthWorkBenchOpaquePixelsCount;
  136. var weight = 0;
  137. var amountToNext = 0;
  138. var actualPosition = 0;
  139. var currentPosition = 0;
  140. var line = 0;
  141. var pixelOffset = 0;
  142. var outputOffset = 0;
  143. var multiplier = 1;
  144. var r = 0;
  145. var g = 0;
  146. var b = 0;
  147. var a = 0;
  148. do {
  149. for (line = 0; line < this.originalHeightMultipliedByChannels;) {
  150. output[line++] = 0;
  151. output[line++] = 0;
  152. output[line++] = 0;
  153. if (fourthChannel) {
  154. output[line++] = 0;
  155. trustworthyColorsCount[line / channelsNum - 1] = 0;
  156. }
  157. }
  158. weight = ratioWeight;
  159. do {
  160. amountToNext = 1 + actualPosition - currentPosition;
  161. multiplier = Math.min(weight, amountToNext);
  162. for (line = 0, pixelOffset = actualPosition; line < this.originalHeightMultipliedByChannels; pixelOffset += nextLineOffsetOriginalWidth) {
  163. r = buffer[pixelOffset];
  164. g = buffer[++pixelOffset];
  165. b = buffer[++pixelOffset];
  166. a = fourthChannel ? buffer[++pixelOffset] : 255; // Ignore RGB values if pixel is completely transparent
  167. output[line++] += (a ? r : 0) * multiplier;
  168. output[line++] += (a ? g : 0) * multiplier;
  169. output[line++] += (a ? b : 0) * multiplier;
  170. if (fourthChannel) {
  171. output[line++] += a * multiplier;
  172. trustworthyColorsCount[line / channelsNum - 1] += a ? multiplier : 0;
  173. }
  174. }
  175. if (weight >= amountToNext) {
  176. actualPosition += channelsNum;
  177. currentPosition = actualPosition;
  178. weight -= amountToNext;
  179. } else {
  180. currentPosition += weight;
  181. break;
  182. }
  183. } while (weight > 0 && actualPosition < this.originalWidthMultipliedByChannels);
  184. for (line = 0, pixelOffset = outputOffset; line < this.originalHeightMultipliedByChannels; pixelOffset += nextLineOffsetTargetWidth) {
  185. weight = fourthChannel ? trustworthyColorsCount[line / channelsNum] : 1;
  186. multiplier = fourthChannel ? weight ? 1 / weight : 0 : ratioWeightDivisor;
  187. outputBuffer[pixelOffset] = output[line++] * multiplier;
  188. outputBuffer[++pixelOffset] = output[line++] * multiplier;
  189. outputBuffer[++pixelOffset] = output[line++] * multiplier;
  190. if (fourthChannel) outputBuffer[++pixelOffset] = output[line++] * ratioWeightDivisor;
  191. }
  192. outputOffset += channelsNum;
  193. } while (outputOffset < this.targetWidthMultipliedByChannels);
  194. return outputBuffer;
  195. };
  196. Resize.prototype._resizeHeightRGBChannels = function (buffer, fourthChannel) {
  197. var ratioWeight = this.ratioWeightHeightPass;
  198. var ratioWeightDivisor = 1 / ratioWeight;
  199. var output = this.outputHeightWorkBench;
  200. var outputBuffer = this.heightBuffer;
  201. var trustworthyColorsCount = this.outputHeightWorkBenchOpaquePixelsCount;
  202. var weight = 0;
  203. var amountToNext = 0;
  204. var actualPosition = 0;
  205. var currentPosition = 0;
  206. var pixelOffset = 0;
  207. var outputOffset = 0;
  208. var caret = 0;
  209. var multiplier = 1;
  210. var r = 0;
  211. var g = 0;
  212. var b = 0;
  213. var a = 0;
  214. do {
  215. for (pixelOffset = 0; pixelOffset < this.targetWidthMultipliedByChannels;) {
  216. output[pixelOffset++] = 0;
  217. output[pixelOffset++] = 0;
  218. output[pixelOffset++] = 0;
  219. if (fourthChannel) {
  220. output[pixelOffset++] = 0;
  221. trustworthyColorsCount[pixelOffset / 4 - 1] = 0;
  222. }
  223. }
  224. weight = ratioWeight;
  225. do {
  226. amountToNext = 1 + actualPosition - currentPosition;
  227. multiplier = Math.min(weight, amountToNext);
  228. caret = actualPosition;
  229. for (pixelOffset = 0; pixelOffset < this.targetWidthMultipliedByChannels;) {
  230. r = buffer[caret++];
  231. g = buffer[caret++];
  232. b = buffer[caret++];
  233. a = fourthChannel ? buffer[caret++] : 255; // Ignore RGB values if pixel is completely transparent
  234. output[pixelOffset++] += (a ? r : 0) * multiplier;
  235. output[pixelOffset++] += (a ? g : 0) * multiplier;
  236. output[pixelOffset++] += (a ? b : 0) * multiplier;
  237. if (fourthChannel) {
  238. output[pixelOffset++] += a * multiplier;
  239. trustworthyColorsCount[pixelOffset / 4 - 1] += a ? multiplier : 0;
  240. }
  241. }
  242. if (weight >= amountToNext) {
  243. actualPosition = caret;
  244. currentPosition = actualPosition;
  245. weight -= amountToNext;
  246. } else {
  247. currentPosition += weight;
  248. break;
  249. }
  250. } while (weight > 0 && actualPosition < this.widthPassResultSize);
  251. for (pixelOffset = 0; pixelOffset < this.targetWidthMultipliedByChannels;) {
  252. weight = fourthChannel ? trustworthyColorsCount[pixelOffset / 4] : 1;
  253. multiplier = fourthChannel ? weight ? 1 / weight : 0 : ratioWeightDivisor;
  254. outputBuffer[outputOffset++] = Math.round(output[pixelOffset++] * multiplier);
  255. outputBuffer[outputOffset++] = Math.round(output[pixelOffset++] * multiplier);
  256. outputBuffer[outputOffset++] = Math.round(output[pixelOffset++] * multiplier);
  257. if (fourthChannel) {
  258. outputBuffer[outputOffset++] = Math.round(output[pixelOffset++] * ratioWeightDivisor);
  259. }
  260. }
  261. } while (outputOffset < this.finalResultSize);
  262. return outputBuffer;
  263. };
  264. Resize.prototype.resizeWidthInterpolatedRGB = function (buffer) {
  265. return this._resizeWidthInterpolatedRGBChannels(buffer, false);
  266. };
  267. Resize.prototype.resizeWidthInterpolatedRGBA = function (buffer) {
  268. return this._resizeWidthInterpolatedRGBChannels(buffer, true);
  269. };
  270. Resize.prototype.resizeWidthRGB = function (buffer) {
  271. return this._resizeWidthRGBChannels(buffer, false);
  272. };
  273. Resize.prototype.resizeWidthRGBA = function (buffer) {
  274. return this._resizeWidthRGBChannels(buffer, true);
  275. };
  276. Resize.prototype.resizeHeightInterpolated = function (buffer) {
  277. var ratioWeight = this.ratioWeightHeightPass;
  278. var outputBuffer = this.heightBuffer;
  279. var weight = 0;
  280. var finalOffset = 0;
  281. var pixelOffset = 0;
  282. var pixelOffsetAccumulated = 0;
  283. var pixelOffsetAccumulated2 = 0;
  284. var firstWeight = 0;
  285. var secondWeight = 0;
  286. var interpolationHeightSourceReadStop; // Handle for only one interpolation input being valid for start calculation:
  287. for (; weight < 1 / 3; weight += ratioWeight) {
  288. for (pixelOffset = 0; pixelOffset < this.targetWidthMultipliedByChannels;) {
  289. outputBuffer[finalOffset++] = Math.round(buffer[pixelOffset++]);
  290. }
  291. } // Adjust for overshoot of the last pass's counter:
  292. weight -= 1 / 3;
  293. for (interpolationHeightSourceReadStop = this.heightOriginal - 1; weight < interpolationHeightSourceReadStop; weight += ratioWeight) {
  294. // Calculate weightings:
  295. secondWeight = weight % 1;
  296. firstWeight = 1 - secondWeight; // Interpolate:
  297. pixelOffsetAccumulated = Math.floor(weight) * this.targetWidthMultipliedByChannels;
  298. pixelOffsetAccumulated2 = pixelOffsetAccumulated + this.targetWidthMultipliedByChannels;
  299. for (pixelOffset = 0; pixelOffset < this.targetWidthMultipliedByChannels; ++pixelOffset) {
  300. outputBuffer[finalOffset++] = Math.round(buffer[pixelOffsetAccumulated++] * firstWeight + buffer[pixelOffsetAccumulated2++] * secondWeight);
  301. }
  302. } // Handle for only one interpolation input being valid for end calculation:
  303. while (finalOffset < this.finalResultSize) {
  304. for (pixelOffset = 0, pixelOffsetAccumulated = interpolationHeightSourceReadStop * this.targetWidthMultipliedByChannels; pixelOffset < this.targetWidthMultipliedByChannels; ++pixelOffset) {
  305. outputBuffer[finalOffset++] = Math.round(buffer[pixelOffsetAccumulated++]);
  306. }
  307. }
  308. return outputBuffer;
  309. };
  310. Resize.prototype.resizeHeightRGB = function (buffer) {
  311. return this._resizeHeightRGBChannels(buffer, false);
  312. };
  313. Resize.prototype.resizeHeightRGBA = function (buffer) {
  314. return this._resizeHeightRGBChannels(buffer, true);
  315. };
  316. Resize.prototype.resize = function (buffer) {
  317. this.resizeCallback(this.resizeHeight(this.resizeWidth(buffer)));
  318. };
  319. Resize.prototype.bypassResizer = function (buffer) {
  320. // Just return the buffer passed:
  321. return buffer;
  322. };
  323. Resize.prototype.initializeFirstPassBuffers = function (BILINEARAlgo) {
  324. // Initialize the internal width pass buffers:
  325. this.widthBuffer = this.generateFloatBuffer(this.widthPassResultSize);
  326. if (!BILINEARAlgo) {
  327. this.outputWidthWorkBench = this.generateFloatBuffer(this.originalHeightMultipliedByChannels);
  328. if (this.colorChannels > 3) {
  329. this.outputWidthWorkBenchOpaquePixelsCount = this.generateFloat64Buffer(this.heightOriginal);
  330. }
  331. }
  332. };
  333. Resize.prototype.initializeSecondPassBuffers = function (BILINEARAlgo) {
  334. // Initialize the internal height pass buffers:
  335. this.heightBuffer = this.generateUint8Buffer(this.finalResultSize);
  336. if (!BILINEARAlgo) {
  337. this.outputHeightWorkBench = this.generateFloatBuffer(this.targetWidthMultipliedByChannels);
  338. if (this.colorChannels > 3) {
  339. this.outputHeightWorkBenchOpaquePixelsCount = this.generateFloat64Buffer(this.targetWidth);
  340. }
  341. }
  342. };
  343. Resize.prototype.generateFloatBuffer = function (bufferLength) {
  344. // Generate a float32 typed array buffer:
  345. try {
  346. return new Float32Array(bufferLength);
  347. } catch (error) {
  348. return [];
  349. }
  350. };
  351. Resize.prototype.generateFloat64Buffer = function (bufferLength) {
  352. // Generate a float64 typed array buffer:
  353. try {
  354. return new Float64Array(bufferLength);
  355. } catch (error) {
  356. return [];
  357. }
  358. };
  359. Resize.prototype.generateUint8Buffer = function (bufferLength) {
  360. // Generate a uint8 typed array buffer:
  361. try {
  362. return new Uint8Array(bufferLength);
  363. } catch (error) {
  364. return [];
  365. }
  366. };
  367. module.exports = Resize;
  368. //# sourceMappingURL=resize.js.map