Path: blob/master/node_modules/@jimp/plugin-resize/dist/modules/resize.js
1129 views
"use strict";12// JavaScript Image Resizer (c) 2012 - Grant Galitz3// Released to public domain 29 July 2013: https://github.com/grantgalitz/JS-Image-Resizer/issues/44function Resize(widthOriginal, heightOriginal, targetWidth, targetHeight, blendAlpha, interpolationPass, resizeCallback) {5this.widthOriginal = Math.abs(Math.floor(widthOriginal) || 0);6this.heightOriginal = Math.abs(Math.floor(heightOriginal) || 0);7this.targetWidth = Math.abs(Math.floor(targetWidth) || 0);8this.targetHeight = Math.abs(Math.floor(targetHeight) || 0);9this.colorChannels = blendAlpha ? 4 : 3;10this.interpolationPass = Boolean(interpolationPass);11this.resizeCallback = typeof resizeCallback === 'function' ? resizeCallback : function () {};12this.targetWidthMultipliedByChannels = this.targetWidth * this.colorChannels;13this.originalWidthMultipliedByChannels = this.widthOriginal * this.colorChannels;14this.originalHeightMultipliedByChannels = this.heightOriginal * this.colorChannels;15this.widthPassResultSize = this.targetWidthMultipliedByChannels * this.heightOriginal;16this.finalResultSize = this.targetWidthMultipliedByChannels * this.targetHeight;17this.initialize();18}1920Resize.prototype.initialize = function () {21// Perform some checks:22if (this.widthOriginal > 0 && this.heightOriginal > 0 && this.targetWidth > 0 && this.targetHeight > 0) {23this.configurePasses();24} else {25throw new Error('Invalid settings specified for the resizer.');26}27};2829Resize.prototype.configurePasses = function () {30if (this.widthOriginal === this.targetWidth) {31// Bypass the width resizer pass:32this.resizeWidth = this.bypassResizer;33} else {34// Setup the width resizer pass:35this.ratioWeightWidthPass = this.widthOriginal / this.targetWidth;3637if (this.ratioWeightWidthPass < 1 && this.interpolationPass) {38this.initializeFirstPassBuffers(true);39this.resizeWidth = this.colorChannels === 4 ? this.resizeWidthInterpolatedRGBA : this.resizeWidthInterpolatedRGB;40} else {41this.initializeFirstPassBuffers(false);42this.resizeWidth = this.colorChannels === 4 ? this.resizeWidthRGBA : this.resizeWidthRGB;43}44}4546if (this.heightOriginal === this.targetHeight) {47// Bypass the height resizer pass:48this.resizeHeight = this.bypassResizer;49} else {50// Setup the height resizer pass:51this.ratioWeightHeightPass = this.heightOriginal / this.targetHeight;5253if (this.ratioWeightHeightPass < 1 && this.interpolationPass) {54this.initializeSecondPassBuffers(true);55this.resizeHeight = this.resizeHeightInterpolated;56} else {57this.initializeSecondPassBuffers(false);58this.resizeHeight = this.colorChannels === 4 ? this.resizeHeightRGBA : this.resizeHeightRGB;59}60}61};6263Resize.prototype._resizeWidthInterpolatedRGBChannels = function (buffer, fourthChannel) {64var channelsNum = fourthChannel ? 4 : 3;65var ratioWeight = this.ratioWeightWidthPass;66var outputBuffer = this.widthBuffer;67var weight = 0;68var finalOffset = 0;69var pixelOffset = 0;70var firstWeight = 0;71var secondWeight = 0;72var targetPosition; // Handle for only one interpolation input being valid for start calculation:7374for (targetPosition = 0; weight < 1 / 3; targetPosition += channelsNum, weight += ratioWeight) {75for (finalOffset = targetPosition, pixelOffset = 0; finalOffset < this.widthPassResultSize; pixelOffset += this.originalWidthMultipliedByChannels, finalOffset += this.targetWidthMultipliedByChannels) {76outputBuffer[finalOffset] = buffer[pixelOffset];77outputBuffer[finalOffset + 1] = buffer[pixelOffset + 1];78outputBuffer[finalOffset + 2] = buffer[pixelOffset + 2];79if (fourthChannel) outputBuffer[finalOffset + 3] = buffer[pixelOffset + 3];80}81} // Adjust for overshoot of the last pass's counter:828384weight -= 1 / 3;85var interpolationWidthSourceReadStop;8687for (interpolationWidthSourceReadStop = this.widthOriginal - 1; weight < interpolationWidthSourceReadStop; targetPosition += channelsNum, weight += ratioWeight) {88// Calculate weightings:89secondWeight = weight % 1;90firstWeight = 1 - secondWeight; // Interpolate:9192for (finalOffset = targetPosition, pixelOffset = Math.floor(weight) * channelsNum; finalOffset < this.widthPassResultSize; pixelOffset += this.originalWidthMultipliedByChannels, finalOffset += this.targetWidthMultipliedByChannels) {93outputBuffer[finalOffset + 0] = buffer[pixelOffset + 0] * firstWeight + buffer[pixelOffset + channelsNum + 0] * secondWeight;94outputBuffer[finalOffset + 1] = buffer[pixelOffset + 1] * firstWeight + buffer[pixelOffset + channelsNum + 1] * secondWeight;95outputBuffer[finalOffset + 2] = buffer[pixelOffset + 2] * firstWeight + buffer[pixelOffset + channelsNum + 2] * secondWeight;96if (fourthChannel) outputBuffer[finalOffset + 3] = buffer[pixelOffset + 3] * firstWeight + buffer[pixelOffset + channelsNum + 3] * secondWeight;97}98} // Handle for only one interpolation input being valid for end calculation:99100101for (interpolationWidthSourceReadStop = this.originalWidthMultipliedByChannels - channelsNum; targetPosition < this.targetWidthMultipliedByChannels; targetPosition += channelsNum) {102for (finalOffset = targetPosition, pixelOffset = interpolationWidthSourceReadStop; finalOffset < this.widthPassResultSize; pixelOffset += this.originalWidthMultipliedByChannels, finalOffset += this.targetWidthMultipliedByChannels) {103outputBuffer[finalOffset] = buffer[pixelOffset];104outputBuffer[finalOffset + 1] = buffer[pixelOffset + 1];105outputBuffer[finalOffset + 2] = buffer[pixelOffset + 2];106if (fourthChannel) outputBuffer[finalOffset + 3] = buffer[pixelOffset + 3];107}108}109110return outputBuffer;111};112113Resize.prototype._resizeWidthRGBChannels = function (buffer, fourthChannel) {114var channelsNum = fourthChannel ? 4 : 3;115var ratioWeight = this.ratioWeightWidthPass;116var ratioWeightDivisor = 1 / ratioWeight;117var nextLineOffsetOriginalWidth = this.originalWidthMultipliedByChannels - channelsNum + 1;118var nextLineOffsetTargetWidth = this.targetWidthMultipliedByChannels - channelsNum + 1;119var output = this.outputWidthWorkBench;120var outputBuffer = this.widthBuffer;121var trustworthyColorsCount = this.outputWidthWorkBenchOpaquePixelsCount;122var weight = 0;123var amountToNext = 0;124var actualPosition = 0;125var currentPosition = 0;126var line = 0;127var pixelOffset = 0;128var outputOffset = 0;129var multiplier = 1;130var r = 0;131var g = 0;132var b = 0;133var a = 0;134135do {136for (line = 0; line < this.originalHeightMultipliedByChannels;) {137output[line++] = 0;138output[line++] = 0;139output[line++] = 0;140141if (fourthChannel) {142output[line++] = 0;143trustworthyColorsCount[line / channelsNum - 1] = 0;144}145}146147weight = ratioWeight;148149do {150amountToNext = 1 + actualPosition - currentPosition;151multiplier = Math.min(weight, amountToNext);152153for (line = 0, pixelOffset = actualPosition; line < this.originalHeightMultipliedByChannels; pixelOffset += nextLineOffsetOriginalWidth) {154r = buffer[pixelOffset];155g = buffer[++pixelOffset];156b = buffer[++pixelOffset];157a = fourthChannel ? buffer[++pixelOffset] : 255; // Ignore RGB values if pixel is completely transparent158159output[line++] += (a ? r : 0) * multiplier;160output[line++] += (a ? g : 0) * multiplier;161output[line++] += (a ? b : 0) * multiplier;162163if (fourthChannel) {164output[line++] += a * multiplier;165trustworthyColorsCount[line / channelsNum - 1] += a ? multiplier : 0;166}167}168169if (weight >= amountToNext) {170actualPosition += channelsNum;171currentPosition = actualPosition;172weight -= amountToNext;173} else {174currentPosition += weight;175break;176}177} while (weight > 0 && actualPosition < this.originalWidthMultipliedByChannels);178179for (line = 0, pixelOffset = outputOffset; line < this.originalHeightMultipliedByChannels; pixelOffset += nextLineOffsetTargetWidth) {180weight = fourthChannel ? trustworthyColorsCount[line / channelsNum] : 1;181multiplier = fourthChannel ? weight ? 1 / weight : 0 : ratioWeightDivisor;182outputBuffer[pixelOffset] = output[line++] * multiplier;183outputBuffer[++pixelOffset] = output[line++] * multiplier;184outputBuffer[++pixelOffset] = output[line++] * multiplier;185if (fourthChannel) outputBuffer[++pixelOffset] = output[line++] * ratioWeightDivisor;186}187188outputOffset += channelsNum;189} while (outputOffset < this.targetWidthMultipliedByChannels);190191return outputBuffer;192};193194Resize.prototype._resizeHeightRGBChannels = function (buffer, fourthChannel) {195var ratioWeight = this.ratioWeightHeightPass;196var ratioWeightDivisor = 1 / ratioWeight;197var output = this.outputHeightWorkBench;198var outputBuffer = this.heightBuffer;199var trustworthyColorsCount = this.outputHeightWorkBenchOpaquePixelsCount;200var weight = 0;201var amountToNext = 0;202var actualPosition = 0;203var currentPosition = 0;204var pixelOffset = 0;205var outputOffset = 0;206var caret = 0;207var multiplier = 1;208var r = 0;209var g = 0;210var b = 0;211var a = 0;212213do {214for (pixelOffset = 0; pixelOffset < this.targetWidthMultipliedByChannels;) {215output[pixelOffset++] = 0;216output[pixelOffset++] = 0;217output[pixelOffset++] = 0;218219if (fourthChannel) {220output[pixelOffset++] = 0;221trustworthyColorsCount[pixelOffset / 4 - 1] = 0;222}223}224225weight = ratioWeight;226227do {228amountToNext = 1 + actualPosition - currentPosition;229multiplier = Math.min(weight, amountToNext);230caret = actualPosition;231232for (pixelOffset = 0; pixelOffset < this.targetWidthMultipliedByChannels;) {233r = buffer[caret++];234g = buffer[caret++];235b = buffer[caret++];236a = fourthChannel ? buffer[caret++] : 255; // Ignore RGB values if pixel is completely transparent237238output[pixelOffset++] += (a ? r : 0) * multiplier;239output[pixelOffset++] += (a ? g : 0) * multiplier;240output[pixelOffset++] += (a ? b : 0) * multiplier;241242if (fourthChannel) {243output[pixelOffset++] += a * multiplier;244trustworthyColorsCount[pixelOffset / 4 - 1] += a ? multiplier : 0;245}246}247248if (weight >= amountToNext) {249actualPosition = caret;250currentPosition = actualPosition;251weight -= amountToNext;252} else {253currentPosition += weight;254break;255}256} while (weight > 0 && actualPosition < this.widthPassResultSize);257258for (pixelOffset = 0; pixelOffset < this.targetWidthMultipliedByChannels;) {259weight = fourthChannel ? trustworthyColorsCount[pixelOffset / 4] : 1;260multiplier = fourthChannel ? weight ? 1 / weight : 0 : ratioWeightDivisor;261outputBuffer[outputOffset++] = Math.round(output[pixelOffset++] * multiplier);262outputBuffer[outputOffset++] = Math.round(output[pixelOffset++] * multiplier);263outputBuffer[outputOffset++] = Math.round(output[pixelOffset++] * multiplier);264265if (fourthChannel) {266outputBuffer[outputOffset++] = Math.round(output[pixelOffset++] * ratioWeightDivisor);267}268}269} while (outputOffset < this.finalResultSize);270271return outputBuffer;272};273274Resize.prototype.resizeWidthInterpolatedRGB = function (buffer) {275return this._resizeWidthInterpolatedRGBChannels(buffer, false);276};277278Resize.prototype.resizeWidthInterpolatedRGBA = function (buffer) {279return this._resizeWidthInterpolatedRGBChannels(buffer, true);280};281282Resize.prototype.resizeWidthRGB = function (buffer) {283return this._resizeWidthRGBChannels(buffer, false);284};285286Resize.prototype.resizeWidthRGBA = function (buffer) {287return this._resizeWidthRGBChannels(buffer, true);288};289290Resize.prototype.resizeHeightInterpolated = function (buffer) {291var ratioWeight = this.ratioWeightHeightPass;292var outputBuffer = this.heightBuffer;293var weight = 0;294var finalOffset = 0;295var pixelOffset = 0;296var pixelOffsetAccumulated = 0;297var pixelOffsetAccumulated2 = 0;298var firstWeight = 0;299var secondWeight = 0;300var interpolationHeightSourceReadStop; // Handle for only one interpolation input being valid for start calculation:301302for (; weight < 1 / 3; weight += ratioWeight) {303for (pixelOffset = 0; pixelOffset < this.targetWidthMultipliedByChannels;) {304outputBuffer[finalOffset++] = Math.round(buffer[pixelOffset++]);305}306} // Adjust for overshoot of the last pass's counter:307308309weight -= 1 / 3;310311for (interpolationHeightSourceReadStop = this.heightOriginal - 1; weight < interpolationHeightSourceReadStop; weight += ratioWeight) {312// Calculate weightings:313secondWeight = weight % 1;314firstWeight = 1 - secondWeight; // Interpolate:315316pixelOffsetAccumulated = Math.floor(weight) * this.targetWidthMultipliedByChannels;317pixelOffsetAccumulated2 = pixelOffsetAccumulated + this.targetWidthMultipliedByChannels;318319for (pixelOffset = 0; pixelOffset < this.targetWidthMultipliedByChannels; ++pixelOffset) {320outputBuffer[finalOffset++] = Math.round(buffer[pixelOffsetAccumulated++] * firstWeight + buffer[pixelOffsetAccumulated2++] * secondWeight);321}322} // Handle for only one interpolation input being valid for end calculation:323324325while (finalOffset < this.finalResultSize) {326for (pixelOffset = 0, pixelOffsetAccumulated = interpolationHeightSourceReadStop * this.targetWidthMultipliedByChannels; pixelOffset < this.targetWidthMultipliedByChannels; ++pixelOffset) {327outputBuffer[finalOffset++] = Math.round(buffer[pixelOffsetAccumulated++]);328}329}330331return outputBuffer;332};333334Resize.prototype.resizeHeightRGB = function (buffer) {335return this._resizeHeightRGBChannels(buffer, false);336};337338Resize.prototype.resizeHeightRGBA = function (buffer) {339return this._resizeHeightRGBChannels(buffer, true);340};341342Resize.prototype.resize = function (buffer) {343this.resizeCallback(this.resizeHeight(this.resizeWidth(buffer)));344};345346Resize.prototype.bypassResizer = function (buffer) {347// Just return the buffer passed:348return buffer;349};350351Resize.prototype.initializeFirstPassBuffers = function (BILINEARAlgo) {352// Initialize the internal width pass buffers:353this.widthBuffer = this.generateFloatBuffer(this.widthPassResultSize);354355if (!BILINEARAlgo) {356this.outputWidthWorkBench = this.generateFloatBuffer(this.originalHeightMultipliedByChannels);357358if (this.colorChannels > 3) {359this.outputWidthWorkBenchOpaquePixelsCount = this.generateFloat64Buffer(this.heightOriginal);360}361}362};363364Resize.prototype.initializeSecondPassBuffers = function (BILINEARAlgo) {365// Initialize the internal height pass buffers:366this.heightBuffer = this.generateUint8Buffer(this.finalResultSize);367368if (!BILINEARAlgo) {369this.outputHeightWorkBench = this.generateFloatBuffer(this.targetWidthMultipliedByChannels);370371if (this.colorChannels > 3) {372this.outputHeightWorkBenchOpaquePixelsCount = this.generateFloat64Buffer(this.targetWidth);373}374}375};376377Resize.prototype.generateFloatBuffer = function (bufferLength) {378// Generate a float32 typed array buffer:379try {380return new Float32Array(bufferLength);381} catch (error) {382return [];383}384};385386Resize.prototype.generateFloat64Buffer = function (bufferLength) {387// Generate a float64 typed array buffer:388try {389return new Float64Array(bufferLength);390} catch (error) {391return [];392}393};394395Resize.prototype.generateUint8Buffer = function (bufferLength) {396// Generate a uint8 typed array buffer:397try {398return new Uint8Array(bufferLength);399} catch (error) {400return [];401}402};403404module.exports = Resize;405//# sourceMappingURL=resize.js.map406407