初始化fy

This commit is contained in:
yziiy
2025-08-11 11:51:38 +08:00
parent 98ce20e897
commit 7e21160e13
19770 changed files with 3108698 additions and 0 deletions

View File

@@ -0,0 +1,11 @@
# v0.9.3 (Tue Nov 26 2019)
#### 🐛 Bug Fix
- `@jimp/cli`, `@jimp/core`, `@jimp/custom`, `jimp`, `@jimp/plugin-blit`, `@jimp/plugin-blur`, `@jimp/plugin-circle`, `@jimp/plugin-color`, `@jimp/plugin-contain`, `@jimp/plugin-cover`, `@jimp/plugin-crop`, `@jimp/plugin-displace`, `@jimp/plugin-dither`, `@jimp/plugin-fisheye`, `@jimp/plugin-flip`, `@jimp/plugin-gaussian`, `@jimp/plugin-invert`, `@jimp/plugin-mask`, `@jimp/plugin-normalize`, `@jimp/plugin-print`, `@jimp/plugin-resize`, `@jimp/plugin-rotate`, `@jimp/plugin-scale`, `@jimp/plugin-shadow`, `@jimp/plugin-threshold`, `@jimp/plugins`, `@jimp/test-utils`, `@jimp/bmp`, `@jimp/gif`, `@jimp/jpeg`, `@jimp/png`, `@jimp/tiff`, `@jimp/types`, `@jimp/utils`
- Fix regeneratorRuntime errors [#815](https://github.com/oliver-moran/jimp/pull/815) ([@crutchcorn](https://github.com/crutchcorn) [@hipstersmoothie](https://github.com/hipstersmoothie))
#### Authors: 2
- Andrew Lisowski ([@hipstersmoothie](https://github.com/hipstersmoothie))
- Corbin Crutchley ([@crutchcorn](https://github.com/crutchcorn))

View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2018 Oliver Moran
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,61 @@
<div align="center">
<img width="200" height="200"
src="https://s3.amazonaws.com/pix.iemoji.com/images/emoji/apple/ios-11/256/crayon.png">
<h1>@jimp/plugin-resize</h1>
<p>Resize an image.</p>
</div>
Resizes the image to a set width and height using a 2-pass bilinear algorithm/
## Usage
- @param {number} w the width to resize the image to (or Jimp.AUTO)
- @param {number} h the height to resize the image to (or Jimp.AUTO)
- @param {string} mode (optional) a scaling method (e.g. Jimp.RESIZE_BEZIER)
- @param {function(Error, Jimp)} cb (optional) a callback for when complete
```js
import jimp from 'jimp';
async function main() {
// Read the image.
const image = await jimp.read('test/image.png');
// Resize the image to width 150 and auto height.
await image.resize(150, jimp.AUTO);
// Save and overwrite the image
await image.writeAsync('test/image.png');
}
main();
```
## Auto
`Jimp.AUTO` can be passes to either the height or width and jimp will scale the image accordingly. `Jimp.AUTO` cannot be both height and width.
```js
// resize the height to 250 and scale the width accordingly
image.resize(Jimp.AUTO, 250);
// resize the width to 250 and scale the height accordingly
image.resize(250, Jimp.AUTO);
```
### Resize modes
The default resizing algorithm uses a bilinear method.
Optionally, the following constants can be passed to choose a particular resizing algorithm:
```js
Jimp.RESIZE_NEAREST_NEIGHBOR;
Jimp.RESIZE_BILINEAR;
Jimp.RESIZE_BICUBIC;
Jimp.RESIZE_HERMITE;
Jimp.RESIZE_BEZIER;
```
```js
image.resize(250, 250, Jimp.RESIZE_BEZIER);
```

View File

@@ -0,0 +1,96 @@
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports["default"] = void 0;
var _utils = require("@jimp/utils");
var _resize = _interopRequireDefault(require("./modules/resize"));
var _resize2 = _interopRequireDefault(require("./modules/resize2"));
var _default = function _default() {
return {
constants: {
RESIZE_NEAREST_NEIGHBOR: 'nearestNeighbor',
RESIZE_BILINEAR: 'bilinearInterpolation',
RESIZE_BICUBIC: 'bicubicInterpolation',
RESIZE_HERMITE: 'hermiteInterpolation',
RESIZE_BEZIER: 'bezierInterpolation'
},
"class": {
/**
* Resizes the image to a set width and height using a 2-pass bilinear algorithm
* @param {number} w the width to resize the image to (or Jimp.AUTO)
* @param {number} h the height to resize the image to (or Jimp.AUTO)
* @param {string} mode (optional) a scaling method (e.g. Jimp.RESIZE_BEZIER)
* @param {function(Error, Jimp)} cb (optional) a callback for when complete
* @returns {Jimp} this for chaining of methods
*/
resize: function resize(w, h, mode, cb) {
if (typeof w !== 'number' || typeof h !== 'number') {
return _utils.throwError.call(this, 'w and h must be numbers', cb);
}
if (typeof mode === 'function' && typeof cb === 'undefined') {
cb = mode;
mode = null;
}
if (w === this.constructor.AUTO && h === this.constructor.AUTO) {
return _utils.throwError.call(this, 'w and h cannot both be set to auto', cb);
}
if (w === this.constructor.AUTO) {
w = this.bitmap.width * (h / this.bitmap.height);
}
if (h === this.constructor.AUTO) {
h = this.bitmap.height * (w / this.bitmap.width);
}
if (w < 0 || h < 0) {
return _utils.throwError.call(this, 'w and h must be positive numbers', cb);
} // round inputs
w = Math.round(w);
h = Math.round(h);
if (typeof _resize2["default"][mode] === 'function') {
var dst = {
data: Buffer.alloc(w * h * 4),
width: w,
height: h
};
_resize2["default"][mode](this.bitmap, dst);
this.bitmap = dst;
} else {
var image = this;
var resize = new _resize["default"](this.bitmap.width, this.bitmap.height, w, h, true, true, function (buffer) {
image.bitmap.data = Buffer.from(buffer);
image.bitmap.width = w;
image.bitmap.height = h;
});
resize.resize(this.bitmap.data);
}
if ((0, _utils.isNodePattern)(cb)) {
cb.call(this, null, this);
}
return this;
}
}
};
};
exports["default"] = _default;
module.exports = exports.default;
//# sourceMappingURL=index.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,464 @@
"use strict";
require("core-js/modules/es.array.iterator");
require("core-js/modules/es.array-buffer.slice");
require("core-js/modules/es.object.to-string");
require("core-js/modules/es.typed-array.float32-array");
require("core-js/modules/es.typed-array.float64-array");
require("core-js/modules/es.typed-array.uint8-array");
require("core-js/modules/es.typed-array.copy-within");
require("core-js/modules/es.typed-array.every");
require("core-js/modules/es.typed-array.fill");
require("core-js/modules/es.typed-array.filter");
require("core-js/modules/es.typed-array.find");
require("core-js/modules/es.typed-array.find-index");
require("core-js/modules/es.typed-array.for-each");
require("core-js/modules/es.typed-array.includes");
require("core-js/modules/es.typed-array.index-of");
require("core-js/modules/es.typed-array.iterator");
require("core-js/modules/es.typed-array.join");
require("core-js/modules/es.typed-array.last-index-of");
require("core-js/modules/es.typed-array.map");
require("core-js/modules/es.typed-array.reduce");
require("core-js/modules/es.typed-array.reduce-right");
require("core-js/modules/es.typed-array.reverse");
require("core-js/modules/es.typed-array.set");
require("core-js/modules/es.typed-array.slice");
require("core-js/modules/es.typed-array.some");
require("core-js/modules/es.typed-array.sort");
require("core-js/modules/es.typed-array.subarray");
require("core-js/modules/es.typed-array.to-locale-string");
require("core-js/modules/es.typed-array.to-string");
// JavaScript Image Resizer (c) 2012 - Grant Galitz
// Released to public domain 29 July 2013: https://github.com/grantgalitz/JS-Image-Resizer/issues/4
function Resize(widthOriginal, heightOriginal, targetWidth, targetHeight, blendAlpha, interpolationPass, resizeCallback) {
this.widthOriginal = Math.abs(Math.floor(widthOriginal) || 0);
this.heightOriginal = Math.abs(Math.floor(heightOriginal) || 0);
this.targetWidth = Math.abs(Math.floor(targetWidth) || 0);
this.targetHeight = Math.abs(Math.floor(targetHeight) || 0);
this.colorChannels = blendAlpha ? 4 : 3;
this.interpolationPass = Boolean(interpolationPass);
this.resizeCallback = typeof resizeCallback === 'function' ? resizeCallback : function () {};
this.targetWidthMultipliedByChannels = this.targetWidth * this.colorChannels;
this.originalWidthMultipliedByChannels = this.widthOriginal * this.colorChannels;
this.originalHeightMultipliedByChannels = this.heightOriginal * this.colorChannels;
this.widthPassResultSize = this.targetWidthMultipliedByChannels * this.heightOriginal;
this.finalResultSize = this.targetWidthMultipliedByChannels * this.targetHeight;
this.initialize();
}
Resize.prototype.initialize = function () {
// Perform some checks:
if (this.widthOriginal > 0 && this.heightOriginal > 0 && this.targetWidth > 0 && this.targetHeight > 0) {
this.configurePasses();
} else {
throw new Error('Invalid settings specified for the resizer.');
}
};
Resize.prototype.configurePasses = function () {
if (this.widthOriginal === this.targetWidth) {
// Bypass the width resizer pass:
this.resizeWidth = this.bypassResizer;
} else {
// Setup the width resizer pass:
this.ratioWeightWidthPass = this.widthOriginal / this.targetWidth;
if (this.ratioWeightWidthPass < 1 && this.interpolationPass) {
this.initializeFirstPassBuffers(true);
this.resizeWidth = this.colorChannels === 4 ? this.resizeWidthInterpolatedRGBA : this.resizeWidthInterpolatedRGB;
} else {
this.initializeFirstPassBuffers(false);
this.resizeWidth = this.colorChannels === 4 ? this.resizeWidthRGBA : this.resizeWidthRGB;
}
}
if (this.heightOriginal === this.targetHeight) {
// Bypass the height resizer pass:
this.resizeHeight = this.bypassResizer;
} else {
// Setup the height resizer pass:
this.ratioWeightHeightPass = this.heightOriginal / this.targetHeight;
if (this.ratioWeightHeightPass < 1 && this.interpolationPass) {
this.initializeSecondPassBuffers(true);
this.resizeHeight = this.resizeHeightInterpolated;
} else {
this.initializeSecondPassBuffers(false);
this.resizeHeight = this.colorChannels === 4 ? this.resizeHeightRGBA : this.resizeHeightRGB;
}
}
};
Resize.prototype._resizeWidthInterpolatedRGBChannels = function (buffer, fourthChannel) {
var channelsNum = fourthChannel ? 4 : 3;
var ratioWeight = this.ratioWeightWidthPass;
var outputBuffer = this.widthBuffer;
var weight = 0;
var finalOffset = 0;
var pixelOffset = 0;
var firstWeight = 0;
var secondWeight = 0;
var targetPosition; // Handle for only one interpolation input being valid for start calculation:
for (targetPosition = 0; weight < 1 / 3; targetPosition += channelsNum, weight += ratioWeight) {
for (finalOffset = targetPosition, pixelOffset = 0; finalOffset < this.widthPassResultSize; pixelOffset += this.originalWidthMultipliedByChannels, finalOffset += this.targetWidthMultipliedByChannels) {
outputBuffer[finalOffset] = buffer[pixelOffset];
outputBuffer[finalOffset + 1] = buffer[pixelOffset + 1];
outputBuffer[finalOffset + 2] = buffer[pixelOffset + 2];
if (fourthChannel) outputBuffer[finalOffset + 3] = buffer[pixelOffset + 3];
}
} // Adjust for overshoot of the last pass's counter:
weight -= 1 / 3;
var interpolationWidthSourceReadStop;
for (interpolationWidthSourceReadStop = this.widthOriginal - 1; weight < interpolationWidthSourceReadStop; targetPosition += channelsNum, weight += ratioWeight) {
// Calculate weightings:
secondWeight = weight % 1;
firstWeight = 1 - secondWeight; // Interpolate:
for (finalOffset = targetPosition, pixelOffset = Math.floor(weight) * channelsNum; finalOffset < this.widthPassResultSize; pixelOffset += this.originalWidthMultipliedByChannels, finalOffset += this.targetWidthMultipliedByChannels) {
outputBuffer[finalOffset + 0] = buffer[pixelOffset + 0] * firstWeight + buffer[pixelOffset + channelsNum + 0] * secondWeight;
outputBuffer[finalOffset + 1] = buffer[pixelOffset + 1] * firstWeight + buffer[pixelOffset + channelsNum + 1] * secondWeight;
outputBuffer[finalOffset + 2] = buffer[pixelOffset + 2] * firstWeight + buffer[pixelOffset + channelsNum + 2] * secondWeight;
if (fourthChannel) outputBuffer[finalOffset + 3] = buffer[pixelOffset + 3] * firstWeight + buffer[pixelOffset + channelsNum + 3] * secondWeight;
}
} // Handle for only one interpolation input being valid for end calculation:
for (interpolationWidthSourceReadStop = this.originalWidthMultipliedByChannels - channelsNum; targetPosition < this.targetWidthMultipliedByChannels; targetPosition += channelsNum) {
for (finalOffset = targetPosition, pixelOffset = interpolationWidthSourceReadStop; finalOffset < this.widthPassResultSize; pixelOffset += this.originalWidthMultipliedByChannels, finalOffset += this.targetWidthMultipliedByChannels) {
outputBuffer[finalOffset] = buffer[pixelOffset];
outputBuffer[finalOffset + 1] = buffer[pixelOffset + 1];
outputBuffer[finalOffset + 2] = buffer[pixelOffset + 2];
if (fourthChannel) outputBuffer[finalOffset + 3] = buffer[pixelOffset + 3];
}
}
return outputBuffer;
};
Resize.prototype._resizeWidthRGBChannels = function (buffer, fourthChannel) {
var channelsNum = fourthChannel ? 4 : 3;
var ratioWeight = this.ratioWeightWidthPass;
var ratioWeightDivisor = 1 / ratioWeight;
var nextLineOffsetOriginalWidth = this.originalWidthMultipliedByChannels - channelsNum + 1;
var nextLineOffsetTargetWidth = this.targetWidthMultipliedByChannels - channelsNum + 1;
var output = this.outputWidthWorkBench;
var outputBuffer = this.widthBuffer;
var trustworthyColorsCount = this.outputWidthWorkBenchOpaquePixelsCount;
var weight = 0;
var amountToNext = 0;
var actualPosition = 0;
var currentPosition = 0;
var line = 0;
var pixelOffset = 0;
var outputOffset = 0;
var multiplier = 1;
var r = 0;
var g = 0;
var b = 0;
var a = 0;
do {
for (line = 0; line < this.originalHeightMultipliedByChannels;) {
output[line++] = 0;
output[line++] = 0;
output[line++] = 0;
if (fourthChannel) {
output[line++] = 0;
trustworthyColorsCount[line / channelsNum - 1] = 0;
}
}
weight = ratioWeight;
do {
amountToNext = 1 + actualPosition - currentPosition;
multiplier = Math.min(weight, amountToNext);
for (line = 0, pixelOffset = actualPosition; line < this.originalHeightMultipliedByChannels; pixelOffset += nextLineOffsetOriginalWidth) {
r = buffer[pixelOffset];
g = buffer[++pixelOffset];
b = buffer[++pixelOffset];
a = fourthChannel ? buffer[++pixelOffset] : 255; // Ignore RGB values if pixel is completely transparent
output[line++] += (a ? r : 0) * multiplier;
output[line++] += (a ? g : 0) * multiplier;
output[line++] += (a ? b : 0) * multiplier;
if (fourthChannel) {
output[line++] += a * multiplier;
trustworthyColorsCount[line / channelsNum - 1] += a ? multiplier : 0;
}
}
if (weight >= amountToNext) {
actualPosition += channelsNum;
currentPosition = actualPosition;
weight -= amountToNext;
} else {
currentPosition += weight;
break;
}
} while (weight > 0 && actualPosition < this.originalWidthMultipliedByChannels);
for (line = 0, pixelOffset = outputOffset; line < this.originalHeightMultipliedByChannels; pixelOffset += nextLineOffsetTargetWidth) {
weight = fourthChannel ? trustworthyColorsCount[line / channelsNum] : 1;
multiplier = fourthChannel ? weight ? 1 / weight : 0 : ratioWeightDivisor;
outputBuffer[pixelOffset] = output[line++] * multiplier;
outputBuffer[++pixelOffset] = output[line++] * multiplier;
outputBuffer[++pixelOffset] = output[line++] * multiplier;
if (fourthChannel) outputBuffer[++pixelOffset] = output[line++] * ratioWeightDivisor;
}
outputOffset += channelsNum;
} while (outputOffset < this.targetWidthMultipliedByChannels);
return outputBuffer;
};
Resize.prototype._resizeHeightRGBChannels = function (buffer, fourthChannel) {
var ratioWeight = this.ratioWeightHeightPass;
var ratioWeightDivisor = 1 / ratioWeight;
var output = this.outputHeightWorkBench;
var outputBuffer = this.heightBuffer;
var trustworthyColorsCount = this.outputHeightWorkBenchOpaquePixelsCount;
var weight = 0;
var amountToNext = 0;
var actualPosition = 0;
var currentPosition = 0;
var pixelOffset = 0;
var outputOffset = 0;
var caret = 0;
var multiplier = 1;
var r = 0;
var g = 0;
var b = 0;
var a = 0;
do {
for (pixelOffset = 0; pixelOffset < this.targetWidthMultipliedByChannels;) {
output[pixelOffset++] = 0;
output[pixelOffset++] = 0;
output[pixelOffset++] = 0;
if (fourthChannel) {
output[pixelOffset++] = 0;
trustworthyColorsCount[pixelOffset / 4 - 1] = 0;
}
}
weight = ratioWeight;
do {
amountToNext = 1 + actualPosition - currentPosition;
multiplier = Math.min(weight, amountToNext);
caret = actualPosition;
for (pixelOffset = 0; pixelOffset < this.targetWidthMultipliedByChannels;) {
r = buffer[caret++];
g = buffer[caret++];
b = buffer[caret++];
a = fourthChannel ? buffer[caret++] : 255; // Ignore RGB values if pixel is completely transparent
output[pixelOffset++] += (a ? r : 0) * multiplier;
output[pixelOffset++] += (a ? g : 0) * multiplier;
output[pixelOffset++] += (a ? b : 0) * multiplier;
if (fourthChannel) {
output[pixelOffset++] += a * multiplier;
trustworthyColorsCount[pixelOffset / 4 - 1] += a ? multiplier : 0;
}
}
if (weight >= amountToNext) {
actualPosition = caret;
currentPosition = actualPosition;
weight -= amountToNext;
} else {
currentPosition += weight;
break;
}
} while (weight > 0 && actualPosition < this.widthPassResultSize);
for (pixelOffset = 0; pixelOffset < this.targetWidthMultipliedByChannels;) {
weight = fourthChannel ? trustworthyColorsCount[pixelOffset / 4] : 1;
multiplier = fourthChannel ? weight ? 1 / weight : 0 : ratioWeightDivisor;
outputBuffer[outputOffset++] = Math.round(output[pixelOffset++] * multiplier);
outputBuffer[outputOffset++] = Math.round(output[pixelOffset++] * multiplier);
outputBuffer[outputOffset++] = Math.round(output[pixelOffset++] * multiplier);
if (fourthChannel) {
outputBuffer[outputOffset++] = Math.round(output[pixelOffset++] * ratioWeightDivisor);
}
}
} while (outputOffset < this.finalResultSize);
return outputBuffer;
};
Resize.prototype.resizeWidthInterpolatedRGB = function (buffer) {
return this._resizeWidthInterpolatedRGBChannels(buffer, false);
};
Resize.prototype.resizeWidthInterpolatedRGBA = function (buffer) {
return this._resizeWidthInterpolatedRGBChannels(buffer, true);
};
Resize.prototype.resizeWidthRGB = function (buffer) {
return this._resizeWidthRGBChannels(buffer, false);
};
Resize.prototype.resizeWidthRGBA = function (buffer) {
return this._resizeWidthRGBChannels(buffer, true);
};
Resize.prototype.resizeHeightInterpolated = function (buffer) {
var ratioWeight = this.ratioWeightHeightPass;
var outputBuffer = this.heightBuffer;
var weight = 0;
var finalOffset = 0;
var pixelOffset = 0;
var pixelOffsetAccumulated = 0;
var pixelOffsetAccumulated2 = 0;
var firstWeight = 0;
var secondWeight = 0;
var interpolationHeightSourceReadStop; // Handle for only one interpolation input being valid for start calculation:
for (; weight < 1 / 3; weight += ratioWeight) {
for (pixelOffset = 0; pixelOffset < this.targetWidthMultipliedByChannels;) {
outputBuffer[finalOffset++] = Math.round(buffer[pixelOffset++]);
}
} // Adjust for overshoot of the last pass's counter:
weight -= 1 / 3;
for (interpolationHeightSourceReadStop = this.heightOriginal - 1; weight < interpolationHeightSourceReadStop; weight += ratioWeight) {
// Calculate weightings:
secondWeight = weight % 1;
firstWeight = 1 - secondWeight; // Interpolate:
pixelOffsetAccumulated = Math.floor(weight) * this.targetWidthMultipliedByChannels;
pixelOffsetAccumulated2 = pixelOffsetAccumulated + this.targetWidthMultipliedByChannels;
for (pixelOffset = 0; pixelOffset < this.targetWidthMultipliedByChannels; ++pixelOffset) {
outputBuffer[finalOffset++] = Math.round(buffer[pixelOffsetAccumulated++] * firstWeight + buffer[pixelOffsetAccumulated2++] * secondWeight);
}
} // Handle for only one interpolation input being valid for end calculation:
while (finalOffset < this.finalResultSize) {
for (pixelOffset = 0, pixelOffsetAccumulated = interpolationHeightSourceReadStop * this.targetWidthMultipliedByChannels; pixelOffset < this.targetWidthMultipliedByChannels; ++pixelOffset) {
outputBuffer[finalOffset++] = Math.round(buffer[pixelOffsetAccumulated++]);
}
}
return outputBuffer;
};
Resize.prototype.resizeHeightRGB = function (buffer) {
return this._resizeHeightRGBChannels(buffer, false);
};
Resize.prototype.resizeHeightRGBA = function (buffer) {
return this._resizeHeightRGBChannels(buffer, true);
};
Resize.prototype.resize = function (buffer) {
this.resizeCallback(this.resizeHeight(this.resizeWidth(buffer)));
};
Resize.prototype.bypassResizer = function (buffer) {
// Just return the buffer passed:
return buffer;
};
Resize.prototype.initializeFirstPassBuffers = function (BILINEARAlgo) {
// Initialize the internal width pass buffers:
this.widthBuffer = this.generateFloatBuffer(this.widthPassResultSize);
if (!BILINEARAlgo) {
this.outputWidthWorkBench = this.generateFloatBuffer(this.originalHeightMultipliedByChannels);
if (this.colorChannels > 3) {
this.outputWidthWorkBenchOpaquePixelsCount = this.generateFloat64Buffer(this.heightOriginal);
}
}
};
Resize.prototype.initializeSecondPassBuffers = function (BILINEARAlgo) {
// Initialize the internal height pass buffers:
this.heightBuffer = this.generateUint8Buffer(this.finalResultSize);
if (!BILINEARAlgo) {
this.outputHeightWorkBench = this.generateFloatBuffer(this.targetWidthMultipliedByChannels);
if (this.colorChannels > 3) {
this.outputHeightWorkBenchOpaquePixelsCount = this.generateFloat64Buffer(this.targetWidth);
}
}
};
Resize.prototype.generateFloatBuffer = function (bufferLength) {
// Generate a float32 typed array buffer:
try {
return new Float32Array(bufferLength);
} catch (error) {
return [];
}
};
Resize.prototype.generateFloat64Buffer = function (bufferLength) {
// Generate a float64 typed array buffer:
try {
return new Float64Array(bufferLength);
} catch (error) {
return [];
}
};
Resize.prototype.generateUint8Buffer = function (bufferLength) {
// Generate a uint8 typed array buffer:
try {
return new Uint8Array(bufferLength);
} catch (error) {
return [];
}
};
module.exports = Resize;
//# sourceMappingURL=resize.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,266 @@
"use strict";
/**
* Copyright (c) 2015 Guyon Roche
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:</p>
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
module.exports = {
nearestNeighbor: function nearestNeighbor(src, dst) {
var wSrc = src.width;
var hSrc = src.height;
var wDst = dst.width;
var hDst = dst.height;
var bufSrc = src.data;
var bufDst = dst.data;
for (var i = 0; i < hDst; i++) {
for (var j = 0; j < wDst; j++) {
var posDst = (i * wDst + j) * 4;
var iSrc = Math.floor(i * hSrc / hDst);
var jSrc = Math.floor(j * wSrc / wDst);
var posSrc = (iSrc * wSrc + jSrc) * 4;
bufDst[posDst++] = bufSrc[posSrc++];
bufDst[posDst++] = bufSrc[posSrc++];
bufDst[posDst++] = bufSrc[posSrc++];
bufDst[posDst++] = bufSrc[posSrc++];
}
}
},
bilinearInterpolation: function bilinearInterpolation(src, dst) {
var wSrc = src.width;
var hSrc = src.height;
var wDst = dst.width;
var hDst = dst.height;
var bufSrc = src.data;
var bufDst = dst.data;
var interpolate = function interpolate(k, kMin, vMin, kMax, vMax) {
// special case - k is integer
if (kMin === kMax) {
return vMin;
}
return Math.round((k - kMin) * vMax + (kMax - k) * vMin);
};
var assign = function assign(pos, offset, x, xMin, xMax, y, yMin, yMax) {
var posMin = (yMin * wSrc + xMin) * 4 + offset;
var posMax = (yMin * wSrc + xMax) * 4 + offset;
var vMin = interpolate(x, xMin, bufSrc[posMin], xMax, bufSrc[posMax]); // special case, y is integer
if (yMax === yMin) {
bufDst[pos + offset] = vMin;
} else {
posMin = (yMax * wSrc + xMin) * 4 + offset;
posMax = (yMax * wSrc + xMax) * 4 + offset;
var vMax = interpolate(x, xMin, bufSrc[posMin], xMax, bufSrc[posMax]);
bufDst[pos + offset] = interpolate(y, yMin, vMin, yMax, vMax);
}
};
for (var i = 0; i < hDst; i++) {
for (var j = 0; j < wDst; j++) {
var posDst = (i * wDst + j) * 4; // x & y in src coordinates
var x = j * wSrc / wDst;
var xMin = Math.floor(x);
var xMax = Math.min(Math.ceil(x), wSrc - 1);
var y = i * hSrc / hDst;
var yMin = Math.floor(y);
var yMax = Math.min(Math.ceil(y), hSrc - 1);
assign(posDst, 0, x, xMin, xMax, y, yMin, yMax);
assign(posDst, 1, x, xMin, xMax, y, yMin, yMax);
assign(posDst, 2, x, xMin, xMax, y, yMin, yMax);
assign(posDst, 3, x, xMin, xMax, y, yMin, yMax);
}
}
},
_interpolate2D: function _interpolate2D(src, dst, options, interpolate) {
var bufSrc = src.data;
var bufDst = dst.data;
var wSrc = src.width;
var hSrc = src.height;
var wDst = dst.width;
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
var wM = Math.max(1, Math.floor(wSrc / wDst));
var wDst2 = wDst * wM;
var hM = Math.max(1, Math.floor(hSrc / hDst));
var hDst2 = hDst * hM; // ===========================================================
// Pass 1 - interpolate rows
// buf1 has width of dst2 and height of src
var buf1 = Buffer.alloc(wDst2 * hSrc * 4);
for (var i = 0; i < hSrc; i++) {
for (var j = 0; j < wDst2; j++) {
// i in src coords, j in dst coords
// calculate x in src coords
// this interpolation requires 4 sample points and the two inner ones must be real
// the outer points can be fudged for the edges.
// therefore (wSrc-1)/wDst2
var x = j * (wSrc - 1) / wDst2;
var xPos = Math.floor(x);
var t = x - xPos;
var srcPos = (i * wSrc + xPos) * 4;
var buf1Pos = (i * wDst2 + j) * 4;
for (var k = 0; k < 4; k++) {
var kPos = srcPos + k;
var x0 = xPos > 0 ? bufSrc[kPos - 4] : 2 * bufSrc[kPos] - bufSrc[kPos + 4];
var x1 = bufSrc[kPos];
var x2 = bufSrc[kPos + 4];
var x3 = xPos < wSrc - 2 ? bufSrc[kPos + 8] : 2 * bufSrc[kPos + 4] - bufSrc[kPos];
buf1[buf1Pos + k] = interpolate(x0, x1, x2, x3, t);
}
}
} // this._writeFile(wDst2, hSrc, buf1, "out/buf1.jpg");
// ===========================================================
// Pass 2 - interpolate columns
// buf2 has width and height of dst2
var buf2 = Buffer.alloc(wDst2 * hDst2 * 4);
for (var _i = 0; _i < hDst2; _i++) {
for (var _j = 0; _j < wDst2; _j++) {
// i&j in dst2 coords
// calculate y in buf1 coords
// this interpolation requires 4 sample points and the two inner ones must be real
// the outer points can be fudged for the edges.
// therefore (hSrc-1)/hDst2
var y = _i * (hSrc - 1) / hDst2;
var yPos = Math.floor(y);
var _t = y - yPos;
var _buf1Pos = (yPos * wDst2 + _j) * 4;
var buf2Pos = (_i * wDst2 + _j) * 4;
for (var _k = 0; _k < 4; _k++) {
var _kPos = _buf1Pos + _k;
var y0 = yPos > 0 ? buf1[_kPos - wDst2 * 4] : 2 * buf1[_kPos] - buf1[_kPos + wDst2 * 4];
var y1 = buf1[_kPos];
var y2 = buf1[_kPos + wDst2 * 4];
var y3 = yPos < hSrc - 2 ? buf1[_kPos + wDst2 * 8] : 2 * buf1[_kPos + wDst2 * 4] - buf1[_kPos];
buf2[buf2Pos + _k] = interpolate(y0, y1, y2, y3, _t);
}
}
} // this._writeFile(wDst2, hDst2, buf2, "out/buf2.jpg");
// ===========================================================
// Pass 3 - scale to dst
var m = wM * hM;
if (m > 1) {
for (var _i2 = 0; _i2 < hDst; _i2++) {
for (var _j2 = 0; _j2 < wDst; _j2++) {
// i&j in dst bounded coords
var r = 0;
var g = 0;
var b = 0;
var a = 0;
var realColors = 0;
for (var _y = 0; _y < hM; _y++) {
var _yPos = _i2 * hM + _y;
for (var _x = 0; _x < wM; _x++) {
var _xPos = _j2 * wM + _x;
var xyPos = (_yPos * wDst2 + _xPos) * 4;
var pixelAlpha = buf2[xyPos + 3];
if (pixelAlpha) {
r += buf2[xyPos];
g += buf2[xyPos + 1];
b += buf2[xyPos + 2];
realColors++;
}
a += pixelAlpha;
}
}
var pos = (_i2 * wDst + _j2) * 4;
bufDst[pos] = realColors ? Math.round(r / realColors) : 0;
bufDst[pos + 1] = realColors ? Math.round(g / realColors) : 0;
bufDst[pos + 2] = realColors ? Math.round(b / realColors) : 0;
bufDst[pos + 3] = Math.round(a / m);
}
}
} else {
// replace dst buffer with buf2
dst.data = buf2;
}
},
bicubicInterpolation: function bicubicInterpolation(src, dst, options) {
var interpolateCubic = function interpolateCubic(x0, x1, x2, x3, t) {
var a0 = x3 - x2 - x0 + x1;
var a1 = x0 - x1 - a0;
var a2 = x2 - x0;
var a3 = x1;
return Math.max(0, Math.min(255, a0 * (t * t * t) + a1 * (t * t) + a2 * t + a3));
};
return this._interpolate2D(src, dst, options, interpolateCubic);
},
hermiteInterpolation: function hermiteInterpolation(src, dst, options) {
var interpolateHermite = function interpolateHermite(x0, x1, x2, x3, t) {
var c0 = x1;
var c1 = 0.5 * (x2 - x0);
var c2 = x0 - 2.5 * x1 + 2 * x2 - 0.5 * x3;
var c3 = 0.5 * (x3 - x0) + 1.5 * (x1 - x2);
return Math.max(0, Math.min(255, Math.round(((c3 * t + c2) * t + c1) * t + c0)));
};
return this._interpolate2D(src, dst, options, interpolateHermite);
},
bezierInterpolation: function bezierInterpolation(src, dst, options) {
// between 2 points y(n), y(n+1), use next points out, y(n-1), y(n+2)
// to predict control points (a & b) to be placed at n+0.5
// ya(n) = y(n) + (y(n+1)-y(n-1))/4
// yb(n) = y(n+1) - (y(n+2)-y(n))/4
// then use std bezier to interpolate [n,n+1)
// 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
// note the 3* factor for the two control points
// for edge cases, can choose:
// y(-1) = y(0) - 2*(y(1)-y(0))
// y(w) = y(w-1) + 2*(y(w-1)-y(w-2))
// but can go with y(-1) = y(0) and y(w) = y(w-1)
var interpolateBezier = function interpolateBezier(x0, x1, x2, x3, t) {
// x1, x2 are the knots, use x0 and x3 to calculate control points
var cp1 = x1 + (x2 - x0) / 4;
var cp2 = x2 - (x3 - x1) / 4;
var nt = 1 - t;
var c0 = x1 * nt * nt * nt;
var c1 = 3 * cp1 * nt * nt * t;
var c2 = 3 * cp2 * nt * t * t;
var c3 = x2 * t * t * t;
return Math.max(0, Math.min(255, Math.round(c0 + c1 + c2 + c3)));
};
return this._interpolate2D(src, dst, options, interpolateBezier);
}
};
//# sourceMappingURL=resize2.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,95 @@
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports["default"] = void 0;
var _utils = require("@jimp/utils");
var _resize = _interopRequireDefault(require("./modules/resize"));
var _resize2 = _interopRequireDefault(require("./modules/resize2"));
var _default = function _default() {
return {
constants: {
RESIZE_NEAREST_NEIGHBOR: 'nearestNeighbor',
RESIZE_BILINEAR: 'bilinearInterpolation',
RESIZE_BICUBIC: 'bicubicInterpolation',
RESIZE_HERMITE: 'hermiteInterpolation',
RESIZE_BEZIER: 'bezierInterpolation'
},
"class": {
/**
* Resizes the image to a set width and height using a 2-pass bilinear algorithm
* @param {number} w the width to resize the image to (or Jimp.AUTO)
* @param {number} h the height to resize the image to (or Jimp.AUTO)
* @param {string} mode (optional) a scaling method (e.g. Jimp.RESIZE_BEZIER)
* @param {function(Error, Jimp)} cb (optional) a callback for when complete
* @returns {Jimp} this for chaining of methods
*/
resize: function resize(w, h, mode, cb) {
if (typeof w !== 'number' || typeof h !== 'number') {
return _utils.throwError.call(this, 'w and h must be numbers', cb);
}
if (typeof mode === 'function' && typeof cb === 'undefined') {
cb = mode;
mode = null;
}
if (w === this.constructor.AUTO && h === this.constructor.AUTO) {
return _utils.throwError.call(this, 'w and h cannot both be set to auto', cb);
}
if (w === this.constructor.AUTO) {
w = this.bitmap.width * (h / this.bitmap.height);
}
if (h === this.constructor.AUTO) {
h = this.bitmap.height * (w / this.bitmap.width);
}
if (w < 0 || h < 0) {
return _utils.throwError.call(this, 'w and h must be positive numbers', cb);
} // round inputs
w = Math.round(w);
h = Math.round(h);
if (typeof _resize2["default"][mode] === 'function') {
var dst = {
data: Buffer.alloc(w * h * 4),
width: w,
height: h
};
_resize2["default"][mode](this.bitmap, dst);
this.bitmap = dst;
} else {
var image = this;
var resize = new _resize["default"](this.bitmap.width, this.bitmap.height, w, h, true, true, function (buffer) {
image.bitmap.data = Buffer.from(buffer);
image.bitmap.width = w;
image.bitmap.height = h;
});
resize.resize(this.bitmap.data);
}
if ((0, _utils.isNodePattern)(cb)) {
cb.call(this, null, this);
}
return this;
}
}
};
};
exports["default"] = _default;
//# sourceMappingURL=index.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,406 @@
"use strict";
// JavaScript Image Resizer (c) 2012 - Grant Galitz
// Released to public domain 29 July 2013: https://github.com/grantgalitz/JS-Image-Resizer/issues/4
function Resize(widthOriginal, heightOriginal, targetWidth, targetHeight, blendAlpha, interpolationPass, resizeCallback) {
this.widthOriginal = Math.abs(Math.floor(widthOriginal) || 0);
this.heightOriginal = Math.abs(Math.floor(heightOriginal) || 0);
this.targetWidth = Math.abs(Math.floor(targetWidth) || 0);
this.targetHeight = Math.abs(Math.floor(targetHeight) || 0);
this.colorChannels = blendAlpha ? 4 : 3;
this.interpolationPass = Boolean(interpolationPass);
this.resizeCallback = typeof resizeCallback === 'function' ? resizeCallback : function () {};
this.targetWidthMultipliedByChannels = this.targetWidth * this.colorChannels;
this.originalWidthMultipliedByChannels = this.widthOriginal * this.colorChannels;
this.originalHeightMultipliedByChannels = this.heightOriginal * this.colorChannels;
this.widthPassResultSize = this.targetWidthMultipliedByChannels * this.heightOriginal;
this.finalResultSize = this.targetWidthMultipliedByChannels * this.targetHeight;
this.initialize();
}
Resize.prototype.initialize = function () {
// Perform some checks:
if (this.widthOriginal > 0 && this.heightOriginal > 0 && this.targetWidth > 0 && this.targetHeight > 0) {
this.configurePasses();
} else {
throw new Error('Invalid settings specified for the resizer.');
}
};
Resize.prototype.configurePasses = function () {
if (this.widthOriginal === this.targetWidth) {
// Bypass the width resizer pass:
this.resizeWidth = this.bypassResizer;
} else {
// Setup the width resizer pass:
this.ratioWeightWidthPass = this.widthOriginal / this.targetWidth;
if (this.ratioWeightWidthPass < 1 && this.interpolationPass) {
this.initializeFirstPassBuffers(true);
this.resizeWidth = this.colorChannels === 4 ? this.resizeWidthInterpolatedRGBA : this.resizeWidthInterpolatedRGB;
} else {
this.initializeFirstPassBuffers(false);
this.resizeWidth = this.colorChannels === 4 ? this.resizeWidthRGBA : this.resizeWidthRGB;
}
}
if (this.heightOriginal === this.targetHeight) {
// Bypass the height resizer pass:
this.resizeHeight = this.bypassResizer;
} else {
// Setup the height resizer pass:
this.ratioWeightHeightPass = this.heightOriginal / this.targetHeight;
if (this.ratioWeightHeightPass < 1 && this.interpolationPass) {
this.initializeSecondPassBuffers(true);
this.resizeHeight = this.resizeHeightInterpolated;
} else {
this.initializeSecondPassBuffers(false);
this.resizeHeight = this.colorChannels === 4 ? this.resizeHeightRGBA : this.resizeHeightRGB;
}
}
};
Resize.prototype._resizeWidthInterpolatedRGBChannels = function (buffer, fourthChannel) {
var channelsNum = fourthChannel ? 4 : 3;
var ratioWeight = this.ratioWeightWidthPass;
var outputBuffer = this.widthBuffer;
var weight = 0;
var finalOffset = 0;
var pixelOffset = 0;
var firstWeight = 0;
var secondWeight = 0;
var targetPosition; // Handle for only one interpolation input being valid for start calculation:
for (targetPosition = 0; weight < 1 / 3; targetPosition += channelsNum, weight += ratioWeight) {
for (finalOffset = targetPosition, pixelOffset = 0; finalOffset < this.widthPassResultSize; pixelOffset += this.originalWidthMultipliedByChannels, finalOffset += this.targetWidthMultipliedByChannels) {
outputBuffer[finalOffset] = buffer[pixelOffset];
outputBuffer[finalOffset + 1] = buffer[pixelOffset + 1];
outputBuffer[finalOffset + 2] = buffer[pixelOffset + 2];
if (fourthChannel) outputBuffer[finalOffset + 3] = buffer[pixelOffset + 3];
}
} // Adjust for overshoot of the last pass's counter:
weight -= 1 / 3;
var interpolationWidthSourceReadStop;
for (interpolationWidthSourceReadStop = this.widthOriginal - 1; weight < interpolationWidthSourceReadStop; targetPosition += channelsNum, weight += ratioWeight) {
// Calculate weightings:
secondWeight = weight % 1;
firstWeight = 1 - secondWeight; // Interpolate:
for (finalOffset = targetPosition, pixelOffset = Math.floor(weight) * channelsNum; finalOffset < this.widthPassResultSize; pixelOffset += this.originalWidthMultipliedByChannels, finalOffset += this.targetWidthMultipliedByChannels) {
outputBuffer[finalOffset + 0] = buffer[pixelOffset + 0] * firstWeight + buffer[pixelOffset + channelsNum + 0] * secondWeight;
outputBuffer[finalOffset + 1] = buffer[pixelOffset + 1] * firstWeight + buffer[pixelOffset + channelsNum + 1] * secondWeight;
outputBuffer[finalOffset + 2] = buffer[pixelOffset + 2] * firstWeight + buffer[pixelOffset + channelsNum + 2] * secondWeight;
if (fourthChannel) outputBuffer[finalOffset + 3] = buffer[pixelOffset + 3] * firstWeight + buffer[pixelOffset + channelsNum + 3] * secondWeight;
}
} // Handle for only one interpolation input being valid for end calculation:
for (interpolationWidthSourceReadStop = this.originalWidthMultipliedByChannels - channelsNum; targetPosition < this.targetWidthMultipliedByChannels; targetPosition += channelsNum) {
for (finalOffset = targetPosition, pixelOffset = interpolationWidthSourceReadStop; finalOffset < this.widthPassResultSize; pixelOffset += this.originalWidthMultipliedByChannels, finalOffset += this.targetWidthMultipliedByChannels) {
outputBuffer[finalOffset] = buffer[pixelOffset];
outputBuffer[finalOffset + 1] = buffer[pixelOffset + 1];
outputBuffer[finalOffset + 2] = buffer[pixelOffset + 2];
if (fourthChannel) outputBuffer[finalOffset + 3] = buffer[pixelOffset + 3];
}
}
return outputBuffer;
};
Resize.prototype._resizeWidthRGBChannels = function (buffer, fourthChannel) {
var channelsNum = fourthChannel ? 4 : 3;
var ratioWeight = this.ratioWeightWidthPass;
var ratioWeightDivisor = 1 / ratioWeight;
var nextLineOffsetOriginalWidth = this.originalWidthMultipliedByChannels - channelsNum + 1;
var nextLineOffsetTargetWidth = this.targetWidthMultipliedByChannels - channelsNum + 1;
var output = this.outputWidthWorkBench;
var outputBuffer = this.widthBuffer;
var trustworthyColorsCount = this.outputWidthWorkBenchOpaquePixelsCount;
var weight = 0;
var amountToNext = 0;
var actualPosition = 0;
var currentPosition = 0;
var line = 0;
var pixelOffset = 0;
var outputOffset = 0;
var multiplier = 1;
var r = 0;
var g = 0;
var b = 0;
var a = 0;
do {
for (line = 0; line < this.originalHeightMultipliedByChannels;) {
output[line++] = 0;
output[line++] = 0;
output[line++] = 0;
if (fourthChannel) {
output[line++] = 0;
trustworthyColorsCount[line / channelsNum - 1] = 0;
}
}
weight = ratioWeight;
do {
amountToNext = 1 + actualPosition - currentPosition;
multiplier = Math.min(weight, amountToNext);
for (line = 0, pixelOffset = actualPosition; line < this.originalHeightMultipliedByChannels; pixelOffset += nextLineOffsetOriginalWidth) {
r = buffer[pixelOffset];
g = buffer[++pixelOffset];
b = buffer[++pixelOffset];
a = fourthChannel ? buffer[++pixelOffset] : 255; // Ignore RGB values if pixel is completely transparent
output[line++] += (a ? r : 0) * multiplier;
output[line++] += (a ? g : 0) * multiplier;
output[line++] += (a ? b : 0) * multiplier;
if (fourthChannel) {
output[line++] += a * multiplier;
trustworthyColorsCount[line / channelsNum - 1] += a ? multiplier : 0;
}
}
if (weight >= amountToNext) {
actualPosition += channelsNum;
currentPosition = actualPosition;
weight -= amountToNext;
} else {
currentPosition += weight;
break;
}
} while (weight > 0 && actualPosition < this.originalWidthMultipliedByChannels);
for (line = 0, pixelOffset = outputOffset; line < this.originalHeightMultipliedByChannels; pixelOffset += nextLineOffsetTargetWidth) {
weight = fourthChannel ? trustworthyColorsCount[line / channelsNum] : 1;
multiplier = fourthChannel ? weight ? 1 / weight : 0 : ratioWeightDivisor;
outputBuffer[pixelOffset] = output[line++] * multiplier;
outputBuffer[++pixelOffset] = output[line++] * multiplier;
outputBuffer[++pixelOffset] = output[line++] * multiplier;
if (fourthChannel) outputBuffer[++pixelOffset] = output[line++] * ratioWeightDivisor;
}
outputOffset += channelsNum;
} while (outputOffset < this.targetWidthMultipliedByChannels);
return outputBuffer;
};
Resize.prototype._resizeHeightRGBChannels = function (buffer, fourthChannel) {
var ratioWeight = this.ratioWeightHeightPass;
var ratioWeightDivisor = 1 / ratioWeight;
var output = this.outputHeightWorkBench;
var outputBuffer = this.heightBuffer;
var trustworthyColorsCount = this.outputHeightWorkBenchOpaquePixelsCount;
var weight = 0;
var amountToNext = 0;
var actualPosition = 0;
var currentPosition = 0;
var pixelOffset = 0;
var outputOffset = 0;
var caret = 0;
var multiplier = 1;
var r = 0;
var g = 0;
var b = 0;
var a = 0;
do {
for (pixelOffset = 0; pixelOffset < this.targetWidthMultipliedByChannels;) {
output[pixelOffset++] = 0;
output[pixelOffset++] = 0;
output[pixelOffset++] = 0;
if (fourthChannel) {
output[pixelOffset++] = 0;
trustworthyColorsCount[pixelOffset / 4 - 1] = 0;
}
}
weight = ratioWeight;
do {
amountToNext = 1 + actualPosition - currentPosition;
multiplier = Math.min(weight, amountToNext);
caret = actualPosition;
for (pixelOffset = 0; pixelOffset < this.targetWidthMultipliedByChannels;) {
r = buffer[caret++];
g = buffer[caret++];
b = buffer[caret++];
a = fourthChannel ? buffer[caret++] : 255; // Ignore RGB values if pixel is completely transparent
output[pixelOffset++] += (a ? r : 0) * multiplier;
output[pixelOffset++] += (a ? g : 0) * multiplier;
output[pixelOffset++] += (a ? b : 0) * multiplier;
if (fourthChannel) {
output[pixelOffset++] += a * multiplier;
trustworthyColorsCount[pixelOffset / 4 - 1] += a ? multiplier : 0;
}
}
if (weight >= amountToNext) {
actualPosition = caret;
currentPosition = actualPosition;
weight -= amountToNext;
} else {
currentPosition += weight;
break;
}
} while (weight > 0 && actualPosition < this.widthPassResultSize);
for (pixelOffset = 0; pixelOffset < this.targetWidthMultipliedByChannels;) {
weight = fourthChannel ? trustworthyColorsCount[pixelOffset / 4] : 1;
multiplier = fourthChannel ? weight ? 1 / weight : 0 : ratioWeightDivisor;
outputBuffer[outputOffset++] = Math.round(output[pixelOffset++] * multiplier);
outputBuffer[outputOffset++] = Math.round(output[pixelOffset++] * multiplier);
outputBuffer[outputOffset++] = Math.round(output[pixelOffset++] * multiplier);
if (fourthChannel) {
outputBuffer[outputOffset++] = Math.round(output[pixelOffset++] * ratioWeightDivisor);
}
}
} while (outputOffset < this.finalResultSize);
return outputBuffer;
};
Resize.prototype.resizeWidthInterpolatedRGB = function (buffer) {
return this._resizeWidthInterpolatedRGBChannels(buffer, false);
};
Resize.prototype.resizeWidthInterpolatedRGBA = function (buffer) {
return this._resizeWidthInterpolatedRGBChannels(buffer, true);
};
Resize.prototype.resizeWidthRGB = function (buffer) {
return this._resizeWidthRGBChannels(buffer, false);
};
Resize.prototype.resizeWidthRGBA = function (buffer) {
return this._resizeWidthRGBChannels(buffer, true);
};
Resize.prototype.resizeHeightInterpolated = function (buffer) {
var ratioWeight = this.ratioWeightHeightPass;
var outputBuffer = this.heightBuffer;
var weight = 0;
var finalOffset = 0;
var pixelOffset = 0;
var pixelOffsetAccumulated = 0;
var pixelOffsetAccumulated2 = 0;
var firstWeight = 0;
var secondWeight = 0;
var interpolationHeightSourceReadStop; // Handle for only one interpolation input being valid for start calculation:
for (; weight < 1 / 3; weight += ratioWeight) {
for (pixelOffset = 0; pixelOffset < this.targetWidthMultipliedByChannels;) {
outputBuffer[finalOffset++] = Math.round(buffer[pixelOffset++]);
}
} // Adjust for overshoot of the last pass's counter:
weight -= 1 / 3;
for (interpolationHeightSourceReadStop = this.heightOriginal - 1; weight < interpolationHeightSourceReadStop; weight += ratioWeight) {
// Calculate weightings:
secondWeight = weight % 1;
firstWeight = 1 - secondWeight; // Interpolate:
pixelOffsetAccumulated = Math.floor(weight) * this.targetWidthMultipliedByChannels;
pixelOffsetAccumulated2 = pixelOffsetAccumulated + this.targetWidthMultipliedByChannels;
for (pixelOffset = 0; pixelOffset < this.targetWidthMultipliedByChannels; ++pixelOffset) {
outputBuffer[finalOffset++] = Math.round(buffer[pixelOffsetAccumulated++] * firstWeight + buffer[pixelOffsetAccumulated2++] * secondWeight);
}
} // Handle for only one interpolation input being valid for end calculation:
while (finalOffset < this.finalResultSize) {
for (pixelOffset = 0, pixelOffsetAccumulated = interpolationHeightSourceReadStop * this.targetWidthMultipliedByChannels; pixelOffset < this.targetWidthMultipliedByChannels; ++pixelOffset) {
outputBuffer[finalOffset++] = Math.round(buffer[pixelOffsetAccumulated++]);
}
}
return outputBuffer;
};
Resize.prototype.resizeHeightRGB = function (buffer) {
return this._resizeHeightRGBChannels(buffer, false);
};
Resize.prototype.resizeHeightRGBA = function (buffer) {
return this._resizeHeightRGBChannels(buffer, true);
};
Resize.prototype.resize = function (buffer) {
this.resizeCallback(this.resizeHeight(this.resizeWidth(buffer)));
};
Resize.prototype.bypassResizer = function (buffer) {
// Just return the buffer passed:
return buffer;
};
Resize.prototype.initializeFirstPassBuffers = function (BILINEARAlgo) {
// Initialize the internal width pass buffers:
this.widthBuffer = this.generateFloatBuffer(this.widthPassResultSize);
if (!BILINEARAlgo) {
this.outputWidthWorkBench = this.generateFloatBuffer(this.originalHeightMultipliedByChannels);
if (this.colorChannels > 3) {
this.outputWidthWorkBenchOpaquePixelsCount = this.generateFloat64Buffer(this.heightOriginal);
}
}
};
Resize.prototype.initializeSecondPassBuffers = function (BILINEARAlgo) {
// Initialize the internal height pass buffers:
this.heightBuffer = this.generateUint8Buffer(this.finalResultSize);
if (!BILINEARAlgo) {
this.outputHeightWorkBench = this.generateFloatBuffer(this.targetWidthMultipliedByChannels);
if (this.colorChannels > 3) {
this.outputHeightWorkBenchOpaquePixelsCount = this.generateFloat64Buffer(this.targetWidth);
}
}
};
Resize.prototype.generateFloatBuffer = function (bufferLength) {
// Generate a float32 typed array buffer:
try {
return new Float32Array(bufferLength);
} catch (error) {
return [];
}
};
Resize.prototype.generateFloat64Buffer = function (bufferLength) {
// Generate a float64 typed array buffer:
try {
return new Float64Array(bufferLength);
} catch (error) {
return [];
}
};
Resize.prototype.generateUint8Buffer = function (bufferLength) {
// Generate a uint8 typed array buffer:
try {
return new Uint8Array(bufferLength);
} catch (error) {
return [];
}
};
module.exports = Resize;
//# sourceMappingURL=resize.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,266 @@
"use strict";
/**
* Copyright (c) 2015 Guyon Roche
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:</p>
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
module.exports = {
nearestNeighbor: function nearestNeighbor(src, dst) {
var wSrc = src.width;
var hSrc = src.height;
var wDst = dst.width;
var hDst = dst.height;
var bufSrc = src.data;
var bufDst = dst.data;
for (var i = 0; i < hDst; i++) {
for (var j = 0; j < wDst; j++) {
var posDst = (i * wDst + j) * 4;
var iSrc = Math.floor(i * hSrc / hDst);
var jSrc = Math.floor(j * wSrc / wDst);
var posSrc = (iSrc * wSrc + jSrc) * 4;
bufDst[posDst++] = bufSrc[posSrc++];
bufDst[posDst++] = bufSrc[posSrc++];
bufDst[posDst++] = bufSrc[posSrc++];
bufDst[posDst++] = bufSrc[posSrc++];
}
}
},
bilinearInterpolation: function bilinearInterpolation(src, dst) {
var wSrc = src.width;
var hSrc = src.height;
var wDst = dst.width;
var hDst = dst.height;
var bufSrc = src.data;
var bufDst = dst.data;
var interpolate = function interpolate(k, kMin, vMin, kMax, vMax) {
// special case - k is integer
if (kMin === kMax) {
return vMin;
}
return Math.round((k - kMin) * vMax + (kMax - k) * vMin);
};
var assign = function assign(pos, offset, x, xMin, xMax, y, yMin, yMax) {
var posMin = (yMin * wSrc + xMin) * 4 + offset;
var posMax = (yMin * wSrc + xMax) * 4 + offset;
var vMin = interpolate(x, xMin, bufSrc[posMin], xMax, bufSrc[posMax]); // special case, y is integer
if (yMax === yMin) {
bufDst[pos + offset] = vMin;
} else {
posMin = (yMax * wSrc + xMin) * 4 + offset;
posMax = (yMax * wSrc + xMax) * 4 + offset;
var vMax = interpolate(x, xMin, bufSrc[posMin], xMax, bufSrc[posMax]);
bufDst[pos + offset] = interpolate(y, yMin, vMin, yMax, vMax);
}
};
for (var i = 0; i < hDst; i++) {
for (var j = 0; j < wDst; j++) {
var posDst = (i * wDst + j) * 4; // x & y in src coordinates
var x = j * wSrc / wDst;
var xMin = Math.floor(x);
var xMax = Math.min(Math.ceil(x), wSrc - 1);
var y = i * hSrc / hDst;
var yMin = Math.floor(y);
var yMax = Math.min(Math.ceil(y), hSrc - 1);
assign(posDst, 0, x, xMin, xMax, y, yMin, yMax);
assign(posDst, 1, x, xMin, xMax, y, yMin, yMax);
assign(posDst, 2, x, xMin, xMax, y, yMin, yMax);
assign(posDst, 3, x, xMin, xMax, y, yMin, yMax);
}
}
},
_interpolate2D: function _interpolate2D(src, dst, options, interpolate) {
var bufSrc = src.data;
var bufDst = dst.data;
var wSrc = src.width;
var hSrc = src.height;
var wDst = dst.width;
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
var wM = Math.max(1, Math.floor(wSrc / wDst));
var wDst2 = wDst * wM;
var hM = Math.max(1, Math.floor(hSrc / hDst));
var hDst2 = hDst * hM; // ===========================================================
// Pass 1 - interpolate rows
// buf1 has width of dst2 and height of src
var buf1 = Buffer.alloc(wDst2 * hSrc * 4);
for (var i = 0; i < hSrc; i++) {
for (var j = 0; j < wDst2; j++) {
// i in src coords, j in dst coords
// calculate x in src coords
// this interpolation requires 4 sample points and the two inner ones must be real
// the outer points can be fudged for the edges.
// therefore (wSrc-1)/wDst2
var x = j * (wSrc - 1) / wDst2;
var xPos = Math.floor(x);
var t = x - xPos;
var srcPos = (i * wSrc + xPos) * 4;
var buf1Pos = (i * wDst2 + j) * 4;
for (var k = 0; k < 4; k++) {
var kPos = srcPos + k;
var x0 = xPos > 0 ? bufSrc[kPos - 4] : 2 * bufSrc[kPos] - bufSrc[kPos + 4];
var x1 = bufSrc[kPos];
var x2 = bufSrc[kPos + 4];
var x3 = xPos < wSrc - 2 ? bufSrc[kPos + 8] : 2 * bufSrc[kPos + 4] - bufSrc[kPos];
buf1[buf1Pos + k] = interpolate(x0, x1, x2, x3, t);
}
}
} // this._writeFile(wDst2, hSrc, buf1, "out/buf1.jpg");
// ===========================================================
// Pass 2 - interpolate columns
// buf2 has width and height of dst2
var buf2 = Buffer.alloc(wDst2 * hDst2 * 4);
for (var _i = 0; _i < hDst2; _i++) {
for (var _j = 0; _j < wDst2; _j++) {
// i&j in dst2 coords
// calculate y in buf1 coords
// this interpolation requires 4 sample points and the two inner ones must be real
// the outer points can be fudged for the edges.
// therefore (hSrc-1)/hDst2
var y = _i * (hSrc - 1) / hDst2;
var yPos = Math.floor(y);
var _t = y - yPos;
var _buf1Pos = (yPos * wDst2 + _j) * 4;
var buf2Pos = (_i * wDst2 + _j) * 4;
for (var _k = 0; _k < 4; _k++) {
var _kPos = _buf1Pos + _k;
var y0 = yPos > 0 ? buf1[_kPos - wDst2 * 4] : 2 * buf1[_kPos] - buf1[_kPos + wDst2 * 4];
var y1 = buf1[_kPos];
var y2 = buf1[_kPos + wDst2 * 4];
var y3 = yPos < hSrc - 2 ? buf1[_kPos + wDst2 * 8] : 2 * buf1[_kPos + wDst2 * 4] - buf1[_kPos];
buf2[buf2Pos + _k] = interpolate(y0, y1, y2, y3, _t);
}
}
} // this._writeFile(wDst2, hDst2, buf2, "out/buf2.jpg");
// ===========================================================
// Pass 3 - scale to dst
var m = wM * hM;
if (m > 1) {
for (var _i2 = 0; _i2 < hDst; _i2++) {
for (var _j2 = 0; _j2 < wDst; _j2++) {
// i&j in dst bounded coords
var r = 0;
var g = 0;
var b = 0;
var a = 0;
var realColors = 0;
for (var _y = 0; _y < hM; _y++) {
var _yPos = _i2 * hM + _y;
for (var _x = 0; _x < wM; _x++) {
var _xPos = _j2 * wM + _x;
var xyPos = (_yPos * wDst2 + _xPos) * 4;
var pixelAlpha = buf2[xyPos + 3];
if (pixelAlpha) {
r += buf2[xyPos];
g += buf2[xyPos + 1];
b += buf2[xyPos + 2];
realColors++;
}
a += pixelAlpha;
}
}
var pos = (_i2 * wDst + _j2) * 4;
bufDst[pos] = realColors ? Math.round(r / realColors) : 0;
bufDst[pos + 1] = realColors ? Math.round(g / realColors) : 0;
bufDst[pos + 2] = realColors ? Math.round(b / realColors) : 0;
bufDst[pos + 3] = Math.round(a / m);
}
}
} else {
// replace dst buffer with buf2
dst.data = buf2;
}
},
bicubicInterpolation: function bicubicInterpolation(src, dst, options) {
var interpolateCubic = function interpolateCubic(x0, x1, x2, x3, t) {
var a0 = x3 - x2 - x0 + x1;
var a1 = x0 - x1 - a0;
var a2 = x2 - x0;
var a3 = x1;
return Math.max(0, Math.min(255, a0 * (t * t * t) + a1 * (t * t) + a2 * t + a3));
};
return this._interpolate2D(src, dst, options, interpolateCubic);
},
hermiteInterpolation: function hermiteInterpolation(src, dst, options) {
var interpolateHermite = function interpolateHermite(x0, x1, x2, x3, t) {
var c0 = x1;
var c1 = 0.5 * (x2 - x0);
var c2 = x0 - 2.5 * x1 + 2 * x2 - 0.5 * x3;
var c3 = 0.5 * (x3 - x0) + 1.5 * (x1 - x2);
return Math.max(0, Math.min(255, Math.round(((c3 * t + c2) * t + c1) * t + c0)));
};
return this._interpolate2D(src, dst, options, interpolateHermite);
},
bezierInterpolation: function bezierInterpolation(src, dst, options) {
// between 2 points y(n), y(n+1), use next points out, y(n-1), y(n+2)
// to predict control points (a & b) to be placed at n+0.5
// ya(n) = y(n) + (y(n+1)-y(n-1))/4
// yb(n) = y(n+1) - (y(n+2)-y(n))/4
// then use std bezier to interpolate [n,n+1)
// 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
// note the 3* factor for the two control points
// for edge cases, can choose:
// y(-1) = y(0) - 2*(y(1)-y(0))
// y(w) = y(w-1) + 2*(y(w-1)-y(w-2))
// but can go with y(-1) = y(0) and y(w) = y(w-1)
var interpolateBezier = function interpolateBezier(x0, x1, x2, x3, t) {
// x1, x2 are the knots, use x0 and x3 to calculate control points
var cp1 = x1 + (x2 - x0) / 4;
var cp2 = x2 - (x3 - x1) / 4;
var nt = 1 - t;
var c0 = x1 * nt * nt * nt;
var c1 = 3 * cp1 * nt * nt * t;
var c2 = 3 * cp2 * nt * t * t;
var c3 = x2 * t * t * t;
return Math.max(0, Math.min(255, Math.round(c0 + c1 + c2 + c3)));
};
return this._interpolate2D(src, dst, options, interpolateBezier);
}
};
//# sourceMappingURL=resize2.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,21 @@
import { ImageCallback } from '@jimp/core';
interface ResizeClass {
resize(w: number, h: number, cb?: ImageCallback<this>): this;
resize(w: number, h: number, mode?: string, cb?: ImageCallback<this>): this;
}
interface Resize {
constants: {
// resize methods
RESIZE_NEAREST_NEIGHBOR: 'nearestNeighbor';
RESIZE_BILINEAR: 'bilinearInterpolation';
RESIZE_BICUBIC: 'bicubicInterpolation';
RESIZE_HERMITE: 'hermiteInterpolation';
RESIZE_BEZIER: 'bezierInterpolation';
}
class: ResizeClass
}
export default function(): Resize;

View File

@@ -0,0 +1,38 @@
{
"name": "@jimp/plugin-resize",
"version": "0.10.3",
"description": "Resize an image.",
"main": "dist/index.js",
"module": "es/index.js",
"types": "index.d.ts",
"scripts": {
"test": "cross-env BABEL_ENV=test mocha --require @babel/register",
"test:watch": "npm run test -- --reporter min --watch",
"test:coverage": "nyc npm run test",
"build": "npm run build:node:production && npm run build:module",
"build:watch": "npm run build:node:debug -- -- --watch --verbose",
"build:debug": "npm run build:node:debug",
"build:module": "cross-env BABEL_ENV=module babel src -d es --source-maps --config-file ../../babel.config.js",
"build:node": "babel src -d dist --source-maps --config-file ../../babel.config.js",
"build:node:debug": "cross-env BABEL_ENV=development npm run build:node",
"build:node:production": "cross-env BABEL_ENV=production npm run build:node"
},
"author": "",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.7.2",
"@jimp/utils": "^0.10.3",
"core-js": "^3.4.1"
},
"peerDependencies": {
"@jimp/custom": ">=0.3.5"
},
"devDependencies": {
"@jimp/custom": "^0.10.3",
"@jimp/test-utils": "^0.10.3"
},
"publishConfig": {
"access": "public"
},
"gitHead": "37197106eae5c26231018dfdc0254422f6b43927"
}

View File

@@ -0,0 +1,87 @@
import { throwError, isNodePattern } from '@jimp/utils';
import Resize from './modules/resize';
import Resize2 from './modules/resize2';
export default () => ({
constants: {
RESIZE_NEAREST_NEIGHBOR: 'nearestNeighbor',
RESIZE_BILINEAR: 'bilinearInterpolation',
RESIZE_BICUBIC: 'bicubicInterpolation',
RESIZE_HERMITE: 'hermiteInterpolation',
RESIZE_BEZIER: 'bezierInterpolation'
},
class: {
/**
* Resizes the image to a set width and height using a 2-pass bilinear algorithm
* @param {number} w the width to resize the image to (or Jimp.AUTO)
* @param {number} h the height to resize the image to (or Jimp.AUTO)
* @param {string} mode (optional) a scaling method (e.g. Jimp.RESIZE_BEZIER)
* @param {function(Error, Jimp)} cb (optional) a callback for when complete
* @returns {Jimp} this for chaining of methods
*/
resize(w, h, mode, cb) {
if (typeof w !== 'number' || typeof h !== 'number') {
return throwError.call(this, 'w and h must be numbers', cb);
}
if (typeof mode === 'function' && typeof cb === 'undefined') {
cb = mode;
mode = null;
}
if (w === this.constructor.AUTO && h === this.constructor.AUTO) {
return throwError.call(this, 'w and h cannot both be set to auto', cb);
}
if (w === this.constructor.AUTO) {
w = this.bitmap.width * (h / this.bitmap.height);
}
if (h === this.constructor.AUTO) {
h = this.bitmap.height * (w / this.bitmap.width);
}
if (w < 0 || h < 0) {
return throwError.call(this, 'w and h must be positive numbers', cb);
}
// round inputs
w = Math.round(w);
h = Math.round(h);
if (typeof Resize2[mode] === 'function') {
const dst = {
data: Buffer.alloc(w * h * 4),
width: w,
height: h
};
Resize2[mode](this.bitmap, dst);
this.bitmap = dst;
} else {
const image = this;
const resize = new Resize(
this.bitmap.width,
this.bitmap.height,
w,
h,
true,
true,
buffer => {
image.bitmap.data = Buffer.from(buffer);
image.bitmap.width = w;
image.bitmap.height = h;
}
);
resize.resize(this.bitmap.data);
}
if (isNodePattern(cb)) {
cb.call(this, null, this);
}
return this;
}
}
});

View File

@@ -0,0 +1,548 @@
// JavaScript Image Resizer (c) 2012 - Grant Galitz
// Released to public domain 29 July 2013: https://github.com/grantgalitz/JS-Image-Resizer/issues/4
function Resize(
widthOriginal,
heightOriginal,
targetWidth,
targetHeight,
blendAlpha,
interpolationPass,
resizeCallback
) {
this.widthOriginal = Math.abs(Math.floor(widthOriginal) || 0);
this.heightOriginal = Math.abs(Math.floor(heightOriginal) || 0);
this.targetWidth = Math.abs(Math.floor(targetWidth) || 0);
this.targetHeight = Math.abs(Math.floor(targetHeight) || 0);
this.colorChannels = blendAlpha ? 4 : 3;
this.interpolationPass = Boolean(interpolationPass);
this.resizeCallback =
typeof resizeCallback === 'function' ? resizeCallback : function() {};
this.targetWidthMultipliedByChannels = this.targetWidth * this.colorChannels;
this.originalWidthMultipliedByChannels =
this.widthOriginal * this.colorChannels;
this.originalHeightMultipliedByChannels =
this.heightOriginal * this.colorChannels;
this.widthPassResultSize =
this.targetWidthMultipliedByChannels * this.heightOriginal;
this.finalResultSize =
this.targetWidthMultipliedByChannels * this.targetHeight;
this.initialize();
}
Resize.prototype.initialize = function() {
// Perform some checks:
if (
this.widthOriginal > 0 &&
this.heightOriginal > 0 &&
this.targetWidth > 0 &&
this.targetHeight > 0
) {
this.configurePasses();
} else {
throw new Error('Invalid settings specified for the resizer.');
}
};
Resize.prototype.configurePasses = function() {
if (this.widthOriginal === this.targetWidth) {
// Bypass the width resizer pass:
this.resizeWidth = this.bypassResizer;
} else {
// Setup the width resizer pass:
this.ratioWeightWidthPass = this.widthOriginal / this.targetWidth;
if (this.ratioWeightWidthPass < 1 && this.interpolationPass) {
this.initializeFirstPassBuffers(true);
this.resizeWidth =
this.colorChannels === 4
? this.resizeWidthInterpolatedRGBA
: this.resizeWidthInterpolatedRGB;
} else {
this.initializeFirstPassBuffers(false);
this.resizeWidth =
this.colorChannels === 4 ? this.resizeWidthRGBA : this.resizeWidthRGB;
}
}
if (this.heightOriginal === this.targetHeight) {
// Bypass the height resizer pass:
this.resizeHeight = this.bypassResizer;
} else {
// Setup the height resizer pass:
this.ratioWeightHeightPass = this.heightOriginal / this.targetHeight;
if (this.ratioWeightHeightPass < 1 && this.interpolationPass) {
this.initializeSecondPassBuffers(true);
this.resizeHeight = this.resizeHeightInterpolated;
} else {
this.initializeSecondPassBuffers(false);
this.resizeHeight =
this.colorChannels === 4 ? this.resizeHeightRGBA : this.resizeHeightRGB;
}
}
};
Resize.prototype._resizeWidthInterpolatedRGBChannels = function(
buffer,
fourthChannel
) {
const channelsNum = fourthChannel ? 4 : 3;
const ratioWeight = this.ratioWeightWidthPass;
const outputBuffer = this.widthBuffer;
let weight = 0;
let finalOffset = 0;
let pixelOffset = 0;
let firstWeight = 0;
let secondWeight = 0;
let targetPosition;
// Handle for only one interpolation input being valid for start calculation:
for (
targetPosition = 0;
weight < 1 / 3;
targetPosition += channelsNum, weight += ratioWeight
) {
for (
finalOffset = targetPosition, pixelOffset = 0;
finalOffset < this.widthPassResultSize;
pixelOffset += this.originalWidthMultipliedByChannels,
finalOffset += this.targetWidthMultipliedByChannels
) {
outputBuffer[finalOffset] = buffer[pixelOffset];
outputBuffer[finalOffset + 1] = buffer[pixelOffset + 1];
outputBuffer[finalOffset + 2] = buffer[pixelOffset + 2];
if (fourthChannel)
outputBuffer[finalOffset + 3] = buffer[pixelOffset + 3];
}
}
// Adjust for overshoot of the last pass's counter:
weight -= 1 / 3;
let interpolationWidthSourceReadStop;
for (
interpolationWidthSourceReadStop = this.widthOriginal - 1;
weight < interpolationWidthSourceReadStop;
targetPosition += channelsNum, weight += ratioWeight
) {
// Calculate weightings:
secondWeight = weight % 1;
firstWeight = 1 - secondWeight;
// Interpolate:
for (
finalOffset = targetPosition,
pixelOffset = Math.floor(weight) * channelsNum;
finalOffset < this.widthPassResultSize;
pixelOffset += this.originalWidthMultipliedByChannels,
finalOffset += this.targetWidthMultipliedByChannels
) {
outputBuffer[finalOffset + 0] =
buffer[pixelOffset + 0] * firstWeight +
buffer[pixelOffset + channelsNum + 0] * secondWeight;
outputBuffer[finalOffset + 1] =
buffer[pixelOffset + 1] * firstWeight +
buffer[pixelOffset + channelsNum + 1] * secondWeight;
outputBuffer[finalOffset + 2] =
buffer[pixelOffset + 2] * firstWeight +
buffer[pixelOffset + channelsNum + 2] * secondWeight;
if (fourthChannel)
outputBuffer[finalOffset + 3] =
buffer[pixelOffset + 3] * firstWeight +
buffer[pixelOffset + channelsNum + 3] * secondWeight;
}
}
// Handle for only one interpolation input being valid for end calculation:
for (
interpolationWidthSourceReadStop =
this.originalWidthMultipliedByChannels - channelsNum;
targetPosition < this.targetWidthMultipliedByChannels;
targetPosition += channelsNum
) {
for (
finalOffset = targetPosition,
pixelOffset = interpolationWidthSourceReadStop;
finalOffset < this.widthPassResultSize;
pixelOffset += this.originalWidthMultipliedByChannels,
finalOffset += this.targetWidthMultipliedByChannels
) {
outputBuffer[finalOffset] = buffer[pixelOffset];
outputBuffer[finalOffset + 1] = buffer[pixelOffset + 1];
outputBuffer[finalOffset + 2] = buffer[pixelOffset + 2];
if (fourthChannel)
outputBuffer[finalOffset + 3] = buffer[pixelOffset + 3];
}
}
return outputBuffer;
};
Resize.prototype._resizeWidthRGBChannels = function(buffer, fourthChannel) {
const channelsNum = fourthChannel ? 4 : 3;
const ratioWeight = this.ratioWeightWidthPass;
const ratioWeightDivisor = 1 / ratioWeight;
const nextLineOffsetOriginalWidth =
this.originalWidthMultipliedByChannels - channelsNum + 1;
const nextLineOffsetTargetWidth =
this.targetWidthMultipliedByChannels - channelsNum + 1;
const output = this.outputWidthWorkBench;
const outputBuffer = this.widthBuffer;
const trustworthyColorsCount = this.outputWidthWorkBenchOpaquePixelsCount;
let weight = 0;
let amountToNext = 0;
let actualPosition = 0;
let currentPosition = 0;
let line = 0;
let pixelOffset = 0;
let outputOffset = 0;
let multiplier = 1;
let r = 0;
let g = 0;
let b = 0;
let a = 0;
do {
for (line = 0; line < this.originalHeightMultipliedByChannels; ) {
output[line++] = 0;
output[line++] = 0;
output[line++] = 0;
if (fourthChannel) {
output[line++] = 0;
trustworthyColorsCount[line / channelsNum - 1] = 0;
}
}
weight = ratioWeight;
do {
amountToNext = 1 + actualPosition - currentPosition;
multiplier = Math.min(weight, amountToNext);
for (
line = 0, pixelOffset = actualPosition;
line < this.originalHeightMultipliedByChannels;
pixelOffset += nextLineOffsetOriginalWidth
) {
r = buffer[pixelOffset];
g = buffer[++pixelOffset];
b = buffer[++pixelOffset];
a = fourthChannel ? buffer[++pixelOffset] : 255;
// Ignore RGB values if pixel is completely transparent
output[line++] += (a ? r : 0) * multiplier;
output[line++] += (a ? g : 0) * multiplier;
output[line++] += (a ? b : 0) * multiplier;
if (fourthChannel) {
output[line++] += a * multiplier;
trustworthyColorsCount[line / channelsNum - 1] += a ? multiplier : 0;
}
}
if (weight >= amountToNext) {
actualPosition += channelsNum;
currentPosition = actualPosition;
weight -= amountToNext;
} else {
currentPosition += weight;
break;
}
} while (
weight > 0 &&
actualPosition < this.originalWidthMultipliedByChannels
);
for (
line = 0, pixelOffset = outputOffset;
line < this.originalHeightMultipliedByChannels;
pixelOffset += nextLineOffsetTargetWidth
) {
weight = fourthChannel ? trustworthyColorsCount[line / channelsNum] : 1;
multiplier = fourthChannel
? weight
? 1 / weight
: 0
: ratioWeightDivisor;
outputBuffer[pixelOffset] = output[line++] * multiplier;
outputBuffer[++pixelOffset] = output[line++] * multiplier;
outputBuffer[++pixelOffset] = output[line++] * multiplier;
if (fourthChannel)
outputBuffer[++pixelOffset] = output[line++] * ratioWeightDivisor;
}
outputOffset += channelsNum;
} while (outputOffset < this.targetWidthMultipliedByChannels);
return outputBuffer;
};
Resize.prototype._resizeHeightRGBChannels = function(buffer, fourthChannel) {
const ratioWeight = this.ratioWeightHeightPass;
const ratioWeightDivisor = 1 / ratioWeight;
const output = this.outputHeightWorkBench;
const outputBuffer = this.heightBuffer;
const trustworthyColorsCount = this.outputHeightWorkBenchOpaquePixelsCount;
let weight = 0;
let amountToNext = 0;
let actualPosition = 0;
let currentPosition = 0;
let pixelOffset = 0;
let outputOffset = 0;
let caret = 0;
let multiplier = 1;
let r = 0;
let g = 0;
let b = 0;
let a = 0;
do {
for (
pixelOffset = 0;
pixelOffset < this.targetWidthMultipliedByChannels;
) {
output[pixelOffset++] = 0;
output[pixelOffset++] = 0;
output[pixelOffset++] = 0;
if (fourthChannel) {
output[pixelOffset++] = 0;
trustworthyColorsCount[pixelOffset / 4 - 1] = 0;
}
}
weight = ratioWeight;
do {
amountToNext = 1 + actualPosition - currentPosition;
multiplier = Math.min(weight, amountToNext);
caret = actualPosition;
for (
pixelOffset = 0;
pixelOffset < this.targetWidthMultipliedByChannels;
) {
r = buffer[caret++];
g = buffer[caret++];
b = buffer[caret++];
a = fourthChannel ? buffer[caret++] : 255;
// Ignore RGB values if pixel is completely transparent
output[pixelOffset++] += (a ? r : 0) * multiplier;
output[pixelOffset++] += (a ? g : 0) * multiplier;
output[pixelOffset++] += (a ? b : 0) * multiplier;
if (fourthChannel) {
output[pixelOffset++] += a * multiplier;
trustworthyColorsCount[pixelOffset / 4 - 1] += a ? multiplier : 0;
}
}
if (weight >= amountToNext) {
actualPosition = caret;
currentPosition = actualPosition;
weight -= amountToNext;
} else {
currentPosition += weight;
break;
}
} while (weight > 0 && actualPosition < this.widthPassResultSize);
for (
pixelOffset = 0;
pixelOffset < this.targetWidthMultipliedByChannels;
) {
weight = fourthChannel ? trustworthyColorsCount[pixelOffset / 4] : 1;
multiplier = fourthChannel
? weight
? 1 / weight
: 0
: ratioWeightDivisor;
outputBuffer[outputOffset++] = Math.round(
output[pixelOffset++] * multiplier
);
outputBuffer[outputOffset++] = Math.round(
output[pixelOffset++] * multiplier
);
outputBuffer[outputOffset++] = Math.round(
output[pixelOffset++] * multiplier
);
if (fourthChannel) {
outputBuffer[outputOffset++] = Math.round(
output[pixelOffset++] * ratioWeightDivisor
);
}
}
} while (outputOffset < this.finalResultSize);
return outputBuffer;
};
Resize.prototype.resizeWidthInterpolatedRGB = function(buffer) {
return this._resizeWidthInterpolatedRGBChannels(buffer, false);
};
Resize.prototype.resizeWidthInterpolatedRGBA = function(buffer) {
return this._resizeWidthInterpolatedRGBChannels(buffer, true);
};
Resize.prototype.resizeWidthRGB = function(buffer) {
return this._resizeWidthRGBChannels(buffer, false);
};
Resize.prototype.resizeWidthRGBA = function(buffer) {
return this._resizeWidthRGBChannels(buffer, true);
};
Resize.prototype.resizeHeightInterpolated = function(buffer) {
const ratioWeight = this.ratioWeightHeightPass;
const outputBuffer = this.heightBuffer;
let weight = 0;
let finalOffset = 0;
let pixelOffset = 0;
let pixelOffsetAccumulated = 0;
let pixelOffsetAccumulated2 = 0;
let firstWeight = 0;
let secondWeight = 0;
let interpolationHeightSourceReadStop;
// Handle for only one interpolation input being valid for start calculation:
for (; weight < 1 / 3; weight += ratioWeight) {
for (
pixelOffset = 0;
pixelOffset < this.targetWidthMultipliedByChannels;
) {
outputBuffer[finalOffset++] = Math.round(buffer[pixelOffset++]);
}
}
// Adjust for overshoot of the last pass's counter:
weight -= 1 / 3;
for (
interpolationHeightSourceReadStop = this.heightOriginal - 1;
weight < interpolationHeightSourceReadStop;
weight += ratioWeight
) {
// Calculate weightings:
secondWeight = weight % 1;
firstWeight = 1 - secondWeight;
// Interpolate:
pixelOffsetAccumulated =
Math.floor(weight) * this.targetWidthMultipliedByChannels;
pixelOffsetAccumulated2 =
pixelOffsetAccumulated + this.targetWidthMultipliedByChannels;
for (
pixelOffset = 0;
pixelOffset < this.targetWidthMultipliedByChannels;
++pixelOffset
) {
outputBuffer[finalOffset++] = Math.round(
buffer[pixelOffsetAccumulated++] * firstWeight +
buffer[pixelOffsetAccumulated2++] * secondWeight
);
}
}
// Handle for only one interpolation input being valid for end calculation:
while (finalOffset < this.finalResultSize) {
for (
pixelOffset = 0,
pixelOffsetAccumulated =
interpolationHeightSourceReadStop *
this.targetWidthMultipliedByChannels;
pixelOffset < this.targetWidthMultipliedByChannels;
++pixelOffset
) {
outputBuffer[finalOffset++] = Math.round(
buffer[pixelOffsetAccumulated++]
);
}
}
return outputBuffer;
};
Resize.prototype.resizeHeightRGB = function(buffer) {
return this._resizeHeightRGBChannels(buffer, false);
};
Resize.prototype.resizeHeightRGBA = function(buffer) {
return this._resizeHeightRGBChannels(buffer, true);
};
Resize.prototype.resize = function(buffer) {
this.resizeCallback(this.resizeHeight(this.resizeWidth(buffer)));
};
Resize.prototype.bypassResizer = function(buffer) {
// Just return the buffer passed:
return buffer;
};
Resize.prototype.initializeFirstPassBuffers = function(BILINEARAlgo) {
// Initialize the internal width pass buffers:
this.widthBuffer = this.generateFloatBuffer(this.widthPassResultSize);
if (!BILINEARAlgo) {
this.outputWidthWorkBench = this.generateFloatBuffer(
this.originalHeightMultipliedByChannels
);
if (this.colorChannels > 3) {
this.outputWidthWorkBenchOpaquePixelsCount = this.generateFloat64Buffer(
this.heightOriginal
);
}
}
};
Resize.prototype.initializeSecondPassBuffers = function(BILINEARAlgo) {
// Initialize the internal height pass buffers:
this.heightBuffer = this.generateUint8Buffer(this.finalResultSize);
if (!BILINEARAlgo) {
this.outputHeightWorkBench = this.generateFloatBuffer(
this.targetWidthMultipliedByChannels
);
if (this.colorChannels > 3) {
this.outputHeightWorkBenchOpaquePixelsCount = this.generateFloat64Buffer(
this.targetWidth
);
}
}
};
Resize.prototype.generateFloatBuffer = function(bufferLength) {
// Generate a float32 typed array buffer:
try {
return new Float32Array(bufferLength);
} catch (error) {
return [];
}
};
Resize.prototype.generateFloat64Buffer = function(bufferLength) {
// Generate a float64 typed array buffer:
try {
return new Float64Array(bufferLength);
} catch (error) {
return [];
}
};
Resize.prototype.generateUint8Buffer = function(bufferLength) {
// Generate a uint8 typed array buffer:
try {
return new Uint8Array(bufferLength);
} catch (error) {
return [];
}
};
module.exports = Resize;

View File

@@ -0,0 +1,293 @@
/**
* Copyright (c) 2015 Guyon Roche
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:</p>
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
module.exports = {
nearestNeighbor(src, dst) {
const wSrc = src.width;
const hSrc = src.height;
const wDst = dst.width;
const hDst = dst.height;
const bufSrc = src.data;
const bufDst = dst.data;
for (let i = 0; i < hDst; i++) {
for (let j = 0; j < wDst; j++) {
let posDst = (i * wDst + j) * 4;
const iSrc = Math.floor((i * hSrc) / hDst);
const jSrc = Math.floor((j * wSrc) / wDst);
let posSrc = (iSrc * wSrc + jSrc) * 4;
bufDst[posDst++] = bufSrc[posSrc++];
bufDst[posDst++] = bufSrc[posSrc++];
bufDst[posDst++] = bufSrc[posSrc++];
bufDst[posDst++] = bufSrc[posSrc++];
}
}
},
bilinearInterpolation(src, dst) {
const wSrc = src.width;
const hSrc = src.height;
const wDst = dst.width;
const hDst = dst.height;
const bufSrc = src.data;
const bufDst = dst.data;
const interpolate = function(k, kMin, vMin, kMax, vMax) {
// special case - k is integer
if (kMin === kMax) {
return vMin;
}
return Math.round((k - kMin) * vMax + (kMax - k) * vMin);
};
const assign = function(pos, offset, x, xMin, xMax, y, yMin, yMax) {
let posMin = (yMin * wSrc + xMin) * 4 + offset;
let posMax = (yMin * wSrc + xMax) * 4 + offset;
const vMin = interpolate(x, xMin, bufSrc[posMin], xMax, bufSrc[posMax]);
// special case, y is integer
if (yMax === yMin) {
bufDst[pos + offset] = vMin;
} else {
posMin = (yMax * wSrc + xMin) * 4 + offset;
posMax = (yMax * wSrc + xMax) * 4 + offset;
const vMax = interpolate(x, xMin, bufSrc[posMin], xMax, bufSrc[posMax]);
bufDst[pos + offset] = interpolate(y, yMin, vMin, yMax, vMax);
}
};
for (let i = 0; i < hDst; i++) {
for (let j = 0; j < wDst; j++) {
const posDst = (i * wDst + j) * 4;
// x & y in src coordinates
const x = (j * wSrc) / wDst;
const xMin = Math.floor(x);
const xMax = Math.min(Math.ceil(x), wSrc - 1);
const y = (i * hSrc) / hDst;
const yMin = Math.floor(y);
const yMax = Math.min(Math.ceil(y), hSrc - 1);
assign(posDst, 0, x, xMin, xMax, y, yMin, yMax);
assign(posDst, 1, x, xMin, xMax, y, yMin, yMax);
assign(posDst, 2, x, xMin, xMax, y, yMin, yMax);
assign(posDst, 3, x, xMin, xMax, y, yMin, yMax);
}
}
},
_interpolate2D(src, dst, options, interpolate) {
const bufSrc = src.data;
const bufDst = dst.data;
const wSrc = src.width;
const hSrc = src.height;
const wDst = dst.width;
const hDst = dst.height;
// when dst smaller than src/2, interpolate first to a multiple between 0.5 and 1.0 src, then sum squares
const wM = Math.max(1, Math.floor(wSrc / wDst));
const wDst2 = wDst * wM;
const hM = Math.max(1, Math.floor(hSrc / hDst));
const hDst2 = hDst * hM;
// ===========================================================
// Pass 1 - interpolate rows
// buf1 has width of dst2 and height of src
const buf1 = Buffer.alloc(wDst2 * hSrc * 4);
for (let i = 0; i < hSrc; i++) {
for (let j = 0; j < wDst2; j++) {
// i in src coords, j in dst coords
// calculate x in src coords
// this interpolation requires 4 sample points and the two inner ones must be real
// the outer points can be fudged for the edges.
// therefore (wSrc-1)/wDst2
const x = (j * (wSrc - 1)) / wDst2;
const xPos = Math.floor(x);
const t = x - xPos;
const srcPos = (i * wSrc + xPos) * 4;
const buf1Pos = (i * wDst2 + j) * 4;
for (let k = 0; k < 4; k++) {
const kPos = srcPos + k;
const x0 =
xPos > 0 ? bufSrc[kPos - 4] : 2 * bufSrc[kPos] - bufSrc[kPos + 4];
const x1 = bufSrc[kPos];
const x2 = bufSrc[kPos + 4];
const x3 =
xPos < wSrc - 2
? bufSrc[kPos + 8]
: 2 * bufSrc[kPos + 4] - bufSrc[kPos];
buf1[buf1Pos + k] = interpolate(x0, x1, x2, x3, t);
}
}
}
// this._writeFile(wDst2, hSrc, buf1, "out/buf1.jpg");
// ===========================================================
// Pass 2 - interpolate columns
// buf2 has width and height of dst2
const buf2 = Buffer.alloc(wDst2 * hDst2 * 4);
for (let i = 0; i < hDst2; i++) {
for (let j = 0; j < wDst2; j++) {
// i&j in dst2 coords
// calculate y in buf1 coords
// this interpolation requires 4 sample points and the two inner ones must be real
// the outer points can be fudged for the edges.
// therefore (hSrc-1)/hDst2
const y = (i * (hSrc - 1)) / hDst2;
const yPos = Math.floor(y);
const t = y - yPos;
const buf1Pos = (yPos * wDst2 + j) * 4;
const buf2Pos = (i * wDst2 + j) * 4;
for (let k = 0; k < 4; k++) {
const kPos = buf1Pos + k;
const y0 =
yPos > 0
? buf1[kPos - wDst2 * 4]
: 2 * buf1[kPos] - buf1[kPos + wDst2 * 4];
const y1 = buf1[kPos];
const y2 = buf1[kPos + wDst2 * 4];
const y3 =
yPos < hSrc - 2
? buf1[kPos + wDst2 * 8]
: 2 * buf1[kPos + wDst2 * 4] - buf1[kPos];
buf2[buf2Pos + k] = interpolate(y0, y1, y2, y3, t);
}
}
}
// this._writeFile(wDst2, hDst2, buf2, "out/buf2.jpg");
// ===========================================================
// Pass 3 - scale to dst
const m = wM * hM;
if (m > 1) {
for (let i = 0; i < hDst; i++) {
for (let j = 0; j < wDst; j++) {
// i&j in dst bounded coords
let r = 0;
let g = 0;
let b = 0;
let a = 0;
let realColors = 0;
for (let y = 0; y < hM; y++) {
const yPos = i * hM + y;
for (let x = 0; x < wM; x++) {
const xPos = j * wM + x;
const xyPos = (yPos * wDst2 + xPos) * 4;
const pixelAlpha = buf2[xyPos + 3];
if (pixelAlpha) {
r += buf2[xyPos];
g += buf2[xyPos + 1];
b += buf2[xyPos + 2];
realColors++;
}
a += pixelAlpha;
}
}
const pos = (i * wDst + j) * 4;
bufDst[pos] = realColors ? Math.round(r / realColors) : 0;
bufDst[pos + 1] = realColors ? Math.round(g / realColors) : 0;
bufDst[pos + 2] = realColors ? Math.round(b / realColors) : 0;
bufDst[pos + 3] = Math.round(a / m);
}
}
} else {
// replace dst buffer with buf2
dst.data = buf2;
}
},
bicubicInterpolation(src, dst, options) {
const interpolateCubic = function(x0, x1, x2, x3, t) {
const a0 = x3 - x2 - x0 + x1;
const a1 = x0 - x1 - a0;
const a2 = x2 - x0;
const a3 = x1;
return Math.max(
0,
Math.min(255, a0 * (t * t * t) + a1 * (t * t) + a2 * t + a3)
);
};
return this._interpolate2D(src, dst, options, interpolateCubic);
},
hermiteInterpolation(src, dst, options) {
const interpolateHermite = function(x0, x1, x2, x3, t) {
const c0 = x1;
const c1 = 0.5 * (x2 - x0);
const c2 = x0 - 2.5 * x1 + 2 * x2 - 0.5 * x3;
const c3 = 0.5 * (x3 - x0) + 1.5 * (x1 - x2);
return Math.max(
0,
Math.min(255, Math.round(((c3 * t + c2) * t + c1) * t + c0))
);
};
return this._interpolate2D(src, dst, options, interpolateHermite);
},
bezierInterpolation(src, dst, options) {
// between 2 points y(n), y(n+1), use next points out, y(n-1), y(n+2)
// to predict control points (a & b) to be placed at n+0.5
// ya(n) = y(n) + (y(n+1)-y(n-1))/4
// yb(n) = y(n+1) - (y(n+2)-y(n))/4
// then use std bezier to interpolate [n,n+1)
// 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
// note the 3* factor for the two control points
// for edge cases, can choose:
// y(-1) = y(0) - 2*(y(1)-y(0))
// y(w) = y(w-1) + 2*(y(w-1)-y(w-2))
// but can go with y(-1) = y(0) and y(w) = y(w-1)
const interpolateBezier = function(x0, x1, x2, x3, t) {
// x1, x2 are the knots, use x0 and x3 to calculate control points
const cp1 = x1 + (x2 - x0) / 4;
const cp2 = x2 - (x3 - x1) / 4;
const nt = 1 - t;
const c0 = x1 * nt * nt * nt;
const c1 = 3 * cp1 * nt * nt * t;
const c2 = 3 * cp2 * nt * t * t;
const c3 = x2 * t * t * t;
return Math.max(0, Math.min(255, Math.round(c0 + c1 + c2 + c3)));
};
return this._interpolate2D(src, dst, options, interpolateBezier);
}
};

View File

@@ -0,0 +1,613 @@
import { Jimp, mkJGD, hashForEach } from '@jimp/test-utils';
import configure from '@jimp/custom';
import resize from '../src';
const jimp = configure({ plugins: [resize] }, Jimp);
describe('Resize images', () => {
const testImages = [
{
title: 'max contrast 8x8',
src: jimp.read(
mkJGD(
'■■■■□□□□',
'■■■■□□□□',
'■■■■□□□□',
'■■■■□□□□',
'□□□□■■■■',
'□□□□■■■■',
'□□□□■■■■',
'□□□□■■■■'
)
),
results: {
'default 4x4': mkJGD('■■□□', '■■□□', '□□■■', '□□■■'),
'NEAREST_NEIGHBOR 4x4': mkJGD('■■□□', '■■□□', '□□■■', '□□■■'),
'BILINEAR 4x4': mkJGD('■■□□', '■■□□', '□□■■', '□□■■'),
'BICUBIC 4x4': {
width: 4,
height: 4,
data: [
0x000000ff,
0x000000ff,
0xbfbfbfff,
0xffffffff,
0x000000ff,
0x000000ff,
0xbfbfbfff,
0xffffffff,
0xbfbfbfff,
0xbfbfbfff,
0x5f5f5fff,
0x404040ff,
0xffffffff,
0xffffffff,
0x404040ff,
0x000000ff
]
},
'HERMITE 4x4': {
width: 4,
height: 4,
data: [
0x000000ff,
0x000000ff,
0xc0c0c0ff,
0xffffffff,
0x000000ff,
0x000000ff,
0xc0c0c0ff,
0xffffffff,
0xc0c0c0ff,
0xc0c0c0ff,
0x606060ff,
0x404040ff,
0xffffffff,
0xffffffff,
0x404040ff,
0x000000ff
]
},
'BEZIER 4x4': {
width: 4,
height: 4,
data: [
0x000000ff,
0x000000ff,
0xc0c0c0ff,
0xffffffff,
0x000000ff,
0x000000ff,
0xc0c0c0ff,
0xffffffff,
0xc0c0c0ff,
0xc0c0c0ff,
0x606060ff,
0x404040ff,
0xffffffff,
0xffffffff,
0x404040ff,
0x000000ff
]
},
'default 5x2': mkJGD('■■▦□□', '□□▦■■'),
'NEAREST_NEIGHBOR 5x2': mkJGD('■■■□□', '□□□■■'),
'BILINEAR 5x2': mkJGD('■■3□□', '□□C■■'),
'BICUBIC 5x2': {
width: 5,
height: 2,
data: [
0x000000ff,
0x000000ff,
0x000000ff,
0xffffffff,
0xffffffff,
0xdfdfdfff,
0xdfdfdfff,
0xdfdfdfff,
0x202020ff,
0x202020ff
]
},
'HERMITE 5x2': {
width: 5,
height: 2,
data: [
0x000000ff,
0x000000ff,
0x000000ff,
0xffffffff,
0xffffffff,
0xdfdfdfff,
0xdfdfdfff,
0xdfdfdfff,
0x202020ff,
0x202020ff
]
},
'BEZIER 5x2': {
width: 5,
height: 2,
data: [
0x000000ff,
0x000000ff,
0x000000ff,
0xffffffff,
0xffffffff,
0xdfdfdfff,
0xdfdfdfff,
0xdfdfdfff,
0x202020ff,
0x202020ff
]
}
}
},
/**********************************************************************/
{
title: 'max contrast 12x12 with dots',
src: jimp.read(
mkJGD(
'■■■■■■□□□□□□',
'■■■■■■□□□□□□',
'■■■□■■□□■□□□',
'■■■■■■□□□□□□',
'■■■■■■□□□□□□',
'■■■■■■□□□□□□',
'□□□□□□■■■■■■',
'□□□□□□■■■■■■',
'□□□□□□■■■■■■',
'□□□■□□■■□■■■',
'□□□□□□■■■■■■',
'□□□□□□■■■■■■'
)
),
results: {
'default 6x6': mkJGD(
'■■■□□□',
'■▩■□▥□',
'■■■□□□',
'□□□■■■',
'□▥□■▩■',
'□□□■■■'
),
'NEAREST_NEIGHBOR 6x6': mkJGD(
'■■■□□□',
'■■■□■□',
'■■■□□□',
'□□□■■■',
'□□□■■■',
'□□□■■■'
),
'BILINEAR 6x6': mkJGD(
'■■■□□□',
'■■■□■□',
'■■■□□□',
'□□□■■■',
'□□□■■■',
'□□□■■■'
),
'BICUBIC 6x6': {
width: 6,
height: 6,
data: [
0x000000ff,
0x000000ff,
0x000000ff,
0xbfbfbfff,
0xffffffff,
0xffffffff,
0x000000ff,
0x474747ff,
0x202020ff,
0xbfbfbfff,
0x979797ff,
0xffffffff,
0x000000ff,
0x000000ff,
0x000000ff,
0xbfbfbfff,
0xffffffff,
0xffffffff,
0xbfbfbfff,
0xbfbfbfff,
0xbfbfbfff,
0x5f5f5fff,
0x404040ff,
0x404040ff,
0xffffffff,
0xeeeeeeff,
0xf7f7f7ff,
0x404040ff,
0x181818ff,
0x000000ff,
0xffffffff,
0xc9c9c9ff,
0xe6e6e6ff,
0x404040ff,
0x4e4e4eff,
0x000000ff
]
},
'HERMITE 6x6': {
width: 6,
height: 6,
data: [
0x000000ff,
0x000000ff,
0x000000ff,
0xc0c0c0ff,
0xffffffff,
0xffffffff,
0x000000ff,
0x404040ff,
0x191919ff,
0xc0c0c0ff,
0xa6a6a6ff,
0xffffffff,
0x000000ff,
0x000000ff,
0x000000ff,
0xc0c0c0ff,
0xffffffff,
0xffffffff,
0xc0c0c0ff,
0xc0c0c0ff,
0xc0c0c0ff,
0x606060ff,
0x404040ff,
0x404040ff,
0xffffffff,
0xf3f3f3ff,
0xfafafaff,
0x404040ff,
0x111111ff,
0x000000ff,
0xffffffff,
0xcbcbcbff,
0xebebebff,
0x404040ff,
0x484848ff,
0x000000ff
]
},
'BEZIER 6x6': {
width: 6,
height: 6,
data: [
0x000000ff,
0x000000ff,
0x000000ff,
0xc0c0c0ff,
0xffffffff,
0xffffffff,
0x000000ff,
0x444444ff,
0x1d1d1dff,
0xc0c0c0ff,
0x9f9f9fff,
0xffffffff,
0x000000ff,
0x000000ff,
0x000000ff,
0xc0c0c0ff,
0xffffffff,
0xffffffff,
0xc0c0c0ff,
0xc0c0c0ff,
0xc0c0c0ff,
0x606060ff,
0x404040ff,
0x404040ff,
0xffffffff,
0xf0f0f0ff,
0xf9f9f9ff,
0x404040ff,
0x151515ff,
0x000000ff,
0xffffffff,
0xcacacaff,
0xe9e9e9ff,
0x404040ff,
0x4b4b4bff,
0x000000ff
]
}
}
},
/**********************************************************************/
{
title: 'mutch contrast 4x4',
src: jimp.read(mkJGD('▩▩▥▥', '▩▩▥▥', '▥▥▩▩', '▥▥▩▩')),
results: {
'default 6x6': {
width: 6,
height: 6,
data: [
0x404040ff,
0x404040ff,
0x404040ff,
0x959595ff,
0xbfbfbfff,
0xbfbfbfff,
0x404040ff,
0x404040ff,
0x404040ff,
0x959595ff,
0xbfbfbfff,
0xbfbfbfff,
0x404040ff,
0x404040ff,
0x404040ff,
0x959595ff,
0xbfbfbfff,
0xbfbfbfff,
0x959595ff,
0x959595ff,
0x959595ff,
0x787878ff,
0x6a6a6aff,
0x6a6a6aff,
0xbfbfbfff,
0xbfbfbfff,
0xbfbfbfff,
0x6a6a6aff,
0x404040ff,
0x404040ff,
0xbfbfbfff,
0xbfbfbfff,
0xbfbfbfff,
0x6a6a6aff,
0x404040ff,
0x404040ff
]
},
'NEAREST_NEIGHBOR 6x6': {
width: 6,
height: 6,
data: [
0x404040ff,
0x404040ff,
0x404040ff,
0xbfbfbfff,
0xbfbfbfff,
0xbfbfbfff,
0x404040ff,
0x404040ff,
0x404040ff,
0xbfbfbfff,
0xbfbfbfff,
0xbfbfbfff,
0x404040ff,
0x404040ff,
0x404040ff,
0xbfbfbfff,
0xbfbfbfff,
0xbfbfbfff,
0xbfbfbfff,
0xbfbfbfff,
0xbfbfbfff,
0x404040ff,
0x404040ff,
0x404040ff,
0xbfbfbfff,
0xbfbfbfff,
0xbfbfbfff,
0x404040ff,
0x404040ff,
0x404040ff,
0xbfbfbfff,
0xbfbfbfff,
0xbfbfbfff,
0x404040ff,
0x404040ff,
0x404040ff
]
},
'BILINEAR 6x6': {
width: 6,
height: 6,
data: [
0x404040ff,
0x404040ff,
0x6a6a6aff,
0xbfbfbfff,
0xbfbfbfff,
0xbfbfbfff,
0x404040ff,
0x404040ff,
0x6a6a6aff,
0xbfbfbfff,
0xbfbfbfff,
0xbfbfbfff,
0x6a6a6aff,
0x6a6a6aff,
0x787878ff,
0x959595ff,
0x959595ff,
0x959595ff,
0xbfbfbfff,
0xbfbfbfff,
0x959595ff,
0x404040ff,
0x404040ff,
0x404040ff,
0xbfbfbfff,
0xbfbfbfff,
0x959595ff,
0x404040ff,
0x404040ff,
0x404040ff,
0xbfbfbfff,
0xbfbfbfff,
0x959595ff,
0x404040ff,
0x404040ff,
0x404040ff
]
},
'BICUBIC 6x6': {
width: 6,
height: 6,
data: [
0x404040ff,
0x303030ff,
0x404040ff,
0x7f7f7fff,
0xbfbfbfff,
0xcececeff,
0x303030ff,
0x1c1c1cff,
0x303030ff,
0x7f7f7fff,
0xcececeff,
0xe1e1e1ff,
0x404040ff,
0x303030ff,
0x404040ff,
0x7f7f7fff,
0xbfbfbfff,
0xcececeff,
0x7f7f7fff,
0x7f7f7fff,
0x7f7f7fff,
0x7f7f7fff,
0x7f7f7fff,
0x7f7f7fff,
0xbfbfbfff,
0xcececeff,
0xbfbfbfff,
0x7f7f7fff,
0x404040ff,
0x303030ff,
0xcececeff,
0xe1e1e1ff,
0xcececeff,
0x7f7f7fff,
0x303030ff,
0x1c1c1cff
]
},
'HERMITE 6x6': {
width: 6,
height: 6,
data: [
0x404040ff,
0x383838ff,
0x404040ff,
0x808080ff,
0xbfbfbfff,
0xc7c7c7ff,
0x383838ff,
0x2f2f2fff,
0x383838ff,
0x808080ff,
0xc7c7c7ff,
0xd0d0d0ff,
0x404040ff,
0x383838ff,
0x404040ff,
0x808080ff,
0xbfbfbfff,
0xc7c7c7ff,
0x808080ff,
0x808080ff,
0x808080ff,
0x808080ff,
0x808080ff,
0x808080ff,
0xbfbfbfff,
0xc7c7c7ff,
0xbfbfbfff,
0x808080ff,
0x404040ff,
0x383838ff,
0xc7c7c7ff,
0xd0d0d0ff,
0xc7c7c7ff,
0x808080ff,
0x383838ff,
0x2f2f2fff
]
},
'BEZIER 6x6': {
width: 6,
height: 6,
data: [
0x404040ff,
0x343434ff,
0x404040ff,
0x808080ff,
0xbfbfbfff,
0xcbcbcbff,
0x343434ff,
0x262626ff,
0x343434ff,
0x808080ff,
0xcbcbcbff,
0xd9d9d9ff,
0x404040ff,
0x343434ff,
0x404040ff,
0x808080ff,
0xbfbfbfff,
0xcbcbcbff,
0x808080ff,
0x808080ff,
0x808080ff,
0x808080ff,
0x808080ff,
0x808080ff,
0xbfbfbfff,
0xcbcbcbff,
0xbfbfbfff,
0x808080ff,
0x404040ff,
0x343434ff,
0xcbcbcbff,
0xd9d9d9ff,
0xcbcbcbff,
0x808080ff,
0x343434ff,
0x262626ff
]
}
}
}
];
before(done => {
const srcImgs = testImages.map(test => test.src);
Promise.all(srcImgs)
.then(imgsJimp => {
for (let i = 0; i < imgsJimp.length; i++) {
testImages[i].src = imgsJimp[i];
}
done();
})
.catch(done);
});
function testEach(test) {
describe(test.title, () => {
hashForEach(test.results, (expectedTitle, expectedJgd) => {
const mode = Jimp['RESIZE_' + expectedTitle.split(' ')[0]];
const size = expectedTitle
.split(' ')[1]
.split('x')
.map(n => parseInt(n, 10));
it('to ' + expectedTitle, () => {
test.src
.clone()
.resize(size[0], size[1], mode)
.getJGDSync()
.should.be.sameJGD(expectedJgd);
});
});
});
}
testImages.forEach(testEach);
});