mirror of
https://github.com/publiclab/image-sequencer.git
synced 2025-12-11 19:00:00 +01:00
[GCI] Experimental GIF Manipulation (#1404)
* wasmSuccess * modules use wasmSuccess * modules use wasmSuccess * add the tooltip * add GIF support * fix imageDImensions function * fix inputCoordinatesParser function * show correct image dimensions * show correct image dimensions * don't allow save as PDF for GIFs * fix QR module * fix BlobAnalysis * fix Blur * fix Colorbar * fix Crop * fix crop defaults * fix DrawRectangle * fix EdgeDetect * fix FlipImage * fix Gradient * fix Invert * fix Overlay * fix Resize * fix Rotate * fix TextOverlay * fix parse input test * make GIFs work in nodejs * sample GIF test * small change * cleanup * cleanup * cleanup * small fix * small change * handle errors * proper error handling and fix tests * cleanup * try a fix * try another fix * fix module benchmarks * try more fixes * revert * try fixing the tests * fix overlay test * add the gif tests * remove unnecessary changes * fix tests * whoops * add some docs * inBrowser * fix all module tests Co-authored-by: Jeffrey Warren <jeff@unterbahn.com>
This commit is contained in:
committed by
Jeffrey Warren
parent
61b2d75383
commit
ea2069d7f6
14
README.md
14
README.md
@@ -599,3 +599,17 @@ let sequencer = ImageSequencer() // also for wasm mode i.e. default mode
|
|||||||
let sequencer = ImageSequencer({useWasm:false}) //for non-wasm mode
|
let sequencer = ImageSequencer({useWasm:false}) //for non-wasm mode
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Experimental GIF processing support
|
||||||
|
|
||||||
|
ImageSequencer currently can process GIFs but only for most of the modules. Every frame of the GIF is manipulated sequentially (parallel processing would be preferable in the future).
|
||||||
|
The final frames are then converted back to a GIF but in the process, the time duration of each frame is lost and defaults to `0.1s`.
|
||||||
|
|
||||||
|
Modules that do not work:
|
||||||
|
1. ColorBar (Will get fixed upon fixing overlay as this is a meta module which uses overlay)
|
||||||
|
2. FisheyeGL
|
||||||
|
4. Overlay
|
||||||
|
5. Text Overlay (Almost fixed)
|
||||||
|
6. Blend
|
||||||
|
7. Histogram
|
||||||
|
8. WebGL Distort
|
||||||
@@ -270,23 +270,26 @@ window.onload = function () {
|
|||||||
* @param {string} imageDataURL - The data URL for the image.
|
* @param {string} imageDataURL - The data URL for the image.
|
||||||
*/
|
*/
|
||||||
function savePDF(imageDataURL) {
|
function savePDF(imageDataURL) {
|
||||||
sequencer.getImageDimensions(imageDataURL, function(dimensions) {
|
sequencer.getImageDimensions(imageDataURL, function(dimensions, isGIF) {
|
||||||
// Get the dimensions of the image.
|
if (!isGIF) {
|
||||||
let pageWidth = dimensions.width;
|
// Get the dimensions of the image.
|
||||||
let pageHeight = dimensions.height;
|
let pageWidth = dimensions.width;
|
||||||
|
let pageHeight = dimensions.height;
|
||||||
|
|
||||||
// Create a new pdf with the same dimensions as the image.
|
// Create a new pdf with the same dimensions as the image.
|
||||||
const pdf = new jsPDF({
|
const pdf = new jsPDF({
|
||||||
orientation: pageHeight > pageWidth ? 'portrait' : 'landscape',
|
orientation: pageHeight > pageWidth ? 'portrait' : 'landscape',
|
||||||
unit: 'px',
|
unit: 'px',
|
||||||
format: [pageHeight, pageWidth]
|
format: [pageHeight, pageWidth]
|
||||||
});
|
});
|
||||||
|
|
||||||
// Add the image to the pdf with dimensions equal to the internal dimensions of the page.
|
// Add the image to the pdf with dimensions equal to the internal dimensions of the page.
|
||||||
pdf.addImage(imageDataURL, 0, 0, pdf.internal.pageSize.getWidth(), pdf.internal.pageSize.getHeight());
|
pdf.addImage(imageDataURL, 0, 0, pdf.internal.pageSize.getWidth(), pdf.internal.pageSize.getHeight());
|
||||||
|
|
||||||
// Save the pdf with the filename specified here:
|
// Save the pdf with the filename specified here:
|
||||||
pdf.save('index.pdf');
|
pdf.save('index.pdf');
|
||||||
|
}
|
||||||
|
else console.log('GIFs cannot be converted to PDF');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -356,14 +356,14 @@ function DefaultHtmlStepUi(_sequencer, options) {
|
|||||||
else $step('.wasm-tooltip').fadeOut();
|
else $step('.wasm-tooltip').fadeOut();
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* @description Updates Dimension of the image
|
* @description Updates Dimension of the image
|
||||||
* @param {Object} step - Current Step
|
* @param {Object} step - Current Step
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
function updateDimensions(step){
|
function updateDimensions(step){
|
||||||
_sequencer.getImageDimensions(step.imgElement.src, function (dim) {
|
_sequencer.getImageDimensions(step.imgElement.src, function (dim, isGIF) {
|
||||||
step.ui.querySelector('.' + step.name).attributes['data-original-title'].value = `<div style="text-align: center"><p>Image Width: ${dim.width}<br>Image Height: ${dim.height}</br></div>`;
|
step.ui.querySelector('.' + step.name).attributes['data-original-title'].value = `<div style="text-align: center"><p>Image Width: ${dim.width}<br>Image Height: ${dim.height}</br>${isGIF ? `Frames: ${dim.frames}` : ''}</div>`;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
18
package-lock.json
generated
18
package-lock.json
generated
@@ -10364,7 +10364,7 @@
|
|||||||
},
|
},
|
||||||
"load-json-file": {
|
"load-json-file": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz",
|
"resolved": "http://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz",
|
||||||
"integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=",
|
"integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=",
|
||||||
"requires": {
|
"requires": {
|
||||||
"graceful-fs": "^4.1.2",
|
"graceful-fs": "^4.1.2",
|
||||||
@@ -12466,6 +12466,14 @@
|
|||||||
"version": "2.1.2",
|
"version": "2.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||||
|
},
|
||||||
|
"ws": {
|
||||||
|
"version": "6.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz",
|
||||||
|
"integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==",
|
||||||
|
"requires": {
|
||||||
|
"async-limiter": "~1.0.0"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -15849,14 +15857,6 @@
|
|||||||
"signal-exit": "^3.0.2"
|
"signal-exit": "^3.0.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ws": {
|
|
||||||
"version": "6.2.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz",
|
|
||||||
"integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==",
|
|
||||||
"requires": {
|
|
||||||
"async-limiter": "~1.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"xhr-write-stream": {
|
"xhr-write-stream": {
|
||||||
"version": "0.1.2",
|
"version": "0.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/xhr-write-stream/-/xhr-write-stream-0.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/xhr-write-stream/-/xhr-write-stream-0.1.2.tgz",
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
"main": "src/ImageSequencer.js",
|
"main": "src/ImageSequencer.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"debug": "TEST=true node ./index.js -i ./examples/images/monarch.png -s invert",
|
"debug": "TEST=true node ./index.js -i ./examples/images/monarch.png -s invert",
|
||||||
"test": "TEST=true istanbul cover tape test/core/*.js test/core/ui/user-interface.js test/core/modules/*.js | tap-spec; node test/core/sequencer/benchmark.js; grunt tests; cat ./output/core-tests.js | tape-run --render=\"tap-spec\"",
|
"test": "TEST=true istanbul cover tape test/core/*.js test/core/ui/user-interface.js test/core/modules/*.js | tap-spec; node test/core/sequencer/benchmark.js | tap-spec; node test/core/gifs/gif-test.js | tap-spec; grunt tests; cat ./output/core-tests.js | tape-run --render=\"tap-spec\"",
|
||||||
"test-ui": "node node_modules/jasmine/bin/jasmine test/ui/spec/*.js",
|
"test-ui": "node node_modules/jasmine/bin/jasmine test/ui/spec/*.js",
|
||||||
"test-ui-2": "node ./node_modules/.bin/jest",
|
"test-ui-2": "node ./node_modules/.bin/jest",
|
||||||
"setup": "npm i && npm i -g grunt grunt-cli && grunt build $$ npm rebuild --build-from-source",
|
"setup": "npm i && npm i -g grunt grunt-cli && grunt build $$ npm rebuild --build-from-source",
|
||||||
|
|||||||
@@ -30,7 +30,8 @@ module.exports = {
|
|||||||
'grid-overlay': require('./modules/GridOverlay'),
|
'grid-overlay': require('./modules/GridOverlay'),
|
||||||
'import-image': require('./modules/ImportImage'),
|
'import-image': require('./modules/ImportImage'),
|
||||||
'minify-image': require('./modules/MinifyImage'),
|
'minify-image': require('./modules/MinifyImage'),
|
||||||
'invert': require('image-sequencer-invert'),
|
// 'invert': require('image-sequencer-invert'),
|
||||||
|
'invert': require('./modules/Invert'),
|
||||||
'ndvi': require('./modules/Ndvi'),
|
'ndvi': require('./modules/Ndvi'),
|
||||||
'ndvi-colormap': require('./modules/NdviColormap'),
|
'ndvi-colormap': require('./modules/NdviColormap'),
|
||||||
'noise-reduction': require('./modules/NoiseReduction'),
|
'noise-reduction': require('./modules/NoiseReduction'),
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
const _ = require('lodash');
|
||||||
module.exports = function AddQR(options, UI) {
|
module.exports = function AddQR(options, UI) {
|
||||||
|
|
||||||
var defaults = require('./../../util/getDefaults.js')(require('./info.json'));
|
var defaults = require('./../../util/getDefaults.js')(require('./info.json'));
|
||||||
@@ -13,34 +14,34 @@ module.exports = function AddQR(options, UI) {
|
|||||||
|
|
||||||
var step = this;
|
var step = this;
|
||||||
|
|
||||||
return getPixels(input.src, function(err, oldPixels) {
|
function changePixel(r, g, b, a) {
|
||||||
function changePixel(r, g, b, a) {
|
return [r, g, b, a];
|
||||||
return [r, g, b, a];
|
}
|
||||||
}
|
|
||||||
|
|
||||||
function extraManipulation(pixels, generateOutput) {
|
function extraManipulation(pixels, setRenderState, generateOutput) {
|
||||||
if (err) {
|
const oldPixels = _.cloneDeep(pixels);
|
||||||
console.log(err);
|
setRenderState(false); // Prevent rendering of final output image until extraManipulation completes.
|
||||||
return;
|
|
||||||
}
|
|
||||||
require('./QR')(options, pixels, oldPixels, generateOutput);
|
|
||||||
}
|
|
||||||
|
|
||||||
function output(image, datauri, mimetype, wasmSuccess) {
|
require('./QR')(options, pixels, oldPixels, () => {
|
||||||
step.output = { src: datauri, format: mimetype, wasmSuccess, useWasm: options.useWasm };
|
setRenderState(true); // Allow rendering in the callback.
|
||||||
}
|
generateOutput();
|
||||||
|
|
||||||
return require('../_nomodule/PixelManipulation.js')(input, {
|
|
||||||
output: output,
|
|
||||||
ui: options.step.ui,
|
|
||||||
changePixel: changePixel,
|
|
||||||
extraManipulation: extraManipulation,
|
|
||||||
format: input.format,
|
|
||||||
image: options.image,
|
|
||||||
inBrowser: options.inBrowser,
|
|
||||||
callback: callback,
|
|
||||||
useWasm:options.useWasm
|
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function output(image, datauri, mimetype, wasmSuccess) {
|
||||||
|
step.output = { src: datauri, format: mimetype, wasmSuccess, useWasm: options.useWasm };
|
||||||
|
}
|
||||||
|
|
||||||
|
return require('../_nomodule/PixelManipulation.js')(input, {
|
||||||
|
output: output,
|
||||||
|
ui: options.step.ui,
|
||||||
|
changePixel: changePixel,
|
||||||
|
extraManipulation: extraManipulation,
|
||||||
|
format: input.format,
|
||||||
|
image: options.image,
|
||||||
|
inBrowser: options.inBrowser,
|
||||||
|
callback: callback,
|
||||||
|
useWasm:options.useWasm
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,44 +1,37 @@
|
|||||||
module.exports = exports = function (options, pixels, oldPixels, callback) {
|
const pixelSetter = require('../../util/pixelSetter.js'),
|
||||||
const pixelSetter = require('../../util/pixelSetter.js');
|
getPixels = require('get-pixels'),
|
||||||
|
QRCode = require('qrcode');
|
||||||
|
module.exports = exports = function (options, pixels, oldPixels, cb) {
|
||||||
|
|
||||||
var QRCode = require('qrcode');
|
QRCode.toDataURL(options.qrCodeString, {width: options.size, scale: 1}, function (error, url) {
|
||||||
QRCode.toDataURL(options.qrCodeString, function (err, url) {
|
|
||||||
var getPixels = require('get-pixels');
|
|
||||||
getPixels(url, function (err, qrPixels) {
|
getPixels(url, function (err, qrPixels) {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.log('Bad image path', image);
|
console.log('get-pixels error: ', err);
|
||||||
}
|
}
|
||||||
|
|
||||||
var imagejs = require('imagejs');
|
const width = oldPixels.shape[0],
|
||||||
var bitmap = new imagejs.Bitmap({ width: qrPixels.shape[0], height: qrPixels.shape[1] });
|
|
||||||
bitmap._data.data = qrPixels.data;
|
|
||||||
var resized = bitmap.resize({
|
|
||||||
width: options.size, height: options.size,
|
|
||||||
algorithm: 'bicubicInterpolation'
|
|
||||||
});
|
|
||||||
|
|
||||||
qrPixels.data = resized._data.data;
|
|
||||||
qrPixels.shape = [options.size, options.size, 4];
|
|
||||||
qrPixels.stride[1] = 4 * options.size;
|
|
||||||
|
|
||||||
var width = oldPixels.shape[0],
|
|
||||||
height = oldPixels.shape[1];
|
height = oldPixels.shape[1];
|
||||||
var xe = width - options.size,
|
|
||||||
|
const xe = width - options.size, // Starting pixel coordinates
|
||||||
ye = height - options.size;
|
ye = height - options.size;
|
||||||
for (var m = 0; m < width; m++) {
|
|
||||||
for (var n = 0; n < height; n++) {
|
for (let x = xe; x < width; x++) {
|
||||||
if (m >= xe && n >= ye) {
|
for (let y = ye; y < height; y++) {
|
||||||
pixelSetter(m, n, [qrPixels.get(m - xe, n - ye, 0), qrPixels.get(m - xe, n - ye, 1), qrPixels.get(m - xe, n - ye, 2), qrPixels.get(m - xe, n - ye, 3)], pixels);
|
pixelSetter(
|
||||||
}
|
x,
|
||||||
|
y,
|
||||||
else {
|
[
|
||||||
pixelSetter(m, n, [oldPixels.get(m, n, 0), oldPixels.get(m, n, 1), oldPixels.get(m, n, 2), oldPixels.get(m, n, 3)], pixels);
|
qrPixels.get(x - xe, y - ye, 0),
|
||||||
}
|
qrPixels.get(x - xe, y - ye, 1),
|
||||||
|
qrPixels.get(x - xe, y - ye, 2),
|
||||||
|
qrPixels.get(x - xe, y - ye, 3)
|
||||||
|
],
|
||||||
|
pixels
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
callback();
|
|
||||||
|
|
||||||
|
if(cb) cb();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ module.exports = function Average(options, UI) {
|
|||||||
return require('../_nomodule/PixelManipulation.js')(input, {
|
return require('../_nomodule/PixelManipulation.js')(input, {
|
||||||
output: output,
|
output: output,
|
||||||
ui: options.step.ui,
|
ui: options.step.ui,
|
||||||
|
inBrowser: options.inBrowser,
|
||||||
extraManipulation: extraManipulation,
|
extraManipulation: extraManipulation,
|
||||||
format: input.format,
|
format: input.format,
|
||||||
image: options.image,
|
image: options.image,
|
||||||
|
|||||||
@@ -1,17 +1,10 @@
|
|||||||
module.exports = function(pixels, options, priorStep){
|
module.exports = function(pixels){
|
||||||
|
|
||||||
var $ = require('jquery'); // To make Blob-analysis work in Node
|
|
||||||
|
|
||||||
var img = $(priorStep.imgElement);
|
|
||||||
if(Object.keys(img).length === 0){
|
|
||||||
img = $(priorStep.options.step.imgElement);
|
|
||||||
}
|
|
||||||
|
|
||||||
var canvas = document.createElement('canvas');
|
var canvas = document.createElement('canvas');
|
||||||
canvas.width = pixels.shape[0];
|
canvas.width = pixels.shape[0];
|
||||||
canvas.height = pixels.shape[1];
|
canvas.height = pixels.shape[1];
|
||||||
var ctx = canvas.getContext('2d');
|
var ctx = canvas.getContext('2d');
|
||||||
ctx.drawImage(img[0], 0, 0);
|
ctx.putImageData(new ImageData(new Uint8ClampedArray(pixels.data), pixels.shape[0], pixels.shape[1]), 0, 0);
|
||||||
|
|
||||||
let imgData = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
let imgData = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -10,11 +10,9 @@ module.exports = function BlobAnalysis(options, UI){
|
|||||||
|
|
||||||
var step = this;
|
var step = this;
|
||||||
|
|
||||||
var priorStep = this.getStep(-1); // Get the previous step to process it
|
|
||||||
|
|
||||||
function extraManipulation(pixels){
|
function extraManipulation(pixels){
|
||||||
|
|
||||||
pixels = require('./BlobAnalysis')(pixels, options, priorStep);
|
pixels = require('./BlobAnalysis')(pixels);
|
||||||
return pixels;
|
return pixels;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,31 @@
|
|||||||
|
// Generates a 5x5 Gaussian kernel
|
||||||
|
function kernelGenerator(sigma = 1) {
|
||||||
|
|
||||||
|
let kernel = [],
|
||||||
|
sum = 0;
|
||||||
|
|
||||||
|
if (sigma == 0) sigma += 0.05;
|
||||||
|
|
||||||
|
const s = 2 * Math.pow(sigma, 2);
|
||||||
|
|
||||||
|
for (let y = -2; y <= 2; y++) {
|
||||||
|
kernel.push([]);
|
||||||
|
for (let x = -2; x <= 2; x++) {
|
||||||
|
let r = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
|
||||||
|
kernel[y + 2].push(Math.exp(-(r / s)));
|
||||||
|
sum += kernel[y + 2][x + 2];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let x = 0; x < 5; x++){
|
||||||
|
for (let y = 0; y < 5; y++){
|
||||||
|
kernel[y][x] = (kernel[y][x] / sum);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return kernel;
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = exports = function(pixels, blur) {
|
module.exports = exports = function(pixels, blur) {
|
||||||
const pixelSetter = require('../../util/pixelSetter.js');
|
const pixelSetter = require('../../util/pixelSetter.js');
|
||||||
|
|
||||||
@@ -34,32 +62,4 @@ module.exports = exports = function(pixels, blur) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return pixels;
|
return pixels;
|
||||||
|
|
||||||
// Generates a 5x5 Gaussian kernel.
|
|
||||||
function kernelGenerator(sigma = 1) {
|
|
||||||
|
|
||||||
let kernel = [],
|
|
||||||
sum = 0;
|
|
||||||
|
|
||||||
if (sigma == 0) sigma += 0.05;
|
|
||||||
|
|
||||||
const s = 2 * Math.pow(sigma, 2);
|
|
||||||
|
|
||||||
for (let y = -2; y <= 2; y++) {
|
|
||||||
kernel.push([]);
|
|
||||||
for (let x = -2; x <= 2; x++) {
|
|
||||||
let r = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
|
|
||||||
kernel[y + 2].push(Math.exp(-(r / s)));
|
|
||||||
sum += kernel[y + 2][x + 2];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let x = 0; x < 5; x++){
|
|
||||||
for (let y = 0; y < 5; y++){
|
|
||||||
kernel[y][x] = (kernel[y][x] / sum);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return kernel;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
@@ -27,6 +27,7 @@ module.exports = function Blur(options, UI) {
|
|||||||
return require('../_nomodule/PixelManipulation.js')(input, {
|
return require('../_nomodule/PixelManipulation.js')(input, {
|
||||||
output: output,
|
output: output,
|
||||||
ui: options.step.ui,
|
ui: options.step.ui,
|
||||||
|
inBrowser: options.inBrowser,
|
||||||
extraManipulation: extraManipulation,
|
extraManipulation: extraManipulation,
|
||||||
format: input.format,
|
format: input.format,
|
||||||
image: options.image,
|
image: options.image,
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ module.exports = require('../../util/createMetaModule.js')(
|
|||||||
return [
|
return [
|
||||||
{ 'name': 'gradient', 'options': {} },
|
{ 'name': 'gradient', 'options': {} },
|
||||||
{ 'name': 'colormap', 'options': { colormap: options.colormap || defaults.colormap } },
|
{ 'name': 'colormap', 'options': { colormap: options.colormap || defaults.colormap } },
|
||||||
{ 'name': 'crop', 'options': { 'y': 0, 'h': options.h || defaults.h } },
|
{ 'name': 'crop', 'options': { 'y': 0, 'w': '100%', 'h': options.h || defaults.h } },
|
||||||
{ 'name': 'overlay', 'options': { 'x': options.x || defaults.x, 'y': options.y || defaults.y, 'offset': -4 } }
|
{ 'name': 'overlay', 'options': { 'x': options.x || defaults.h, 'y': options.y || defaults.y, 'offset': -4 } }
|
||||||
];
|
];
|
||||||
}, {
|
}, {
|
||||||
infoJson: require('./info.json')
|
infoJson: require('./info.json')
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ module.exports = function Contrast(options, UI) {
|
|||||||
format: input.format,
|
format: input.format,
|
||||||
image: options.image,
|
image: options.image,
|
||||||
callback: callback,
|
callback: callback,
|
||||||
|
inBrowser: options.inBrowser,
|
||||||
useWasm:options.useWasm
|
useWasm:options.useWasm
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ module.exports = function Convolution(options, UI) {
|
|||||||
extraManipulation: extraManipulation,
|
extraManipulation: extraManipulation,
|
||||||
format: input.format,
|
format: input.format,
|
||||||
image: options.image,
|
image: options.image,
|
||||||
|
inBrowser: options.inBrowser,
|
||||||
callback: callback,
|
callback: callback,
|
||||||
useWasm:options.useWasm
|
useWasm:options.useWasm
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,61 +1,71 @@
|
|||||||
module.exports = function Crop(input, options, callback) {
|
const ndarray = require('ndarray'),
|
||||||
|
pixelSetter = require('../../util/pixelSetter'),
|
||||||
|
parseCornerCoordinateInputs = require('../../util/ParseInputCoordinates');
|
||||||
|
|
||||||
|
module.exports = function Crop(pixels, options, cb) {
|
||||||
var defaults = require('./../../util/getDefaults.js')(require('./info.json'));
|
var defaults = require('./../../util/getDefaults.js')(require('./info.json'));
|
||||||
var getPixels = require('get-pixels'),
|
options.x = options.x || defaults.x;
|
||||||
savePixels = require('save-pixels');
|
options.y = options.y || defaults.y;
|
||||||
|
|
||||||
options.x = parseInt(options.x) || defaults.x;
|
options.w = options.w || defaults.w;
|
||||||
options.y = parseInt(options.y) || defaults.y;
|
options.h = options.h || defaults.h;
|
||||||
|
|
||||||
getPixels(input.src, function(err, pixels){
|
options.backgroundColor = options.backgroundColor || defaults.backgroundColor;
|
||||||
options.w = parseInt(options.w) || Math.floor(pixels.shape[0]);
|
|
||||||
options.h = parseInt(options.h) || Math.floor(pixels.shape[1]);
|
|
||||||
options.backgroundColor = options.backgroundColor || defaults.backgroundColor;
|
|
||||||
var ox = options.x;
|
|
||||||
var oy = options.y;
|
|
||||||
var w = options.w;
|
|
||||||
var h = options.h;
|
|
||||||
var iw = pixels.shape[0]; //Width of Original Image
|
|
||||||
var ih = pixels.shape[1]; //Height of Original Image
|
|
||||||
var backgroundArray = [];
|
|
||||||
backgroundColor = options.backgroundColor.substring(options.backgroundColor.indexOf('(') + 1, options.backgroundColor.length - 1); // extract only the values from rgba(_,_,_,_)
|
|
||||||
backgroundColor = backgroundColor.split(',');
|
|
||||||
for(var i = 0; i < w ; i++){
|
|
||||||
backgroundArray = backgroundArray.concat([backgroundColor[0], backgroundColor[1], backgroundColor[2], backgroundColor[3]]);
|
|
||||||
}
|
|
||||||
// var newarray = new Uint8Array(4*w*h);
|
|
||||||
var array = [];
|
|
||||||
for (var n = oy; n < oy + h; n++) {
|
|
||||||
var offsetValue = 4 * w * n;
|
|
||||||
if(n < ih){
|
|
||||||
var start = n * 4 * iw + ox * 4;
|
|
||||||
var end = n * 4 * iw + ox * 4 + 4 * w;
|
|
||||||
var pushArray = Array.from(pixels.data.slice(start, end ));
|
|
||||||
array.push.apply(array, pushArray);
|
|
||||||
} else {
|
|
||||||
array.push.apply(array, backgroundArray);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var newarray = Uint8Array.from(array);
|
|
||||||
pixels.data = newarray;
|
|
||||||
pixels.shape = [w, h, 4];
|
|
||||||
pixels.stride[1] = 4 * w;
|
|
||||||
|
|
||||||
options.format = input.format;
|
const bg = options.backgroundColor.replace('rgba', '').replace('(', '').replace(')', '').split(',');
|
||||||
|
|
||||||
var chunks = [];
|
let iw = pixels.shape[0], // Width of Original Image
|
||||||
var totalLength = 0;
|
ih = pixels.shape[1], // Height of Original Image
|
||||||
var r = savePixels(pixels, options.format);
|
offsetX,
|
||||||
|
offsetY,
|
||||||
|
w,
|
||||||
|
h;
|
||||||
|
|
||||||
r.on('data', function(chunk){
|
// Parse the inputs
|
||||||
totalLength += chunk.length;
|
parseCornerCoordinateInputs({iw, ih},
|
||||||
chunks.push(chunk);
|
{
|
||||||
|
x: { valInp: options.x, type: 'horizontal' },
|
||||||
|
y: { valInp: options.y, type: 'vertical' },
|
||||||
|
w: { valInp: options.w, type: 'horizontal' },
|
||||||
|
h: { valInp: options.h, type: 'vertical' },
|
||||||
|
}, function (opt, coord) {
|
||||||
|
offsetX = Math.floor(coord.x.valInp);
|
||||||
|
offsetY = Math.floor(coord.y.valInp);
|
||||||
|
w = Math.floor(coord.w.valInp);
|
||||||
|
h = Math.floor(coord.h.valInp);
|
||||||
});
|
});
|
||||||
|
|
||||||
r.on('end', function(){
|
const newPixels = new ndarray([], [w, h, 4]);
|
||||||
var data = Buffer.concat(chunks, totalLength).toString('base64');
|
|
||||||
var datauri = 'data:image/' + options.format + ';base64,' + data;
|
for (let x = 0; x < w; x++) {
|
||||||
callback(datauri, options.format);
|
for (let y = 0; y < h; y++) {
|
||||||
});
|
pixelSetter(x, y, bg, newPixels); // Set the background color
|
||||||
});
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (
|
||||||
|
let x = 0;
|
||||||
|
x < Math.min(w - 1, offsetX + iw - 1);
|
||||||
|
x++
|
||||||
|
) {
|
||||||
|
for (
|
||||||
|
let y = 0;
|
||||||
|
y < Math.min(h - 1, offsetY + ih - 1);
|
||||||
|
y++
|
||||||
|
) {
|
||||||
|
const inputImgX = x + offsetX,
|
||||||
|
inputImgY = y + offsetY;
|
||||||
|
|
||||||
|
pixelSetter(x, y, [
|
||||||
|
pixels.get(inputImgX, inputImgY, 0),
|
||||||
|
pixels.get(inputImgX, inputImgY, 1),
|
||||||
|
pixels.get(inputImgX, inputImgY, 2),
|
||||||
|
pixels.get(inputImgX, inputImgY, 3)
|
||||||
|
], newPixels); // Set the background color
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cb) cb();
|
||||||
|
|
||||||
|
return newPixels;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
const pixelManipulation = require('../_nomodule/PixelManipulation');
|
||||||
/*
|
/*
|
||||||
* Image Cropping module
|
* Image Cropping module
|
||||||
* Usage:
|
* Usage:
|
||||||
@@ -26,54 +27,35 @@ module.exports = function CropModule(options, UI) {
|
|||||||
|
|
||||||
var step = this;
|
var step = this;
|
||||||
|
|
||||||
// save the input image;
|
function extraManipulation(pixels) {
|
||||||
// TODO: this should be moved to module API to persist the input image
|
const newPixels = require('./Crop')(pixels, options, function() {
|
||||||
options.step.input = input.src;
|
// We should do this via event/listener:
|
||||||
var parseCornerCoordinateInputs = require('../../util/ParseInputCoordinates');
|
if (ui && ui.hide) ui.hide();
|
||||||
|
|
||||||
//parse the inputs
|
// Start custom UI setup (draggable UI)
|
||||||
parseCornerCoordinateInputs(options, {
|
// Only once we have an input image
|
||||||
src: input.src,
|
if (setupComplete === false && options.step.inBrowser && !options.noUI) {
|
||||||
x: { valInp: options.x, type: 'horizontal' },
|
setupComplete = true;
|
||||||
y: { valInp: options.y, type: 'vertical' },
|
ui.setup();
|
||||||
w: { valInp: options.w, type: 'horizontal' },
|
}
|
||||||
h: { valInp: options.h, type: 'vertical' },
|
});
|
||||||
}, function (options, coord) {
|
return newPixels;
|
||||||
options.x = parseInt(coord.x.valInp);
|
}
|
||||||
options.y = parseInt(coord.y.valInp);
|
|
||||||
options.w = coord.w.valInp;
|
function output(image, datauri, mimetype, wasmSuccess) {
|
||||||
options.h = coord.h.valInp;
|
step.output = { src: datauri, format: mimetype, wasmSuccess, useWasm: options.useWasm };
|
||||||
|
}
|
||||||
|
|
||||||
|
return pixelManipulation(input, {
|
||||||
|
output: output,
|
||||||
|
ui: options.step.ui,
|
||||||
|
extraManipulation: extraManipulation,
|
||||||
|
format: input.format,
|
||||||
|
image: options.image,
|
||||||
|
inBrowser: options.inBrowser,
|
||||||
|
callback: callback,
|
||||||
|
useWasm:options.useWasm
|
||||||
});
|
});
|
||||||
|
|
||||||
require('./Crop')(input, options, function (out, format) {
|
|
||||||
|
|
||||||
// This output is accessible to Image Sequencer
|
|
||||||
step.output = {
|
|
||||||
src: out,
|
|
||||||
format: format
|
|
||||||
};
|
|
||||||
|
|
||||||
// This output is accessible to the UI
|
|
||||||
options.step.output = out;
|
|
||||||
|
|
||||||
// Tell the UI that the step has been drawn
|
|
||||||
UI.onComplete(options.step);
|
|
||||||
|
|
||||||
// we should do this via event/listener:
|
|
||||||
if (ui && ui.hide) ui.hide();
|
|
||||||
|
|
||||||
// start custom UI setup (draggable UI)
|
|
||||||
// only once we have an input image
|
|
||||||
if (setupComplete === false && options.step.inBrowser && !options.noUI) {
|
|
||||||
setupComplete = true;
|
|
||||||
ui.setup();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tell Image Sequencer that step has been drawn
|
|
||||||
callback();
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -16,12 +16,12 @@
|
|||||||
"w": {
|
"w": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"desc": "Width of crop",
|
"desc": "Width of crop",
|
||||||
"default": "(50%)"
|
"default": "50%"
|
||||||
},
|
},
|
||||||
"h": {
|
"h": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"desc": "Height of crop",
|
"desc": "Height of crop",
|
||||||
"default": "(50%)"
|
"default": "50%"
|
||||||
},
|
},
|
||||||
"backgroundColor": {
|
"backgroundColor": {
|
||||||
"type": "text",
|
"type": "text",
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ module.exports = function DoNothing(options, UI) {
|
|||||||
ui: options.step.ui,
|
ui: options.step.ui,
|
||||||
format: input.format,
|
format: input.format,
|
||||||
image: options.image,
|
image: options.image,
|
||||||
|
inBrowser: options.inBrowser,
|
||||||
callback: callback,
|
callback: callback,
|
||||||
useWasm:options.useWasm
|
useWasm:options.useWasm
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ module.exports = function Dither(options, UI) {
|
|||||||
extraManipulation: extraManipulation,
|
extraManipulation: extraManipulation,
|
||||||
format: input.format,
|
format: input.format,
|
||||||
image: options.image,
|
image: options.image,
|
||||||
|
inBrowser: options.inBrowser,
|
||||||
callback: callback,
|
callback: callback,
|
||||||
useWasm:options.useWasm
|
useWasm:options.useWasm
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -4,22 +4,24 @@ module.exports = exports = function(pixels, options){
|
|||||||
|
|
||||||
options.startingX = options.startingX || defaults.startingX;
|
options.startingX = options.startingX || defaults.startingX;
|
||||||
options.startingY = options.startingY || defaults.startingY;
|
options.startingY = options.startingY || defaults.startingY;
|
||||||
|
|
||||||
var ox = Number(options.startingX),
|
var ox = Number(options.startingX),
|
||||||
oy = Number(options.startingY),
|
oy = Number(options.startingY),
|
||||||
iw = pixels.shape[0],
|
iw = pixels.shape[0],
|
||||||
ih = pixels.shape[1],
|
ih = pixels.shape[1],
|
||||||
thickness = Number(options.thickness) || defaults.thickness,
|
thickness = Number(options.thickness) || defaults.thickness,
|
||||||
ex = options.endX = Number(options.endX) - thickness || iw - 1,
|
ex = Number(options.endX) - thickness || iw - 1,
|
||||||
ey = options.endY = Number(options.endY) - thickness || ih - 1,
|
ey = Number(options.endY) - thickness || ih - 1,
|
||||||
color = options.color || defaults.color;
|
color = options.color || defaults.color;
|
||||||
color = color.substring(color.indexOf('(') + 1, color.length - 1); // extract only the values from rgba(_,_,_,_)
|
|
||||||
|
color = color.substring(color.indexOf('(') + 1, color.length - 1); // Extract only the values from rgba(_,_,_,_)
|
||||||
color = color.split(',');
|
color = color.split(',');
|
||||||
|
|
||||||
var drawSide = function(startX, startY, endX, endY){
|
var drawSide = function(startX, startY, endX, endY){
|
||||||
for (var n = startX; n <= endX + thickness; n++){
|
for (var n = startX; n <= endX + thickness; n++){
|
||||||
for (var k = startY; k <= endY + thickness; k++){
|
for (var k = startY; k <= endY + thickness; k++){
|
||||||
|
|
||||||
pixelSetter(n, k, [color[0], color[1], color[2]], pixels); //to remove 4th channel - pixels.set(n, k, 3, color[3]);
|
pixelSetter(n, k, [color[0], color[1], color[2]], pixels); // To remove 4th channel - pixels.set(n, k, 3, color[3]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ module.exports = function DrawRectangle(options, UI) {
|
|||||||
extraManipulation: extraManipulation,
|
extraManipulation: extraManipulation,
|
||||||
format: input.format,
|
format: input.format,
|
||||||
image: options.image,
|
image: options.image,
|
||||||
|
inBrowser: options.inBrowser,
|
||||||
callback: callback,
|
callback: callback,
|
||||||
useWasm:options.useWasm
|
useWasm:options.useWasm
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
/**
|
const Blur = require('../Blur/Blur');
|
||||||
|
/*
|
||||||
* Detect Edges in an Image
|
* Detect Edges in an Image
|
||||||
* Uses Canny method for the same
|
* Uses Canny method for the same
|
||||||
* Read more: https://en.wikipedia.org/wiki/Canny_edge_detector
|
* Read more: https://en.wikipedia.org/wiki/Canny_edge_detector
|
||||||
@@ -21,42 +22,31 @@ module.exports = function edgeDetect(options, UI) {
|
|||||||
|
|
||||||
var step = this;
|
var step = this;
|
||||||
|
|
||||||
// Blur the image.
|
// Makes the image greyscale
|
||||||
const internalSequencer = ImageSequencer({ inBrowser: false, ui: false });
|
function changePixel(r, g, b, a) {
|
||||||
return internalSequencer.loadImage(input.src, function() {
|
return [(r + g + b) / 3, (r + g + b) / 3, (r + g + b) / 3, a];
|
||||||
internalSequencer.importJSON([{ 'name': 'blur', 'options': { blur: options.blur } }]); // Blurs the image before detecting edges to reduce noise.
|
}
|
||||||
return internalSequencer.run(function onCallback(internalOutput) {
|
|
||||||
require('get-pixels')(internalOutput, function(err, blurPixels) {
|
|
||||||
if (err) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extra Manipulation function used as an enveloper for applying gaussian blur and Convolution.
|
// Extra Manipulation function used as an enveloper for applying gaussian blur and Convolution
|
||||||
function changePixel(r, g, b, a) {
|
function extraManipulation(pixels) {
|
||||||
return [(r + g + b) / 3, (r + g + b) / 3, (r + g + b) / 3, a];
|
const blurPixels = Blur(pixels, options.blur);
|
||||||
}
|
return require('./EdgeUtils')(blurPixels, options.highThresholdRatio, options.lowThresholdRatio, options.hysteresis);
|
||||||
|
}
|
||||||
|
|
||||||
function extraManipulation() {
|
function output(image, datauri, mimetype, wasmSuccess) {
|
||||||
return require('./EdgeUtils')(blurPixels, options.highThresholdRatio, options.lowThresholdRatio, options.hysteresis);
|
step.output = { src: datauri, format: mimetype, wasmSuccess, useWasm: options.useWasm };
|
||||||
}
|
}
|
||||||
|
|
||||||
function output(image, datauri, mimetype, wasmSuccess) {
|
return require('../_nomodule/PixelManipulation.js')(input, {
|
||||||
step.output = { src: datauri, format: mimetype, wasmSuccess, useWasm: options.useWasm };
|
output: output,
|
||||||
}
|
ui: options.step.ui,
|
||||||
|
changePixel: changePixel,
|
||||||
return require('../_nomodule/PixelManipulation.js')(input, {
|
extraManipulation: extraManipulation,
|
||||||
output: output,
|
format: input.format,
|
||||||
ui: options.step.ui,
|
image: options.image,
|
||||||
changePixel: changePixel,
|
inBrowser: options.inBrowser,
|
||||||
extraManipulation: extraManipulation,
|
callback: callback,
|
||||||
format: input.format,
|
useWasm: options.useWasm
|
||||||
image: options.image,
|
|
||||||
inBrowser: options.inBrowser,
|
|
||||||
callback: callback,
|
|
||||||
useWasm: options.useWasm
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
const _ = require('lodash');
|
||||||
/*
|
/*
|
||||||
* Flip the image on vertical/horizontal axis.
|
* Flip the image on vertical/horizontal axis.
|
||||||
*/
|
*/
|
||||||
@@ -5,8 +6,7 @@ module.exports = function FlipImage(options, UI) {
|
|||||||
var defaults = require('./../../util/getDefaults.js')(require('./info.json'));
|
var defaults = require('./../../util/getDefaults.js')(require('./info.json'));
|
||||||
options.Axis = options.Axis || defaults.Axis;
|
options.Axis = options.Axis || defaults.Axis;
|
||||||
|
|
||||||
var output,
|
let output;
|
||||||
getPixels = require('get-pixels');
|
|
||||||
|
|
||||||
function draw(input, callback, progressObj) {
|
function draw(input, callback, progressObj) {
|
||||||
|
|
||||||
@@ -15,35 +15,30 @@ module.exports = function FlipImage(options, UI) {
|
|||||||
|
|
||||||
var step = this;
|
var step = this;
|
||||||
|
|
||||||
return getPixels(input.src, function(err, oldPixels) {
|
function changePixel(r, g, b, a) {
|
||||||
function changePixel(r, g, b, a) {
|
return [r, g, b, a];
|
||||||
return [r, g, b, a];
|
}
|
||||||
}
|
function extraManipulation(pixels) {
|
||||||
function extraManipulation(pixels) {
|
const oldPixels = _.cloneDeep(pixels);
|
||||||
if (err) {
|
|
||||||
console.log(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
return require('./flipImage')(oldPixels, pixels, options.Axis);
|
|
||||||
}
|
|
||||||
|
|
||||||
function output(image, datauri, mimetype, wasmSuccess) {
|
|
||||||
step.output = { src: datauri, format: mimetype, wasmSuccess, useWasm: options.useWasm };
|
|
||||||
}
|
|
||||||
|
|
||||||
return require('../_nomodule/PixelManipulation.js')(input, {
|
return require('./flipImage')(oldPixels, pixels, options.Axis);
|
||||||
output: output,
|
}
|
||||||
ui: options.step.ui,
|
|
||||||
changePixel: changePixel,
|
function output(image, datauri, mimetype, wasmSuccess) {
|
||||||
extraManipulation: extraManipulation,
|
step.output = { src: datauri, format: mimetype, wasmSuccess, useWasm: options.useWasm };
|
||||||
format: input.format,
|
}
|
||||||
image: options.image,
|
|
||||||
inBrowser: options.inBrowser,
|
return require('../_nomodule/PixelManipulation.js')(input, {
|
||||||
callback: callback,
|
output: output,
|
||||||
useWasm:options.useWasm
|
ui: options.step.ui,
|
||||||
});
|
changePixel: changePixel,
|
||||||
|
extraManipulation: extraManipulation,
|
||||||
|
format: input.format,
|
||||||
|
image: options.image,
|
||||||
|
inBrowser: options.inBrowser,
|
||||||
|
callback: callback,
|
||||||
|
useWasm:options.useWasm
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -1,52 +1,41 @@
|
|||||||
module.exports = function Invert(options, UI) {
|
const pixelSetter = require('../../util/pixelSetter.js'),
|
||||||
const pixelSetter = require('../../util/pixelSetter.js');
|
pixelManipulation = require('../_nomodule/PixelManipulation');
|
||||||
|
|
||||||
|
module.exports = function Gradient(options, UI) {
|
||||||
|
|
||||||
var output;
|
var output;
|
||||||
|
|
||||||
// The function which is called on every draw.
|
// The function which is called on every draw.
|
||||||
function draw(input, callback) {
|
function draw(input, callback) {
|
||||||
|
|
||||||
var getPixels = require('get-pixels');
|
|
||||||
var savePixels = require('save-pixels');
|
|
||||||
|
|
||||||
var step = this;
|
var step = this;
|
||||||
|
|
||||||
getPixels(input.src, function(err, pixels) {
|
|
||||||
|
|
||||||
if (err) {
|
|
||||||
console.log('Bad Image path');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var width = pixels.shape[0];
|
|
||||||
|
|
||||||
for (var i = 0; i < pixels.shape[0]; i++) {
|
|
||||||
for (var j = 0; j < pixels.shape[1]; j++) {
|
|
||||||
let val = (i / width) * 255;
|
|
||||||
pixelSetter(i, j, [val, val, val, 255], pixels);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var chunks = [];
|
|
||||||
var totalLength = 0;
|
|
||||||
var r = savePixels(pixels, input.format, { quality: 100 });
|
|
||||||
|
|
||||||
r.on('data', function(chunk) {
|
|
||||||
totalLength += chunk.length;
|
|
||||||
chunks.push(chunk);
|
|
||||||
});
|
|
||||||
|
|
||||||
r.on('end', function() {
|
|
||||||
var data = Buffer.concat(chunks, totalLength).toString('base64');
|
|
||||||
var datauri = 'data:image/' + input.format + ';base64,' + data;
|
|
||||||
output(input.image, datauri, input.format);
|
|
||||||
callback();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
function output(image, datauri, mimetype, wasmSuccess) {
|
function output(image, datauri, mimetype, wasmSuccess) {
|
||||||
step.output = { src: datauri, format: mimetype, wasmSuccess, useWasm: options.useWasm };
|
step.output = { src: datauri, format: mimetype, wasmSuccess, useWasm: options.useWasm };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function extraManipulation(pixels) {
|
||||||
|
const [w, h] = pixels.shape;
|
||||||
|
for (var i = 0; i < w; i++) {
|
||||||
|
for (var j = 0; j < h; j++) {
|
||||||
|
let val = (i / w) * 255;
|
||||||
|
|
||||||
|
pixelSetter(i, j, [val, val, val, 255], pixels);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return pixels;
|
||||||
|
}
|
||||||
|
|
||||||
|
return pixelManipulation(input, {
|
||||||
|
output,
|
||||||
|
extraManipulation,
|
||||||
|
callback,
|
||||||
|
format: input.format,
|
||||||
|
image: options.image,
|
||||||
|
inBrowser: options.inBrowser,
|
||||||
|
useWasm:options.useWasm
|
||||||
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
const pixelManipulation = require('../_nomodule/PixelManipulation');
|
||||||
/*
|
/*
|
||||||
* Invert the image
|
* Invert the image
|
||||||
*/
|
*/
|
||||||
@@ -21,7 +22,7 @@ function Invert(options, UI) {
|
|||||||
step.output = { src: datauri, format: mimetype, wasmSuccess, useWasm: options.useWasm };
|
step.output = { src: datauri, format: mimetype, wasmSuccess, useWasm: options.useWasm };
|
||||||
}
|
}
|
||||||
|
|
||||||
return input.pixelManipulation({
|
return pixelManipulation(input, {
|
||||||
output: output,
|
output: output,
|
||||||
changePixel: changePixel,
|
changePixel: changePixel,
|
||||||
format: input.format,
|
format: input.format,
|
||||||
@@ -40,10 +41,4 @@ function Invert(options, UI) {
|
|||||||
UI: UI
|
UI: UI
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
var info = {
|
module.exports = Invert;
|
||||||
'name': 'invert',
|
|
||||||
'description': 'Inverts the image.',
|
|
||||||
'inputs': {
|
|
||||||
}
|
|
||||||
};
|
|
||||||
module.exports = [Invert, info];
|
|
||||||
|
|||||||
4
src/modules/Invert/index.js
Normal file
4
src/modules/Invert/index.js
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
module.exports = [
|
||||||
|
require('./Module'),
|
||||||
|
require('./info.json')
|
||||||
|
];
|
||||||
@@ -25,6 +25,7 @@ module.exports = function NoiseReduction(options, UI) {
|
|||||||
extraManipulation: extraManipulation,
|
extraManipulation: extraManipulation,
|
||||||
format: input.format,
|
format: input.format,
|
||||||
image: options.image,
|
image: options.image,
|
||||||
|
inBrowser: options.inBrowser,
|
||||||
callback: callback,
|
callback: callback,
|
||||||
useWasm:options.useWasm
|
useWasm:options.useWasm
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -18,16 +18,6 @@ module.exports = function Dynamic(options, UI, util) {
|
|||||||
|
|
||||||
var parseCornerCoordinateInputs = require('../../util/ParseInputCoordinates');
|
var parseCornerCoordinateInputs = require('../../util/ParseInputCoordinates');
|
||||||
|
|
||||||
//parse the inputs
|
|
||||||
parseCornerCoordinateInputs(options, {
|
|
||||||
src: input.src,
|
|
||||||
x: { valInp: options.x, type: 'horizontal' },
|
|
||||||
y: { valInp: options.y, type: 'vertical' },
|
|
||||||
}, function(options, input) {
|
|
||||||
options.x = parseInt(input.x.valInp);
|
|
||||||
options.y = parseInt(input.y.valInp);
|
|
||||||
});
|
|
||||||
|
|
||||||
// save the pixels of the base image
|
// save the pixels of the base image
|
||||||
var baseStepImage = this.getStep(options.offset).image;
|
var baseStepImage = this.getStep(options.offset).image;
|
||||||
var baseStepOutput = this.getOutput(options.offset);
|
var baseStepOutput = this.getOutput(options.offset);
|
||||||
@@ -35,6 +25,19 @@ module.exports = function Dynamic(options, UI, util) {
|
|||||||
var getPixels = require('get-pixels');
|
var getPixels = require('get-pixels');
|
||||||
|
|
||||||
getPixels(input.src, function(err, pixels) {
|
getPixels(input.src, function(err, pixels) {
|
||||||
|
// parse the inputs
|
||||||
|
parseCornerCoordinateInputs({
|
||||||
|
iw: pixels.shape[0],
|
||||||
|
ih: pixels.shape[1]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
x: { valInp: options.x, type: 'horizontal' },
|
||||||
|
y: { valInp: options.y, type: 'vertical' },
|
||||||
|
}, function(opt, input) {
|
||||||
|
options.x = parseInt(input.x.valInp);
|
||||||
|
options.y = parseInt(input.y.valInp);
|
||||||
|
});
|
||||||
|
|
||||||
options.secondImagePixels = pixels;
|
options.secondImagePixels = pixels;
|
||||||
|
|
||||||
function changePixel(r1, g1, b1, a1, x, y) {
|
function changePixel(r1, g1, b1, a1, x, y) {
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ module.exports = function ReplaceColor(options, UI) {
|
|||||||
extraManipulation: extraManipulation,
|
extraManipulation: extraManipulation,
|
||||||
format: input.format,
|
format: input.format,
|
||||||
image: options.image,
|
image: options.image,
|
||||||
|
inBrowser: options.inBrowser,
|
||||||
callback: callback,
|
callback: callback,
|
||||||
useWasm:options.useWasm
|
useWasm:options.useWasm
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,59 +1,71 @@
|
|||||||
|
const imagejs = require('imagejs'),
|
||||||
|
pixelSetter = require('../../util/pixelSetter'),
|
||||||
|
ndarray = require('ndarray');
|
||||||
/*
|
/*
|
||||||
* Resize the image by given percentage value
|
* Resize the image by given percentage value
|
||||||
*/
|
*/
|
||||||
module.exports = function Resize(options, UI) {
|
module.exports = function Resize(options, UI) {
|
||||||
|
|
||||||
var output;
|
let output;
|
||||||
|
|
||||||
function draw(input, callback, progressObj) {
|
function draw(input, callback, progressObj) {
|
||||||
|
|
||||||
var defaults = require('./../../util/getDefaults.js')(require('./info.json'));
|
const defaults = require('./../../util/getDefaults.js')(require('./info.json'));
|
||||||
options.resize = options.resize || defaults.resize;
|
options.resize = options.resize || defaults.resize;
|
||||||
|
|
||||||
progressObj.stop(true);
|
progressObj.stop(true);
|
||||||
progressObj.overrideFlag = true;
|
progressObj.overrideFlag = true;
|
||||||
|
|
||||||
var step = this;
|
const step = this;
|
||||||
|
|
||||||
var imagejs = require('imagejs');
|
|
||||||
|
|
||||||
function changePixel(r, g, b, a) {
|
|
||||||
return [r, g, b, a];
|
|
||||||
}
|
|
||||||
|
|
||||||
function extraManipulation(pixels) {
|
function extraManipulation(pixels) {
|
||||||
// value above 100% scales up, and below 100% scales down
|
// Value above 100% scales up, and below 100% scales down
|
||||||
var resize_value = parseInt(options.resize.slice(0, -1));
|
const resize_value = parseInt(options.resize.slice(0, -1));
|
||||||
|
|
||||||
var new_width,
|
if (resize_value == 100) return pixels;
|
||||||
new_height;
|
|
||||||
|
|
||||||
new_width = Math.round(pixels.shape[0] * (resize_value / 100));
|
|
||||||
new_height = Math.round(pixels.shape[1] * (resize_value / 100));
|
|
||||||
|
|
||||||
var bitmap = new imagejs.Bitmap({ width: pixels.shape[0], height: pixels.shape[1] });
|
|
||||||
bitmap._data.data = pixels.data;
|
|
||||||
|
|
||||||
|
|
||||||
var resized = bitmap.resize({
|
const new_width = Math.round(pixels.shape[0] * (resize_value / 100)),
|
||||||
width: new_width, height: new_height,
|
new_height = Math.round(pixels.shape[1] * (resize_value / 100));
|
||||||
|
|
||||||
|
const bitmap = new imagejs.Bitmap({ width: pixels.shape[0], height: pixels.shape[1] });
|
||||||
|
|
||||||
|
for (let x = 0; x < pixels.shape[0]; x++) {
|
||||||
|
for (let y = 0; y < pixels.shape[1]; y++) {
|
||||||
|
let r = pixels.get(x, y, 0),
|
||||||
|
g = pixels.get(x, y, 1),
|
||||||
|
b = pixels.get(x, y, 2),
|
||||||
|
a = pixels.get(x, y, 3);
|
||||||
|
|
||||||
|
bitmap.setPixel(x, y, r, g, b, a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const resized = bitmap.resize({
|
||||||
|
width: new_width,
|
||||||
|
height: new_height,
|
||||||
algorithm: 'bicubicInterpolation'
|
algorithm: 'bicubicInterpolation'
|
||||||
});
|
});
|
||||||
|
|
||||||
pixels.data = resized._data.data;
|
const newPix = new ndarray([], [new_width, new_height, 4]);
|
||||||
pixels.shape = [new_width, new_height, 4];
|
|
||||||
pixels.stride[1] = 4 * new_width;
|
|
||||||
|
|
||||||
return pixels;
|
for (let x = 0; x < new_width; x++) {
|
||||||
|
for (let y = 0; y < new_height; y++) {
|
||||||
|
const {r, g, b, a} = resized.getPixel(x, y);
|
||||||
|
pixelSetter(x, y, [r, g, b, a], newPix);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return newPix;
|
||||||
}
|
}
|
||||||
|
|
||||||
function output(image, datauri, mimetype, wasmSuccess) {
|
function output(image, datauri, mimetype, wasmSuccess) {
|
||||||
step.output = { src: datauri, format: mimetype, wasmSuccess, useWasm: options.useWasm };
|
step.output = { src: datauri, format: mimetype, wasmSuccess, useWasm: options.useWasm };
|
||||||
}
|
}
|
||||||
|
|
||||||
return require('../_nomodule/PixelManipulation.js')(input, {
|
return require('../_nomodule/PixelManipulation.js')(input, {
|
||||||
output: output,
|
output: output,
|
||||||
ui: options.step.ui,
|
ui: options.step.ui,
|
||||||
changePixel: changePixel,
|
|
||||||
extraManipulation: extraManipulation,
|
extraManipulation: extraManipulation,
|
||||||
format: input.format,
|
format: input.format,
|
||||||
image: options.image,
|
image: options.image,
|
||||||
|
|||||||
@@ -3,34 +3,57 @@
|
|||||||
*/
|
*/
|
||||||
module.exports = function Rotate(options, UI) {
|
module.exports = function Rotate(options, UI) {
|
||||||
|
|
||||||
var output;
|
let output;
|
||||||
|
|
||||||
function draw(input, callback, progressObj) {
|
function draw(input, callback, progressObj) {
|
||||||
|
|
||||||
var defaults = require('./../../util/getDefaults.js')(require('./info.json'));
|
const defaults = require('./../../util/getDefaults.js')(require('./info.json'));
|
||||||
options.rotate = options.rotate || defaults.rotate;
|
options.rotate = options.rotate || defaults.rotate;
|
||||||
|
|
||||||
progressObj.stop(true);
|
progressObj.stop(true);
|
||||||
progressObj.overrideFlag = true;
|
progressObj.overrideFlag = true;
|
||||||
|
|
||||||
var step = this;
|
const step = this;
|
||||||
|
|
||||||
var imagejs = require('imagejs');
|
|
||||||
|
|
||||||
function changePixel(r, g, b, a) {
|
function changePixel(r, g, b, a) {
|
||||||
return [r, g, b, a];
|
return [r, g, b, a];
|
||||||
}
|
}
|
||||||
|
|
||||||
function extraManipulation(pixels) {
|
function extraManipulation(pixels) {
|
||||||
var rotate_value = (options.rotate) % 360;
|
const rotate_value = (options.rotate) % 360;
|
||||||
var radians = (Math.PI) * rotate_value / 180;
|
radians = (Math.PI) * rotate_value / 180,
|
||||||
var width = pixels.shape[0];
|
width = pixels.shape[0],
|
||||||
var height = pixels.shape[1];
|
height = pixels.shape[1],
|
||||||
var cos = Math.cos(radians);
|
cos = Math.cos(radians),
|
||||||
var sin = Math.sin(radians);
|
sin = Math.sin(radians);
|
||||||
//final dimensions after rotation
|
// Final dimensions after rotation
|
||||||
var pixels2 = require('ndarray')(new Uint8Array(4 * (Math.floor(Math.abs(width * cos) + Math.abs(height * sin) + 5) * (Math.floor(Math.abs(width * sin) + Math.abs(height * cos)) + 5))).fill(0), [Math.floor(Math.abs(width * cos) + Math.abs(height * sin)) + 5, Math.floor(Math.abs(width * sin) + Math.abs(height * cos)) + 4, 4]);
|
|
||||||
pixels = require('./Rotate')(pixels, pixels2, options, rotate_value, width, height, cos, sin);
|
const finalPixels = require('ndarray')(
|
||||||
|
new Uint8Array(
|
||||||
|
4 *
|
||||||
|
(
|
||||||
|
Math.floor(
|
||||||
|
Math.abs(width * cos) +
|
||||||
|
Math.abs(height * sin) +
|
||||||
|
5
|
||||||
|
) *
|
||||||
|
(
|
||||||
|
Math.floor(
|
||||||
|
Math.abs(width * sin) +
|
||||||
|
Math.abs(height * cos)
|
||||||
|
) +
|
||||||
|
5
|
||||||
|
)
|
||||||
|
)
|
||||||
|
).fill(255),
|
||||||
|
[
|
||||||
|
Math.floor(Math.abs(width * cos) + Math.abs(height * sin)) + 5,
|
||||||
|
Math.floor(Math.abs(width * sin) + Math.abs(height * cos)) + 4,
|
||||||
|
4
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
pixels = require('./Rotate')(pixels, finalPixels, rotate_value, width, height, cos, sin);
|
||||||
return pixels;
|
return pixels;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,38 +1,81 @@
|
|||||||
module.exports = function Rotate(pixels, pixels2, options, rotate_value, width, height, cos, sin){
|
const imagejs = require('imagejs'),
|
||||||
var imagejs = require('imagejs');
|
ndarray = require('ndarray'),
|
||||||
var height_half = Math.floor(height / 2);
|
pixelSetter = require('../../util/pixelSetter');
|
||||||
var width_half = Math.floor(width / 2);
|
|
||||||
var dimension = width + height;
|
|
||||||
|
|
||||||
if (rotate_value % 360 == 0)
|
module.exports = function Rotate(pixels, finalPixels, rotate_value, width, height, cos, sin){
|
||||||
return pixels;
|
const height_half = Math.floor(height / 2),
|
||||||
function copyPixel(x1, y1, x2, y2,pixel_set,pixel_get){
|
width_half = Math.floor(width / 2);
|
||||||
pixel_set.set(x1, y1, 0, pixel_get.get(x2, y2, 0));
|
dimension = width + height;
|
||||||
pixel_set.set(x1, y1, 1, pixel_get.get(x2, y2, 1));
|
|
||||||
pixel_set.set(x1, y1, 2, pixel_get.get(x2, y2, 2));
|
if (rotate_value % 360 == 0) return pixels;
|
||||||
pixel_set.set(x1, y1, 3, pixel_get.get(x2, y2, 3));
|
|
||||||
|
function copyPixel(x1, y1, x2, y2, finalPix, initPix) {
|
||||||
|
finalPix.set(x1, y1, 0, initPix.get(x2, y2, 0));
|
||||||
|
finalPix.set(x1, y1, 1, initPix.get(x2, y2, 1));
|
||||||
|
finalPix.set(x1, y1, 2, initPix.get(x2, y2, 2));
|
||||||
|
finalPix.set(x1, y1, 3, initPix.get(x2, y2, 3));
|
||||||
}
|
}
|
||||||
|
|
||||||
pixels1 = require('ndarray')(new Uint8Array(4 * dimension * dimension).fill(0), [dimension, dimension, 4]);
|
const intermediatePixels = new ndarray(
|
||||||
//copying all the pixels from image to pixels1
|
new Uint8Array(4 * dimension * dimension).fill(255),
|
||||||
for (var n = 0; n < pixels.shape[0]; n++){
|
[dimension, dimension, 4]
|
||||||
for (var m = 0; m < pixels.shape[1]; m++){
|
); // Intermediate ndarray of pixels with a greater size to prevent clipping.
|
||||||
copyPixel(n + height_half, m + width_half, n, m,pixels1,pixels);
|
|
||||||
|
// Copying all the pixels from image to intermediatePixels
|
||||||
|
for (let x = 0; x < pixels.shape[0]; x++){
|
||||||
|
for (let y = 0; y < pixels.shape[1]; y++){
|
||||||
|
copyPixel(x + height_half, y + width_half, x, y, intermediatePixels, pixels);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//rotating pixels1
|
|
||||||
var bitmap = new imagejs.Bitmap({ width: pixels1.shape[0], height: pixels1.shape[1] });
|
|
||||||
bitmap._data.data = pixels1.data;
|
|
||||||
|
|
||||||
var rotated = bitmap.rotate({
|
// Rotating intermediatePixels
|
||||||
|
const bitmap = new imagejs.Bitmap({ width: intermediatePixels.shape[0], height: intermediatePixels.shape[1] });
|
||||||
|
|
||||||
|
for (let x = 0; x < intermediatePixels.shape[0]; x++) {
|
||||||
|
for (let y = 0; y < intermediatePixels.shape[1]; y++) {
|
||||||
|
let r = intermediatePixels.get(x, y, 0),
|
||||||
|
g = intermediatePixels.get(x, y, 1),
|
||||||
|
b = intermediatePixels.get(x, y, 2),
|
||||||
|
a = intermediatePixels.get(x, y, 3);
|
||||||
|
|
||||||
|
bitmap.setPixel(x, y, r, g, b, a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const rotated = bitmap.rotate({
|
||||||
degrees: rotate_value,
|
degrees: rotate_value,
|
||||||
});
|
});
|
||||||
pixels1.data = rotated._data.data;
|
|
||||||
//cropping extra whitespace
|
for (let x = 0; x < intermediatePixels.shape[0]; x++) {
|
||||||
for (var n = 0; n < pixels2.shape[0]; n++){
|
for (let y = 0; y < intermediatePixels.shape[1]; y++) {
|
||||||
for (var m = 0; m < pixels2.shape[1]; m++){
|
const {r, g, b, a} = rotated.getPixel(x, y);
|
||||||
copyPixel(n, m, n + Math.floor(dimension / 2 - Math.abs(width * cos / 2) - Math.abs(height * sin / 2)) - 1, m + Math.floor(dimension / 2 - Math.abs(height * cos / 2) - Math.abs(width * sin / 2)) - 1,pixels2,pixels1);
|
pixelSetter(x, y, [r, g, b, a], intermediatePixels);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return pixels2;
|
|
||||||
|
// Cropping extra whitespace
|
||||||
|
for (let x = 0; x < finalPixels.shape[0]; x++){
|
||||||
|
for (let y = 0; y < finalPixels.shape[1]; y++){
|
||||||
|
copyPixel(
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
x +
|
||||||
|
Math.floor(
|
||||||
|
dimension / 2 -
|
||||||
|
Math.abs(width * cos / 2) -
|
||||||
|
Math.abs(height * sin / 2)
|
||||||
|
) - 1,
|
||||||
|
y +
|
||||||
|
Math.floor(
|
||||||
|
dimension / 2 -
|
||||||
|
Math.abs(height * cos / 2) -
|
||||||
|
Math.abs(width * sin / 2)
|
||||||
|
) - 1,
|
||||||
|
finalPixels,
|
||||||
|
intermediatePixels
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return finalPixels;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -7,11 +7,9 @@ module.exports = function TextOverlay(options, UI) {
|
|||||||
|
|
||||||
var step = this;
|
var step = this;
|
||||||
|
|
||||||
var priorStep = this.getStep(-1); // get the previous step to add text onto it.
|
|
||||||
|
|
||||||
function extraManipulation(pixels) {
|
function extraManipulation(pixels) {
|
||||||
//if (options.step.inBrowser)
|
//if (options.step.inBrowser)
|
||||||
pixels = require('./TextOverlay')(pixels, options, priorStep);
|
pixels = require('./TextOverlay')(pixels, options);
|
||||||
return pixels;
|
return pixels;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,4 @@
|
|||||||
module.exports = exports = function(pixels, options, priorstep){
|
module.exports = exports = function(pixels, options){
|
||||||
|
|
||||||
var $ = require('jquery'); // to make text-overlay work for node.js
|
|
||||||
|
|
||||||
var defaults = require('./../../util/getDefaults.js')(require('./info.json'));
|
var defaults = require('./../../util/getDefaults.js')(require('./info.json'));
|
||||||
|
|
||||||
options.text = options.text || defaults.text;
|
options.text = options.text || defaults.text;
|
||||||
@@ -11,20 +8,18 @@ module.exports = exports = function(pixels, options, priorstep){
|
|||||||
options.color = options.color || defaults.color;
|
options.color = options.color || defaults.color;
|
||||||
options.size = options.size || defaults.size;
|
options.size = options.size || defaults.size;
|
||||||
|
|
||||||
var img = $(priorstep.imgElement);
|
|
||||||
if(Object.keys(img).length === 0){
|
|
||||||
img = $(priorstep.options.step.imgElement);
|
|
||||||
}
|
|
||||||
var canvas = document.createElement('canvas');
|
var canvas = document.createElement('canvas');
|
||||||
canvas.width = pixels.shape[0]; //img.width();
|
canvas.width = pixels.shape[0]; //img.width();
|
||||||
canvas.height = pixels.shape[1]; //img.height();
|
canvas.height = pixels.shape[1]; //img.height();
|
||||||
var ctx = canvas.getContext('2d');
|
var ctx = canvas.getContext('2d');
|
||||||
ctx.drawImage(img[0], 0, 0);
|
|
||||||
|
ctx.putImageData(new ImageData(new Uint8ClampedArray(pixels.data), pixels.shape[0], pixels.shape[1]), 0, 0);
|
||||||
|
|
||||||
ctx.fillStyle = options.color;
|
ctx.fillStyle = options.color;
|
||||||
ctx.font = options.size + 'px ' + options.font;
|
ctx.font = options.size + 'px ' + options.font;
|
||||||
ctx.fillText(options.text, options.x, options.y);
|
ctx.fillText(options.text, options.x, options.y);
|
||||||
|
|
||||||
var myImageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
var myImageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
||||||
pixels.data = myImageData.data;
|
pixels.data = new Uint8Array(myImageData.data);
|
||||||
return pixels;
|
return pixels;
|
||||||
};
|
};
|
||||||
@@ -34,6 +34,7 @@ module.exports = function ImageThreshold(options, UI) {
|
|||||||
extraManipulation: extraManipulation,
|
extraManipulation: extraManipulation,
|
||||||
format: input.format,
|
format: input.format,
|
||||||
image: options.image,
|
image: options.image,
|
||||||
|
inBrowser: options.inBrowser,
|
||||||
callback: callback,
|
callback: callback,
|
||||||
useWasm:options.useWasm
|
useWasm:options.useWasm
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,3 +1,10 @@
|
|||||||
|
const pixelSetter = require('../../util/pixelSetter.js'),
|
||||||
|
getPixels = require('get-pixels'),
|
||||||
|
savePixels = require('save-pixels'),
|
||||||
|
ndarray = require('ndarray'),
|
||||||
|
gifshot = require('gifshot'),
|
||||||
|
fs = require('fs'),
|
||||||
|
path = require('path');
|
||||||
/*
|
/*
|
||||||
* General purpose per-pixel manipulation
|
* General purpose per-pixel manipulation
|
||||||
* accepting a changePixel() method to remix a pixel's channels
|
* accepting a changePixel() method to remix a pixel's channels
|
||||||
@@ -13,9 +20,24 @@
|
|||||||
module.exports = function PixelManipulation(image, options) {
|
module.exports = function PixelManipulation(image, options) {
|
||||||
// To handle the case where pixelmanipulation is called on the input object itself
|
// To handle the case where pixelmanipulation is called on the input object itself
|
||||||
// like input.pixelManipulation(options)
|
// like input.pixelManipulation(options)
|
||||||
|
|
||||||
const pixelSetter = require('../../util/pixelSetter.js');
|
const isGIF = image.src.includes('image/gif');
|
||||||
let wasmSuccess; // Whether wasm succeded or failed
|
let numFrames = 1, // Number of frames: 1 for a still image
|
||||||
|
frames = [], // Ndarray of pixels of each frame
|
||||||
|
perFrameShape, // Width, Height and color chanels of each frame
|
||||||
|
wasmSuccess, // Whether wasm succeded or failed
|
||||||
|
renderableFrames, // To block rendering in async modules
|
||||||
|
resolvedFrames = 0; // Number of WASM promises resolved.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description Sets the render state of the current frame. True -> Renderable and False -> Not Renderable.
|
||||||
|
* @param {Boolean} state Render state of the frame
|
||||||
|
* @returns {Number} Total number of renderable frames.
|
||||||
|
*/
|
||||||
|
function setRenderState(state) {
|
||||||
|
renderableFrames += state ? 1 : -1;
|
||||||
|
return renderableFrames;
|
||||||
|
}
|
||||||
|
|
||||||
if (arguments.length <= 1) {
|
if (arguments.length <= 1) {
|
||||||
options = image;
|
options = image;
|
||||||
@@ -24,146 +46,278 @@ module.exports = function PixelManipulation(image, options) {
|
|||||||
|
|
||||||
options = options || {};
|
options = options || {};
|
||||||
|
|
||||||
const getPixels = require('get-pixels'),
|
/**
|
||||||
savePixels = require('save-pixels');
|
* @description Returns the DataURI of an image from its pixels
|
||||||
|
* @param {"ndarray"} pix pixels ndarray of pixels of the image.
|
||||||
|
* @param {String} format Format/MimeType of the image input.
|
||||||
|
* @returns {Promise} Promise with DataURI as parameter in the callback.
|
||||||
|
*/
|
||||||
|
const getDataUri = (pix, format) => {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
let chunks = [],
|
||||||
|
totalLength = 0;
|
||||||
|
|
||||||
|
let r = savePixels(pix, format, {
|
||||||
|
quality: 100
|
||||||
|
});
|
||||||
|
|
||||||
|
r.on('data', function (chunk) {
|
||||||
|
totalLength += chunk.length;
|
||||||
|
chunks.push(chunk);
|
||||||
|
});
|
||||||
|
|
||||||
|
r.on('end', function () {
|
||||||
|
let data = Buffer.concat(chunks, totalLength).toString('base64');
|
||||||
|
let datauri = 'data:image/' + format + ';base64,' + data;
|
||||||
|
|
||||||
|
resolve(datauri);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
getPixels(image.src, function (err, pixels) {
|
getPixels(image.src, function (err, pixels) {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.log('Bad image path', image);
|
console.log('get-pixels error: ', err);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.getNeighbourPixel) {
|
// There may be a more efficient means to encode an image object,
|
||||||
options.getNeighbourPixel.fun = function getNeighborPixel(distX, distY) {
|
// but node modules and their documentation are essentially arcane on this point.
|
||||||
return options.getNeighbourPixel(pixels, x, y, distX, distY);
|
function generateOutput() {
|
||||||
};
|
if (!(renderableFrames < numFrames) && !(resolvedFrames < numFrames)) {
|
||||||
}
|
|
||||||
|
|
||||||
// Iterate through pixels
|
if (isGIF) {
|
||||||
// TODO: this could possibly be more efficient; see
|
const dataPromises = []; // Array of all DataURI promises
|
||||||
// https://github.com/p-v-o-s/infragram-js/blob/master/public/infragram.js#L173-L181
|
|
||||||
|
|
||||||
|
|
||||||
if (options.preProcess) pixels = options.preProcess(pixels); // Allow for preprocessing of pixels.
|
|
||||||
|
|
||||||
function extraOperation() {
|
|
||||||
var res;
|
|
||||||
if (options.extraManipulation) res = options.extraManipulation(pixels, generateOutput); // extraManipulation is used to manipulate each pixel individually.
|
|
||||||
// There may be a more efficient means to encode an image object,
|
|
||||||
// but node modules and their documentation are essentially arcane on this point.
|
|
||||||
function generateOutput() {
|
|
||||||
var chunks = [];
|
|
||||||
var totalLength = 0;
|
|
||||||
|
|
||||||
var r = savePixels(pixels, options.format, {
|
|
||||||
quality: 100
|
|
||||||
});
|
|
||||||
|
|
||||||
r.on('data', function (chunk) {
|
|
||||||
totalLength += chunk.length;
|
|
||||||
chunks.push(chunk);
|
|
||||||
});
|
|
||||||
|
|
||||||
r.on('end', function () {
|
|
||||||
var data = Buffer.concat(chunks, totalLength).toString('base64');
|
|
||||||
var datauri = 'data:image/' + options.format + ';base64,' + data;
|
|
||||||
if (options.output)
|
|
||||||
options.output(options.image, datauri, options.format, wasmSuccess);
|
|
||||||
if (options.callback) options.callback();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (res) {
|
|
||||||
pixels = res;
|
|
||||||
generateOutput();
|
|
||||||
} else if (!options.extraManipulation) generateOutput();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!options.changePixel) extraOperation();
|
|
||||||
|
|
||||||
if (options.changePixel) {
|
|
||||||
|
|
||||||
/* Allows for Flexibility
|
|
||||||
if per pixel manipulation is not required */
|
|
||||||
|
|
||||||
const imports = {
|
|
||||||
env: {
|
|
||||||
consoleLog: console.log,
|
|
||||||
perform: function (x, y) {
|
|
||||||
let pixel = options.changePixel( // changePixel function is run over every pixel.
|
|
||||||
pixels.get(x, y, 0),
|
|
||||||
pixels.get(x, y, 1),
|
|
||||||
pixels.get(x, y, 2),
|
|
||||||
pixels.get(x, y, 3),
|
|
||||||
x,
|
|
||||||
y
|
|
||||||
);
|
|
||||||
|
|
||||||
pixelSetter(x, y, pixel, pixels);
|
|
||||||
|
|
||||||
|
for (let f = 0; f < numFrames; f++) {
|
||||||
|
dataPromises.push(getDataUri(frames[f], options.format));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
function perPixelManipulation() {
|
Promise.all(dataPromises).then(datauris => {
|
||||||
for (var x = 0; x < pixels.shape[0]; x++) {
|
const gifshotOptions = {
|
||||||
for (var y = 0; y < pixels.shape[1]; y++) {
|
images: datauris,
|
||||||
imports.env.perform(x, y);
|
frameDuration: 1, // Duration of each frame in 1/10 seconds.
|
||||||
}
|
numFrames: datauris.length,
|
||||||
}
|
gifWidth: perFrameShape[0],
|
||||||
}
|
gifHeight: perFrameShape[1]
|
||||||
|
};
|
||||||
|
|
||||||
|
const gifshotCb = out => {
|
||||||
|
if (out.error) {
|
||||||
|
console.log('gifshot error: ', out.errorMsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.output)
|
||||||
|
options.output(options.image, out.image, 'gif', wasmSuccess);
|
||||||
|
if (options.callback) options.callback();
|
||||||
|
};
|
||||||
|
|
||||||
const inBrowser = (options.inBrowser) ? 1 : 0;
|
if (options.inBrowser) {
|
||||||
const test = (process.env.TEST) ? 1 : 0;
|
gifshot.createGIF(gifshotOptions, gifshotCb);
|
||||||
|
}
|
||||||
if (options.useWasm) {
|
else {
|
||||||
if (options.inBrowser) {
|
const nodejsGIFShot = eval('require')('./node-gifshot');
|
||||||
|
nodejsGIFShot(gifshotOptions, gifshotCb);
|
||||||
fetch('../../../dist/manipulation.wasm').then(response =>
|
}
|
||||||
response.arrayBuffer()
|
|
||||||
).then(bytes =>
|
|
||||||
WebAssembly.instantiate(bytes, imports)
|
|
||||||
).then(results => {
|
|
||||||
results.instance.exports.manipulatePixel(pixels.shape[0], pixels.shape[1], inBrowser, test);
|
|
||||||
wasmSuccess = true;
|
|
||||||
|
|
||||||
extraOperation();
|
|
||||||
}).catch(err => {
|
|
||||||
console.log(err);
|
|
||||||
console.log('WebAssembly acceleration errored; falling back to JavaScript in PixelManipulation');
|
|
||||||
wasmSuccess = false;
|
|
||||||
|
|
||||||
perPixelManipulation();
|
|
||||||
extraOperation();
|
|
||||||
});
|
});
|
||||||
} else {
|
|
||||||
try{
|
|
||||||
const fs = require('fs');
|
|
||||||
const path = require('path');
|
|
||||||
const wasmPath = path.join(__dirname, '../../../', 'dist', 'manipulation.wasm');
|
|
||||||
const buf = fs.readFileSync(wasmPath);
|
|
||||||
WebAssembly.instantiate(buf, imports).then(results => {
|
|
||||||
results.instance.exports.manipulatePixel(pixels.shape[0], pixels.shape[1], inBrowser, test);
|
|
||||||
wasmSuccess = true;
|
|
||||||
|
|
||||||
extraOperation();
|
}
|
||||||
|
else {
|
||||||
|
getDataUri(frames[0], options.format).then(datauri => {
|
||||||
|
if (options.output)
|
||||||
|
options.output(options.image, datauri, options.format, wasmSuccess);
|
||||||
|
if (options.callback) options.callback();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get pixels of each frame
|
||||||
|
if (isGIF) {
|
||||||
|
const { shape } = pixels;
|
||||||
|
const [
|
||||||
|
noOfFrames,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
channels
|
||||||
|
] = shape;
|
||||||
|
|
||||||
|
numFrames = noOfFrames;
|
||||||
|
renderableFrames = noOfFrames; // Total number of renderable frames (mutable)
|
||||||
|
perFrameShape = [width, height, channels]; // Shape of ndarray of each frame
|
||||||
|
|
||||||
|
const numPixelsInFrame = width * height;
|
||||||
|
|
||||||
|
/* Coalesce the GIF frames (Some GIFs store delta information in between frames
|
||||||
|
i.e. Only the pixels which change between frames are stored. All these frames need to be
|
||||||
|
"Coalesced" to get final GIF frame.
|
||||||
|
More Info: https://www.npmjs.com/package/gif-extract-frames#why
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Credit for the below code: https://www.npmjs.com/package/gif-extract-frames
|
||||||
|
// We couldn't use the library because it uses ES6 features which cannot be browserified
|
||||||
|
for (let i = 0; i < numFrames; ++i) {
|
||||||
|
if (i > 0) {
|
||||||
|
const currIndex = pixels.index(i, 0, 0, 0);
|
||||||
|
const prevIndex = pixels.index(i - 1, 0, 0, 0);
|
||||||
|
|
||||||
|
for (let j = 0; j < numPixelsInFrame; ++j) {
|
||||||
|
const curr = currIndex + j * channels;
|
||||||
|
|
||||||
|
if (pixels.data[curr + channels - 1] === 0) {
|
||||||
|
const prev = prevIndex + j * channels;
|
||||||
|
|
||||||
|
for (let k = 0; k < channels; ++k) {
|
||||||
|
pixels.data[curr + k] = pixels.data[prev + k];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let f = 0; f < numFrames; f++) {
|
||||||
|
frames.push(
|
||||||
|
new ndarray(
|
||||||
|
new Uint8Array(
|
||||||
|
perFrameShape[0] *
|
||||||
|
perFrameShape[1] *
|
||||||
|
perFrameShape[2]
|
||||||
|
),
|
||||||
|
perFrameShape
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
for (let x = 0; x < width; x++) {
|
||||||
|
for (let y = 0; y < height; y++) {
|
||||||
|
for (let c = 0; c < channels; c++) {
|
||||||
|
frames[f].set(x, y, c, pixels.get(f, x, y, c));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
frames.push(pixels);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Manipulate every frame separately
|
||||||
|
for (let f = 0; f < numFrames; f++) {
|
||||||
|
let framePix = frames[f];
|
||||||
|
|
||||||
|
if (options.getNeighbourPixel) {
|
||||||
|
options.getNeighbourPixel.fun = function getNeighborPixel(distX, distY) {
|
||||||
|
return options.getNeighbourPixel(framePix, x, y, distX, distY);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterate through framePix
|
||||||
|
// TODO: this could possibly be more efficient; see
|
||||||
|
// https://github.com/p-v-o-s/infragram-js/blob/master/public/infragram.js#L173-L181
|
||||||
|
|
||||||
|
|
||||||
|
if (options.preProcess){
|
||||||
|
frames[f] = options.preProcess(framePix, setRenderState) || framePix; // Allow for preprocessing of framePix.
|
||||||
|
perFrameShape = frames[f].shape;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.changePixel) {
|
||||||
|
|
||||||
|
/* Allows for Flexibility
|
||||||
|
if per pixel manipulation is not required */
|
||||||
|
const imports = {
|
||||||
|
env: {
|
||||||
|
consoleLog: console.log,
|
||||||
|
perform: function (x, y) {
|
||||||
|
let pixel = options.changePixel( // changePixel function is run over every pixel.
|
||||||
|
framePix.get(x, y, 0),
|
||||||
|
framePix.get(x, y, 1),
|
||||||
|
framePix.get(x, y, 2),
|
||||||
|
framePix.get(x, y, 3),
|
||||||
|
x,
|
||||||
|
y
|
||||||
|
);
|
||||||
|
|
||||||
|
pixelSetter(x, y, pixel, framePix);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description Pure JS pixelmanipulation fallback when WASM is not working.
|
||||||
|
*/
|
||||||
|
function perPixelManipulation() {
|
||||||
|
for (var x = 0; x < framePix.shape[0]; x++) {
|
||||||
|
for (var y = 0; y < framePix.shape[1]; y++) {
|
||||||
|
imports.env.perform(x, y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const inBrowser = (options.inBrowser) ? 1 : 0;
|
||||||
|
const test = (process.env.TEST) ? 1 : 0;
|
||||||
|
|
||||||
|
if (options.useWasm) {
|
||||||
|
if (options.inBrowser) {
|
||||||
|
|
||||||
|
fetch('../../../dist/manipulation.wasm').then(response =>
|
||||||
|
response.arrayBuffer()
|
||||||
|
).then(bytes =>
|
||||||
|
WebAssembly.instantiate(bytes, imports)
|
||||||
|
).then(results => {
|
||||||
|
results.instance.exports.manipulatePixel(framePix.shape[0], framePix.shape[1], inBrowser, test);
|
||||||
|
|
||||||
|
wasmSuccess = true;
|
||||||
|
resolvedFrames++;
|
||||||
|
generateOutput();
|
||||||
|
}).catch(err => {
|
||||||
|
console.log(err);
|
||||||
|
console.log('WebAssembly acceleration errored; falling back to JavaScript in PixelManipulation');
|
||||||
|
perPixelManipulation();
|
||||||
|
|
||||||
|
wasmSuccess = false;
|
||||||
|
resolvedFrames++;
|
||||||
|
generateOutput();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
catch(err){
|
else {
|
||||||
console.log(err);
|
try{
|
||||||
console.log('WebAssembly acceleration errored; falling back to JavaScript in PixelManipulation');
|
const wasmPath = path.join(__dirname, '../../../', 'dist', 'manipulation.wasm');
|
||||||
wasmSuccess = false;
|
const buf = fs.readFileSync(wasmPath);
|
||||||
|
WebAssembly.instantiate(buf, imports).then(results => {
|
||||||
perPixelManipulation();
|
results.instance.exports.manipulatePixel(framePix.shape[0], framePix.shape[1], inBrowser, test);
|
||||||
extraOperation();
|
|
||||||
|
wasmSuccess = true;
|
||||||
|
resolvedFrames++;
|
||||||
|
generateOutput();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch(err){
|
||||||
|
console.log(err);
|
||||||
|
console.log('WebAssembly acceleration errored; falling back to JavaScript in PixelManipulation');
|
||||||
|
perPixelManipulation();
|
||||||
|
|
||||||
|
wasmSuccess = false;
|
||||||
|
resolvedFrames++;
|
||||||
|
generateOutput();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
else {
|
||||||
wasmSuccess = false;
|
perPixelManipulation();
|
||||||
|
|
||||||
perPixelManipulation();
|
wasmSuccess = false;
|
||||||
extraOperation();
|
resolvedFrames++;
|
||||||
|
generateOutput();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
else resolvedFrames++;
|
||||||
|
|
||||||
|
if (options.extraManipulation){
|
||||||
|
frames[f] = options.extraManipulation(framePix, setRenderState, generateOutput) || framePix; // extraManipulation is used to manipulate each pixel individually.
|
||||||
|
perFrameShape = frames[f].shape;
|
||||||
|
}
|
||||||
|
generateOutput();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
47
src/modules/_nomodule/node-gifshot.js
Normal file
47
src/modules/_nomodule/node-gifshot.js
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
/**
|
||||||
|
* @param {Object} options GIFShot options object
|
||||||
|
* @param {Function} cb GIFShot callback
|
||||||
|
* @returns {null}
|
||||||
|
*/
|
||||||
|
function nodejsGIFShot(options, cb) {
|
||||||
|
const puppeteer = eval('require')('puppeteer');
|
||||||
|
puppeteer.launch(
|
||||||
|
{
|
||||||
|
headless: true,
|
||||||
|
args:['--no-sandbox', '--disable-setuid-sandbox']
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.then(browser => {
|
||||||
|
browser.newPage().then(page => {
|
||||||
|
page.goto('about:blank').then(() => {
|
||||||
|
|
||||||
|
page.addScriptTag({ path: require('path').join(__dirname, '../../../node_modules/gifshot/dist/gifshot.min.js') })
|
||||||
|
.then(() => {
|
||||||
|
page.evaluate(options => {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
gifshot.createGIF(options, resolve);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
options
|
||||||
|
)
|
||||||
|
.then(obj => {
|
||||||
|
browser.close().then(() => {
|
||||||
|
if (cb) cb(obj);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(e => {
|
||||||
|
console.log('Puppeteer error: ', e);
|
||||||
|
browser.close().then(() => {
|
||||||
|
if (cb) cb({
|
||||||
|
error: true,
|
||||||
|
errorMsg: e
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = nodejsGIFShot;
|
||||||
@@ -1,24 +1,23 @@
|
|||||||
module.exports = function parseCornerCoordinateInputs(options, coord, callback) {
|
module.exports = function parseCornerCoordinateInputs(options, coord, callback) {
|
||||||
var getPixels = require('get-pixels');
|
const {iw, ih} = options;
|
||||||
getPixels(coord.src, function(err, pixels) {
|
|
||||||
var iw = pixels.shape[0],
|
function convert(key) {
|
||||||
ih = pixels.shape[1];
|
var val = coord[key];
|
||||||
if (!coord.x.valInp) {
|
|
||||||
return;
|
val.valInp = val.valInp.toString();
|
||||||
|
val.valInp = val.valInp.replace(/[\)\(]/g, '');
|
||||||
|
|
||||||
|
if (val.valInp && val.valInp.slice(-1) === '%') {
|
||||||
|
val.valInp = val.valInp.replace('%', '');
|
||||||
|
|
||||||
|
val.valInp = parseInt(val.valInp);
|
||||||
|
|
||||||
|
if (val.type === 'horizontal') val.valInp = val.valInp * iw / 100;
|
||||||
|
else val.valInp = val.valInp * ih / 100;
|
||||||
}
|
}
|
||||||
else {
|
else val.valInp = parseInt(val.valInp);
|
||||||
Object.keys(coord).forEach(convert);
|
}
|
||||||
function convert(key) {
|
|
||||||
var val = coord[key];
|
Object.keys(coord).forEach(convert);
|
||||||
if (val.valInp && val.valInp.slice(-1) === '%') {
|
callback(options, coord);
|
||||||
val.valInp = parseInt(val.valInp, 10);
|
|
||||||
if (val.type === 'horizontal')
|
|
||||||
val.valInp = val.valInp * iw / 100;
|
|
||||||
else
|
|
||||||
val.valInp = val.valInp * ih / 100;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
callback(options, coord);
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
@@ -1,10 +1,29 @@
|
|||||||
|
const getPixels = require('get-pixels');
|
||||||
module.exports = function getImageDimensions(img, cb) {
|
module.exports = function getImageDimensions(img, cb) {
|
||||||
var getPixels = require('get-pixels');
|
let dimensions;
|
||||||
var dimensions = { width: '', height: '' };
|
let isGIF;
|
||||||
getPixels(img, function(err, pixels) {
|
getPixels(img, function(err, pixels) {
|
||||||
dimensions.width = pixels.shape[0];
|
if (pixels.shape.length === 4) {
|
||||||
dimensions.height = pixels.shape[1];
|
const [frames, width, height] = pixels.shape;
|
||||||
cb(dimensions);
|
dimensions = {
|
||||||
|
frames,
|
||||||
|
width,
|
||||||
|
height
|
||||||
|
};
|
||||||
|
|
||||||
|
isGIF = true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const [width, height] = pixels.shape;
|
||||||
|
dimensions = {
|
||||||
|
width,
|
||||||
|
height
|
||||||
|
};
|
||||||
|
|
||||||
|
isGIF = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cb) cb(dimensions, isGIF);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
30
test/core/gifs/gif-test.js
Normal file
30
test/core/gifs/gif-test.js
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -42,19 +42,22 @@ function runBenchmarks(sequencer, t) {
|
|||||||
function cb() {
|
function cb() {
|
||||||
var end = Date.now();
|
var end = Date.now();
|
||||||
console.log('Module ' + mods[0] + ' ran in: ' + (end - global.start) + ' milliseconds');
|
console.log('Module ' + mods[0] + ' ran in: ' + (end - global.start) + ' milliseconds');
|
||||||
mods.splice(0, 1);
|
|
||||||
if (mods.length > 1) { // Last one is test module, we need not benchmark it
|
if (mods.length > 1) { // Last one is test module, we need not benchmark it
|
||||||
sequencer.steps[global.idx].output.src = image;
|
sequencer.steps[global.idx].output.src = image;
|
||||||
global.idx++;
|
global.idx++;
|
||||||
if (mods[0] === 'import-image' || (!!sequencer.modulesInfo(mods[0]).requires && sequencer.modulesInfo(mods[0]).requires.includes('webgl'))) {
|
mods.splice(0, 1);
|
||||||
|
|
||||||
|
while (mods[0] === 'import-image' || mods[0] === 'minify-image' || (!!sequencer.modulesInfo(mods[0]).requires && sequencer.modulesInfo(mods[0]).requires.includes('webgl'))) {
|
||||||
/* Not currently working for this module, which is for importing a new image */
|
/* Not currently working for this module, which is for importing a new image */
|
||||||
console.log('Bypassing import-image');
|
console.log(`Bypassing ${mods[0]}`);
|
||||||
mods.splice(0, 1);
|
mods.splice(0, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
sequencer.addSteps(mods[0]);
|
sequencer.addSteps(mods[0]);
|
||||||
global.start = Date.now();
|
global.start = Date.now();
|
||||||
sequencer.run({ index: global.idx }, cb);
|
sequencer.run({ index: global.idx }, cb);
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
t.end();
|
t.end();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,11 +38,11 @@ module.exports = (moduleName, options, benchmark, input) => {
|
|||||||
sequencer.run({mode: 'test'}, () => {
|
sequencer.run({mode: 'test'}, () => {
|
||||||
let result = sequencer.steps[1].output.src;
|
let result = sequencer.steps[1].output.src;
|
||||||
|
|
||||||
base64Img.imgSync(result, target, 'result');
|
base64Img.imgSync(result, target, `${moduleName}-result`);
|
||||||
base64Img.imgSync(benchmark, target, 'benchmark');
|
base64Img.imgSync(benchmark, target, `${moduleName}-benchmark`);
|
||||||
|
|
||||||
result = './test_outputs/result.png';
|
result = `./test_outputs/${moduleName}-result.png`;
|
||||||
benchmark = './test_outputs/benchmark.png';
|
benchmark = `./test_outputs/${moduleName}-benchmark.png`;
|
||||||
|
|
||||||
looksSame(result, benchmark, function(err, res) {
|
looksSame(result, benchmark, function(err, res) {
|
||||||
if (err) console.log(err);
|
if (err) console.log(err);
|
||||||
|
|||||||
@@ -1,17 +1,18 @@
|
|||||||
var test = require('tape');
|
var test = require('tape');
|
||||||
|
|
||||||
var red = 'data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAYEBQYFBAYGBQYHBwYIChAKCgkJChQODwwQFxQYGBcUFhYaHSUfGhsjHBYWICwgIyYnKSopGR8tMC0oMCUoKSj/2wBDAQcHBwoIChMKChMoGhYaKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCj/wAARCAAQABADASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAf/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFQEBAQAAAAAAAAAAAAAAAAAABgj/xAAUEQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIRAxEAPwCdABykX//Z';
|
|
||||||
|
|
||||||
var parseCornerCoordinateInputs = require('../../../src/util/ParseInputCoordinates');
|
var parseCornerCoordinateInputs = require('../../../src/util/ParseInputCoordinates');
|
||||||
|
|
||||||
|
|
||||||
test('parseCornerCoordinateInputs works.', function (t) {
|
test('parseCornerCoordinateInputs works.', function (t) {
|
||||||
var options = { x: '10%' },
|
var options = { x: '10%', iw: 10, ih: 10 },
|
||||||
coord = { src: red, x: { valInp: options.x, type: 'horizontal' } };
|
coord = { x: { valInp: options.x, type: 'horizontal' } };
|
||||||
|
|
||||||
callback = function (options, coord) {
|
callback = function (options, coord) {
|
||||||
options.x = parseInt(coord.x.valInp);
|
options.x = parseInt(coord.x.valInp);
|
||||||
t.equal(options.x, 1, 'parseCornerCoordinateInputs works.');
|
t.equal(options.x, 1, 'parseCornerCootesrdinateInputs Works.');
|
||||||
|
t.equal(typeof options.x, 'number', 'Correct output type');
|
||||||
t.end();
|
t.end();
|
||||||
};
|
};
|
||||||
|
|
||||||
parseCornerCoordinateInputs(options, coord, callback);
|
parseCornerCoordinateInputs(options, coord, callback);
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user