mirror of
https://github.com/publiclab/image-sequencer.git
synced 2025-12-09 01:39:59 +01:00
Compare commits
23 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
de20254637 | ||
|
|
10c6ad5865 | ||
|
|
a0f7e6c766 | ||
|
|
2b07ecdb36 | ||
|
|
37122d4c28 | ||
|
|
871453b857 | ||
|
|
ac183205f0 | ||
|
|
be810fb9c4 | ||
|
|
8d3ca0887f | ||
|
|
1de72d7325 | ||
|
|
58a4798674 | ||
|
|
f8006da07e | ||
|
|
09c4f250a2 | ||
|
|
bd490d2515 | ||
|
|
ca79924d11 | ||
|
|
630eb773de | ||
|
|
6ce78f87f4 | ||
|
|
991e9bb29c | ||
|
|
cbbfbff4bc | ||
|
|
768e2ce95d | ||
|
|
51524f747a | ||
|
|
b16b45afa8 | ||
|
|
39418da187 |
80
Gruntfile.js
80
Gruntfile.js
@@ -1,50 +1,50 @@
|
||||
module.exports = function(grunt) {
|
||||
module.exports = function (grunt) {
|
||||
grunt.loadNpmTasks("grunt-browserify");
|
||||
grunt.loadNpmTasks("grunt-contrib-uglify-es");
|
||||
grunt.loadNpmTasks("grunt-browser-sync");
|
||||
|
||||
grunt.loadNpmTasks('grunt-browserify');
|
||||
grunt.loadNpmTasks('grunt-contrib-uglify');
|
||||
require("matchdep")
|
||||
.filterDev("grunt-*")
|
||||
.forEach(grunt.loadNpmTasks);
|
||||
|
||||
require('matchdep').filterDev('grunt-*').forEach(grunt.loadNpmTasks);
|
||||
grunt.initConfig({
|
||||
pkg: grunt.file.readJSON("package.json"),
|
||||
|
||||
grunt.initConfig({
|
||||
|
||||
pkg: grunt.file.readJSON('package.json'),
|
||||
|
||||
watch: {
|
||||
options : {
|
||||
livereload: true
|
||||
},
|
||||
source: {
|
||||
files: [
|
||||
'src/*.js',
|
||||
'src/*/*.js',
|
||||
'Gruntfile.js'
|
||||
],
|
||||
tasks: [ 'build:js' ]
|
||||
}
|
||||
watch: {
|
||||
options: {
|
||||
livereload: true
|
||||
},
|
||||
source: {
|
||||
files: ["src/*.js", "src/*/*.js", "src/*/*/*.js", "Gruntfile.js"],
|
||||
tasks: ["build:js"]
|
||||
}
|
||||
},
|
||||
|
||||
browserify: {
|
||||
dist: {
|
||||
src: ['src/ImageSequencer.js'],
|
||||
dest: 'dist/image-sequencer.js'
|
||||
}
|
||||
},
|
||||
browserify: {
|
||||
dist: {
|
||||
src: ["src/ImageSequencer.js"],
|
||||
dest: "dist/image-sequencer.js"
|
||||
}
|
||||
},
|
||||
|
||||
uglify: {
|
||||
dist: {
|
||||
src: ['./dist/image-sequencer.js'],
|
||||
dest: './dist/image-sequencer.min.js'
|
||||
uglify: {
|
||||
dist: {
|
||||
src: ["./dist/image-sequencer.js"],
|
||||
dest: "./dist/image-sequencer.min.js"
|
||||
}
|
||||
},
|
||||
browserSync: {
|
||||
dev: {
|
||||
options: {
|
||||
watchTask: true,
|
||||
server: "./"
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
/* Default (development): Watch files and build on change. */
|
||||
grunt.registerTask('default', ['watch']);
|
||||
|
||||
grunt.registerTask('build', [
|
||||
'browserify:dist',
|
||||
'uglify:dist'
|
||||
]);
|
||||
|
||||
/* Default (development): Watch files and build on change. */
|
||||
grunt.registerTask("default", ["watch"]);
|
||||
grunt.registerTask("build", ["browserify:dist", "uglify:dist"]);
|
||||
grunt.registerTask("serve", ["browserify:dist", "uglify:dist", "browserSync", "watch"]);
|
||||
};
|
||||
|
||||
28
README.md
28
README.md
@@ -45,7 +45,7 @@ A diagram of this running 5 steps on a single sample image may help explain how
|
||||
|
||||
## Installation
|
||||
|
||||
This library works in the browser, in Node, and on the commandline (CLI), which we think is great.
|
||||
This library works in the browser, in Node, and on the commandline (CLI), which we think is great. You can start a local environement to test the UI with `npm start`
|
||||
|
||||
### Browser
|
||||
|
||||
@@ -204,13 +204,35 @@ modules.
|
||||
sequencer.run();
|
||||
```
|
||||
|
||||
Additionally, an optional callback can be passed to this method.
|
||||
Sequencer can be run with a custom config object
|
||||
|
||||
```js
|
||||
sequencer.run(function(out){
|
||||
// The config object enables custom progress bars in node environment and
|
||||
// ability to run the sequencer from a particular index(of the steps array)
|
||||
|
||||
sequencer.run(config);
|
||||
|
||||
```
|
||||
|
||||
The config object can have the following keys
|
||||
|
||||
```js
|
||||
config: {
|
||||
progressObj: , //A custom object to handle progress bar
|
||||
index: //Index to run the sequencer from (defaults to 0)
|
||||
}
|
||||
```
|
||||
|
||||
Additionally, an optional callback function can be passed to this method.
|
||||
|
||||
```js
|
||||
sequencer.run(function callback(out){
|
||||
// this gets called back.
|
||||
// "out" is the DataURL of the final image.
|
||||
});
|
||||
sequencer.run(config,function callback(out){
|
||||
// the callback is supported with all types of invocations
|
||||
});
|
||||
```
|
||||
|
||||
return value: **`sequencer`** (To allow method chaining)
|
||||
|
||||
35604
dist/image-sequencer.js
vendored
35604
dist/image-sequencer.js
vendored
File diff suppressed because one or more lines are too long
2
dist/image-sequencer.min.js
vendored
2
dist/image-sequencer.min.js
vendored
File diff suppressed because one or more lines are too long
216
examples/demo.js
216
examples/demo.js
@@ -1,208 +1,38 @@
|
||||
window.onload = function() {
|
||||
|
||||
sequencer = ImageSequencer();
|
||||
|
||||
// Load information of all modules (Name, Inputs, Outputs)
|
||||
var modulesInfo = sequencer.modulesInfo();
|
||||
|
||||
// Add modules to the addStep dropdown
|
||||
for(var m in modulesInfo) {
|
||||
$('#addStep select').append(
|
||||
'<option value="'+m+'">'+modulesInfo[m].name+'</option>'
|
||||
for (var m in modulesInfo) {
|
||||
$("#addStep select").append(
|
||||
'<option value="' + m + '">' + modulesInfo[m].name + "</option>"
|
||||
);
|
||||
}
|
||||
|
||||
// Initial definitions
|
||||
var steps = document.querySelector('#steps');
|
||||
var parser = new DOMParser();
|
||||
var reader = new FileReader();
|
||||
// UI for each step:
|
||||
sequencer.setUI(DefaultHtmlStepUi(sequencer));
|
||||
|
||||
// Set the UI in sequencer. This Will generate HTML based on
|
||||
// Image Sequencer events :
|
||||
// onSetup : Called every time a step is added
|
||||
// onDraw : Called every time a step starts draw
|
||||
// onComplete : Called every time a step finishes drawing
|
||||
// onRemove : Called everytime a step is removed
|
||||
// The variable 'step' stores useful data like input and
|
||||
// output values, step information.
|
||||
// See documetation for more details.
|
||||
sequencer.setUI({
|
||||
// UI for the overall demo:
|
||||
var ui = DefaultHtmlSequencerUi(sequencer);
|
||||
|
||||
onSetup: function(step) {
|
||||
|
||||
if (step.options && step.options.description) step.description = step.options.description
|
||||
|
||||
step.ui = '\
|
||||
<div class="row step">\
|
||||
<div class="col-md-4 details">\
|
||||
<h3>'+step.name+'</h3>\
|
||||
<p><i>'+(step.description || '')+'</i></p>\
|
||||
</div>\
|
||||
<div class="col-md-8">\
|
||||
<div class="load" style="display:none;"><i class="fa fa-circle-o-notch fa-spin"></i></div>\
|
||||
<a><img alt="" class=“img-thumbnail” /></a>\
|
||||
</div>\
|
||||
</div>\
|
||||
';
|
||||
|
||||
var tools =
|
||||
'<div class="tools btn-group">\
|
||||
<button confirm="Are you sure?" class="remove btn btn btn-default">\
|
||||
<i class="fa fa-trash"></i>\
|
||||
</button>\
|
||||
</div>';
|
||||
|
||||
step.ui = parser.parseFromString(step.ui,'text/html');
|
||||
step.ui = step.ui.querySelector('div.row');
|
||||
step.linkElement = step.ui.querySelector('a');
|
||||
step.imgElement = step.ui.querySelector('a img');
|
||||
|
||||
if(sequencer.modulesInfo().hasOwnProperty(step.name)) {
|
||||
var inputs = sequencer.modulesInfo(step.name).inputs;
|
||||
var outputs = sequencer.modulesInfo(step.name).outputs;
|
||||
var merged = Object.assign(inputs, outputs); // combine outputs w inputs
|
||||
for (var paramName in merged) {
|
||||
var isInput = inputs.hasOwnProperty(paramName);
|
||||
var html = "";
|
||||
var inputDesc = (isInput)?inputs[paramName]:{};
|
||||
if (!isInput) {
|
||||
html += "<span class=\"output\"></span>";
|
||||
} else if (inputDesc.type.toLowerCase() == "select") {
|
||||
html += "<select class=\"form-control\" name=\""+paramName+"\">";
|
||||
for (var option in inputDesc.values) {
|
||||
html += "<option>"+inputDesc.values[option]+"</option>";
|
||||
}
|
||||
html += "</select>";
|
||||
} else {
|
||||
html = "<input class=\"form-control\" type=\""+inputDesc.type+"\" name=\""+paramName+"\">";
|
||||
}
|
||||
var div = document.createElement('div');
|
||||
div.className = "row";
|
||||
div.setAttribute('name', paramName);
|
||||
var description = inputs[paramName].desc || paramName;
|
||||
div.innerHTML = "<div class='det'>\
|
||||
<label for='" + paramName + "'>" + description + "</label>\
|
||||
"+html+"\
|
||||
</div>";
|
||||
step.ui.querySelector('div.details').appendChild(div);
|
||||
}
|
||||
$(step.ui.querySelector('div.details')).append("<p><button class='btn btn-default btn-save'>Save</button></p>");
|
||||
|
||||
function saveOptions() {
|
||||
$(step.ui.querySelector('div.details')).find('input,select').each(function(i, input) {
|
||||
step.options[$(input).attr('name')] = input.value;
|
||||
});
|
||||
sequencer.run();
|
||||
}
|
||||
|
||||
// on clicking Save in the details pane of the step
|
||||
$(step.ui.querySelector('div.details .btn-save')).click(saveOptions);
|
||||
}
|
||||
|
||||
if(step.name != "load-image")
|
||||
step.ui.querySelector('div.details').appendChild(
|
||||
parser.parseFromString(tools,'text/html')
|
||||
.querySelector('div')
|
||||
);
|
||||
|
||||
steps.appendChild(step.ui);
|
||||
},
|
||||
|
||||
onDraw: function(step) {
|
||||
$(step.ui.querySelector('.load')).show();
|
||||
$(step.ui.querySelector('img')).hide();
|
||||
},
|
||||
|
||||
onComplete: function(step) {
|
||||
$(step.ui.querySelector('.load')).hide();
|
||||
$(step.ui.querySelector('img')).show();
|
||||
|
||||
step.imgElement.src = step.output;
|
||||
step.linkElement.href = step.output;
|
||||
|
||||
function fileExtension(output) {
|
||||
return output.split('/')[1].split(';')[0];
|
||||
}
|
||||
|
||||
step.linkElement.download = step.name + "." + fileExtension(step.output);
|
||||
step.linkElement.target = "_blank";
|
||||
|
||||
if(sequencer.modulesInfo().hasOwnProperty(step.name)) {
|
||||
var inputs = sequencer.modulesInfo(step.name).inputs;
|
||||
var outputs = sequencer.modulesInfo(step.name).outputs;
|
||||
for (var i in inputs) {
|
||||
if (step.options[i] !== undefined &&
|
||||
inputs[i].type.toLowerCase() === "input") step.ui.querySelector('div[name="' + i + '"] input')
|
||||
.value = step.options[i];
|
||||
if (step.options[i] !== undefined &&
|
||||
inputs[i].type.toLowerCase() === "select") step.ui.querySelector('div[name="' + i + '"] select')
|
||||
.value = step.options[i];
|
||||
}
|
||||
for (var i in outputs) {
|
||||
if (step[i] !== undefined) step.ui.querySelector('div[name="'+i+'"] input')
|
||||
.value = step[i];
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
onRemove: function(step) {
|
||||
step.ui.remove();
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
sequencer.loadImage('images/tulips.png', function loadImageUI() {
|
||||
|
||||
// look up needed steps from Url Hash:
|
||||
var hash = getUrlHashParameter('steps');
|
||||
|
||||
if (hash) {
|
||||
var stepsFromHash = hash.split(',');
|
||||
stepsFromHash.forEach(function eachStep(step) {
|
||||
sequencer.addSteps(step);
|
||||
});
|
||||
sequencer.run();
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
$('#addStep select').on('change', selectNewStepUI);
|
||||
|
||||
function selectNewStepUI() {
|
||||
var m = $('#addStep select').val();
|
||||
$('#addStep .info').html(sequencer.modulesInfo(m).description);
|
||||
}
|
||||
|
||||
function addStepUI() {
|
||||
var options = {};
|
||||
var inputs = $('#options input, #options select');
|
||||
$.each(inputs, function() {
|
||||
options[this.name] = $(this).val();
|
||||
});
|
||||
if($('#addStep select').val() == "none") return;
|
||||
// add to URL hash too
|
||||
var hash = getUrlHashParameter('steps') || '';
|
||||
if (hash != '') hash += ',';
|
||||
setUrlHashParameter('steps', hash + $('#addStep select').val())
|
||||
sequencer.addSteps($('#addStep select').val(),options).run();
|
||||
}
|
||||
|
||||
$('#addStep button').on('click', addStepUI);
|
||||
|
||||
|
||||
function removeStepUI(){
|
||||
var index = $('button.remove').index(this) + 1;
|
||||
sequencer.removeSteps(index).run();
|
||||
// remove from URL hash too
|
||||
var urlHash = getUrlHashParameter('steps').split(',');
|
||||
urlHash.splice(index - 1, 1);
|
||||
setUrlHashParameter("steps", urlHash.join(','));
|
||||
}
|
||||
|
||||
$('body').on('click','button.remove', removeStepUI);
|
||||
sequencer.loadImage("images/tulips.png", ui.onLoad);
|
||||
|
||||
$("#addStep select").on("change", ui.selectNewStepUi);
|
||||
$("#addStep button").on("click", ui.addStepUi);
|
||||
$('body').on('click', 'button.remove', ui.removeStepUi);
|
||||
|
||||
// image selection and drag/drop handling from examples/lib/imageSelection.js
|
||||
setupFileHandling(sequencer);
|
||||
|
||||
}
|
||||
sequencer.setInputStep({
|
||||
dropZoneSelector: "#dropzone",
|
||||
fileInputSelector: "#fileInput",
|
||||
onLoad: function onFileReaderLoad(progress) {
|
||||
var reader = progress.target;
|
||||
var step = sequencer.images.image1.steps[0];
|
||||
step.output.src = reader.result;
|
||||
sequencer.run(0);
|
||||
step.options.step.imgElement.src = reader.result;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
}
|
||||
</style>
|
||||
|
||||
<script src="../node_modules/jquery/dist/jquery.min.js"></script>
|
||||
<script src="../dist/image-sequencer.js"></script>
|
||||
</head>
|
||||
|
||||
|
||||
@@ -3,76 +3,92 @@
|
||||
<html>
|
||||
|
||||
|
||||
<head>
|
||||
<head>
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="content-type" content="text/html; charset=UTF8">
|
||||
<link rel="icon" sizes="192x192" href="../icons/ic_192.png">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="content-type" content="text/html; charset=UTF8">
|
||||
<link rel="icon" sizes="192x192" href="../icons/ic_192.png">
|
||||
|
||||
|
||||
<title>Image Sequencer</title>
|
||||
<title>Image Sequencer</title>
|
||||
|
||||
<script src="../node_modules/jquery/dist/jquery.min.js"></script>
|
||||
<script src="../node_modules/bootstrap/dist/js/bootstrap.min.js"></script>
|
||||
<script src="../dist/image-sequencer.js" charset="utf-8"></script>
|
||||
<script src="lib/urlHash.js" charset="utf-8"></script>
|
||||
<script src="lib/imageSelection.js" charset="utf-8"></script>
|
||||
<script src="demo.js" charset="utf-8"></script>
|
||||
<script src="../node_modules/jquery/dist/jquery.min.js"></script>
|
||||
<script src="../node_modules/bootstrap/dist/js/bootstrap.min.js"></script>
|
||||
<script src="../dist/image-sequencer.js" charset="utf-8"></script>
|
||||
<script src="lib/urlHash.js" charset="utf-8"></script>
|
||||
<script src="lib/defaultHtmlStepUi.js" charset="utf-8"></script>
|
||||
<script src="lib/defaultHtmlSequencerUi.js" charset="utf-8"></script>
|
||||
<script src="demo.js" charset="utf-8"></script>
|
||||
<!-- for crop module: -->
|
||||
<script src="../node_modules/imgareaselect/jquery.imgareaselect.dev.js"></script>
|
||||
|
||||
</head>
|
||||
</head>
|
||||
|
||||
|
||||
<body>
|
||||
<body>
|
||||
|
||||
<link href="../node_modules/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="../node_modules/font-awesome/css/font-awesome.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="demo.css">
|
||||
<link href="../node_modules/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="../node_modules/font-awesome/css/font-awesome.min.css" rel="stylesheet">
|
||||
<link href="../node_modules/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="../node_modules/font-awesome/css/font-awesome.min.css" rel="stylesheet">
|
||||
<!-- for crop module: -->
|
||||
<link href="../node_modules/imgareaselect/distfiles/css/imgareaselect-default.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="demo.css">
|
||||
|
||||
<div class="container-fluid">
|
||||
<div class="container-fluid">
|
||||
|
||||
<header class="text-center">
|
||||
<h1>Image Sequencer</h1>
|
||||
<p>
|
||||
A pure JavaScript sequential image processing system, inspired by storyboards. Instead of modifying the original image, it creates a new image at each step in a sequence.
|
||||
<a href="https://publiclab.org/image-sequencer">Learn more</a>
|
||||
</p>
|
||||
<p>
|
||||
Open Source <a href="https://github.com/publiclab/image-sequencer"><i class="fa fa-github"></i></a> by <a href="https://publiclab.org">Public Lab</a>
|
||||
</p>
|
||||
</header>
|
||||
|
||||
<div id="dropzone">
|
||||
<p><i>Select or drag in an image to start!</i></p>
|
||||
<center><input type="file" id="fileInput" value=""></center>
|
||||
</div>
|
||||
|
||||
<section id="steps" class="row"></section>
|
||||
|
||||
<hr />
|
||||
|
||||
<section id="addStep" class="panel panel-primary">
|
||||
<div class="form-inline">
|
||||
<div class="panel-body">
|
||||
<div style="text-align:center;">
|
||||
<select class="form-control input-lg">
|
||||
<option value="none" disabled selected>Select a new step...</option>
|
||||
</select>
|
||||
<button class="btn btn-success btn-lg" name="add">Add Step</button>
|
||||
</div>
|
||||
<br />
|
||||
<p class="info" style="padding:8px;">Select a new module to add to your sequence.</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<header class="text-center">
|
||||
<h1>Image Sequencer</h1>
|
||||
<p>
|
||||
A pure JavaScript sequential image processing system, inspired by storyboards. Instead of modifying the original image, it
|
||||
creates a new image at each step in a sequence.
|
||||
<a href="https://publiclab.org/image-sequencer">Learn more</a>
|
||||
</p>
|
||||
<p>
|
||||
Open Source
|
||||
<a href="https://github.com/publiclab/image-sequencer">
|
||||
<i class="fa fa-github"></i>
|
||||
</a> by
|
||||
<a href="https://publiclab.org">Public Lab</a>
|
||||
</p>
|
||||
</header>
|
||||
|
||||
<div id="dropzone">
|
||||
<p>
|
||||
<i>Select or drag in an image to start!</i>
|
||||
</p>
|
||||
<center>
|
||||
<input type="file" id="fileInput" value="">
|
||||
</center>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
<section id="steps" class="row"></section>
|
||||
|
||||
<hr />
|
||||
|
||||
<section id="addStep" class="panel panel-primary">
|
||||
<div class="form-inline">
|
||||
<div class="panel-body">
|
||||
<div style="text-align:center;">
|
||||
<select class="form-control input-lg" id="selectStep">
|
||||
<option value="none" disabled selected>Select a new step...</option>
|
||||
</select>
|
||||
<button class="btn btn-success btn-lg" name="add">Add Step</button>
|
||||
</div>
|
||||
<br />
|
||||
<p class="info" style="padding:8px;">Select a new module to add to your sequence.</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
$(function () {
|
||||
var sequencer;
|
||||
})
|
||||
</script>
|
||||
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
||||
60
examples/lib/defaultHtmlSequencerUi.js
Normal file
60
examples/lib/defaultHtmlSequencerUi.js
Normal file
@@ -0,0 +1,60 @@
|
||||
function DefaultHtmlSequencerUi(_sequencer, options) {
|
||||
|
||||
options = options || {};
|
||||
var addStepSel = options.addStepSel = options.addStepSel || "#addStep";
|
||||
var removeStepSel = options.removeStepSel = options.removeStepSel || "button.remove";
|
||||
var selectStepSel = options.selectStepSel = options.selectStepSel || "#selectStep";
|
||||
|
||||
function onLoad() {
|
||||
importStepsFromUrlHash();
|
||||
}
|
||||
|
||||
// look up needed steps from Url Hash:
|
||||
function importStepsFromUrlHash() {
|
||||
var hash = getUrlHashParameter("steps");
|
||||
|
||||
if (hash) {
|
||||
_sequencer.importString(hash);
|
||||
_sequencer.run({index:0});
|
||||
}
|
||||
setUrlHashParameter("steps", sequencer.toString());
|
||||
}
|
||||
|
||||
function selectNewStepUi() {
|
||||
var m = $(addStepSel + " select").val();
|
||||
$(addStepSel + " .info").html(_sequencer.modulesInfo(m).description);
|
||||
}
|
||||
|
||||
function removeStepUi() {
|
||||
var index = $(removeStepSel).index(this) + 1;
|
||||
sequencer.removeSteps(index).run({index : sequencer.images.image1.steps.length-1});
|
||||
// remove from URL hash too
|
||||
setUrlHashParameter("steps", sequencer.toString());
|
||||
}
|
||||
|
||||
function addStepUi() {
|
||||
if ($(addStepSel + " select").val() == "none") return;
|
||||
|
||||
var newStepName = $(addStepSel + " select").val();
|
||||
|
||||
/*
|
||||
* after adding the step we run the sequencer from defined step
|
||||
* and since loadImage is not a part of the drawarray the step lies at current
|
||||
* length - 2 of the drawarray
|
||||
*/
|
||||
_sequencer
|
||||
.addSteps(newStepName, options)
|
||||
.run({index: _sequencer.images.image1.steps.length - 2});
|
||||
|
||||
// add to URL hash too
|
||||
setUrlHashParameter("steps", _sequencer.toString());
|
||||
}
|
||||
|
||||
return {
|
||||
onLoad: onLoad,
|
||||
importStepsFromUrlHash: importStepsFromUrlHash,
|
||||
selectNewStepUi: selectNewStepUi,
|
||||
removeStepUi: removeStepUi,
|
||||
addStepUi: addStepUi
|
||||
}
|
||||
}
|
||||
185
examples/lib/defaultHtmlStepUi.js
Normal file
185
examples/lib/defaultHtmlStepUi.js
Normal file
@@ -0,0 +1,185 @@
|
||||
// Set the UI in sequencer. This Will generate HTML based on
|
||||
// Image Sequencer events :
|
||||
// onSetup : Called every time a step is added
|
||||
// onDraw : Called every time a step starts draw
|
||||
// onComplete : Called every time a step finishes drawing
|
||||
// onRemove : Called everytime a step is removed
|
||||
// The variable 'step' stores useful data like input and
|
||||
// output values, step information.
|
||||
// See documetation for more details.
|
||||
function DefaultHtmlStepUi(_sequencer, options) {
|
||||
|
||||
options = options || {};
|
||||
var stepsEl = options.stepsEl || document.querySelector("#steps");
|
||||
var selectStepSel = options.selectStepSel = options.selectStepSel || "#selectStep";
|
||||
|
||||
function onSetup(step) {
|
||||
if (step.options && step.options.description)
|
||||
step.description = step.options.description;
|
||||
|
||||
step.ui =
|
||||
'\
|
||||
<div class="row step">\
|
||||
<div class="col-md-4 details">\
|
||||
<h3>' +
|
||||
step.name +
|
||||
"</h3>\
|
||||
<p><i>" +
|
||||
(step.description || "") +
|
||||
'</i></p>\
|
||||
</div>\
|
||||
<div class="col-md-8">\
|
||||
<div class="load" style="display:none;"><i class="fa fa-circle-o-notch fa-spin"></i></div>\
|
||||
<a><img alt="" style="max-width=100%" class="img-thumbnail" /></a>\
|
||||
</div>\
|
||||
</div>\
|
||||
';
|
||||
|
||||
var tools =
|
||||
'<div class="tools btn-group">\
|
||||
<button confirm="Are you sure?" class="remove btn btn btn-default">\
|
||||
<i class="fa fa-trash"></i>\
|
||||
</button>\
|
||||
</div>';
|
||||
|
||||
var parser = new DOMParser();
|
||||
step.ui = parser.parseFromString(step.ui, "text/html");
|
||||
step.ui = step.ui.querySelector("div.row");
|
||||
step.linkElement = step.ui.querySelector("a");
|
||||
step.imgElement = step.ui.querySelector("a img");
|
||||
|
||||
if (_sequencer.modulesInfo().hasOwnProperty(step.name)) {
|
||||
var inputs = _sequencer.modulesInfo(step.name).inputs;
|
||||
var outputs = _sequencer.modulesInfo(step.name).outputs;
|
||||
var merged = Object.assign(inputs, outputs); // combine outputs w inputs
|
||||
|
||||
for (var paramName in merged) {
|
||||
var isInput = inputs.hasOwnProperty(paramName);
|
||||
var html = "";
|
||||
var inputDesc = isInput ? inputs[paramName] : {};
|
||||
if (!isInput) {
|
||||
html += '<span class="output"></span>';
|
||||
} else if (inputDesc.type.toLowerCase() == "select") {
|
||||
html += '<select class="form-control" name="' + paramName + '">';
|
||||
for (var option in inputDesc.values) {
|
||||
html += "<option>" + inputDesc.values[option] + "</option>";
|
||||
}
|
||||
html += "</select>";
|
||||
} else {
|
||||
html =
|
||||
'<input class="form-control" type="' +
|
||||
inputDesc.type +
|
||||
'" name="' +
|
||||
paramName +
|
||||
'">';
|
||||
}
|
||||
|
||||
var div = document.createElement("div");
|
||||
div.className = "row";
|
||||
div.setAttribute("name", paramName);
|
||||
var description = inputs[paramName].desc || paramName;
|
||||
div.innerHTML =
|
||||
"<div class='det'>\
|
||||
<label for='" +
|
||||
paramName +
|
||||
"'>" +
|
||||
description +
|
||||
"</label>\
|
||||
" +
|
||||
html +
|
||||
"\
|
||||
</div>";
|
||||
step.ui.querySelector("div.details").appendChild(div);
|
||||
}
|
||||
$(step.ui.querySelector("div.details")).append(
|
||||
"<p><button class='btn btn-default btn-save'>Save</button></p>"
|
||||
);
|
||||
|
||||
function saveOptions() {
|
||||
$(step.ui.querySelector("div.details"))
|
||||
.find("input,select")
|
||||
.each(function(i, input) {
|
||||
step.options[$(input).attr("name")] = input.value;
|
||||
});
|
||||
_sequencer.run({index: _sequencer.images.image1.steps.length - 2});
|
||||
|
||||
// modify the url hash
|
||||
setUrlHashParameter("steps", _sequencer.toString());
|
||||
}
|
||||
|
||||
// on clicking Save in the details pane of the step
|
||||
$(step.ui.querySelector("div.details .btn-save")).click(saveOptions);
|
||||
}
|
||||
|
||||
if (step.name != "load-image")
|
||||
step.ui
|
||||
.querySelector("div.details")
|
||||
.appendChild(
|
||||
parser.parseFromString(tools, "text/html").querySelector("div")
|
||||
);
|
||||
|
||||
stepsEl.appendChild(step.ui);
|
||||
}
|
||||
|
||||
function onDraw(step) {
|
||||
$(step.ui.querySelector(".load")).show();
|
||||
$(step.ui.querySelector("img")).hide();
|
||||
}
|
||||
|
||||
function onComplete(step) {
|
||||
$(step.ui.querySelector(".load")).hide();
|
||||
$(step.ui.querySelector("img")).show();
|
||||
|
||||
step.imgElement.src = step.output;
|
||||
step.linkElement.href = step.output;
|
||||
|
||||
// TODO: use a generalized version of this
|
||||
function fileExtension(output) {
|
||||
return output.split("/")[1].split(";")[0];
|
||||
}
|
||||
|
||||
step.linkElement.download = step.name + "." + fileExtension(step.output);
|
||||
step.linkElement.target = "_blank";
|
||||
|
||||
// fill inputs with stored step options
|
||||
if (_sequencer.modulesInfo().hasOwnProperty(step.name)) {
|
||||
var inputs = _sequencer.modulesInfo(step.name).inputs;
|
||||
var outputs = _sequencer.modulesInfo(step.name).outputs;
|
||||
for (var i in inputs) {
|
||||
if (
|
||||
step.options[i] !== undefined &&
|
||||
inputs[i].type.toLowerCase() === "input"
|
||||
)
|
||||
step.ui.querySelector('div[name="' + i + '"] input').value =
|
||||
step.options[i];
|
||||
if (
|
||||
step.options[i] !== undefined &&
|
||||
inputs[i].type.toLowerCase() === "select"
|
||||
)
|
||||
step.ui.querySelector('div[name="' + i + '"] select').value =
|
||||
step.options[i];
|
||||
}
|
||||
for (var i in outputs) {
|
||||
if (step[i] !== undefined)
|
||||
step.ui.querySelector('div[name="' + i + '"] input').value =
|
||||
step[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function onRemove(step) {
|
||||
step.ui.remove();
|
||||
}
|
||||
|
||||
function getPreview() {
|
||||
return step.imgElement;
|
||||
}
|
||||
|
||||
return {
|
||||
getPreview: getPreview,
|
||||
onSetup: onSetup,
|
||||
onComplete: onComplete,
|
||||
onRemove: onRemove,
|
||||
onDraw: onDraw
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
function setupFileHandling(_sequencer, dropzoneId, fileInputId) {
|
||||
|
||||
dropzoneId = dropzoneId || "dropzone";
|
||||
var dropzone = $('#' + dropzoneId);
|
||||
|
||||
fileInputId = fileInputId || "fileInput";
|
||||
var fileInput = $('#' + fileInputId);
|
||||
|
||||
var reader = new FileReader();
|
||||
|
||||
function handleFile(e) {
|
||||
|
||||
e.preventDefault();
|
||||
e.stopPropagation(); // stops the browser from redirecting.
|
||||
|
||||
if (e.target && e.target.files) var file = e.target.files[0];
|
||||
else var file = e.dataTransfer.files[0];
|
||||
if(!file) return;
|
||||
|
||||
var reader = new FileReader();
|
||||
|
||||
reader.onload = function onFileReaderLoad() {
|
||||
var loadStep = _sequencer.images.image1.steps[0];
|
||||
loadStep.output.src = reader.result;
|
||||
_sequencer.run(0);
|
||||
loadStep.options.step.imgElement.src = reader.result;
|
||||
}
|
||||
|
||||
reader.readAsDataURL(file);
|
||||
}
|
||||
|
||||
fileInput.on('change', handleFile);
|
||||
|
||||
dropzone[0].addEventListener('drop', handleFile, false);
|
||||
|
||||
dropzone.on('dragover', function onDragover(e) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
e.dataTransfer.dropEffect = 'copy'; // Explicitly show this is a copy.
|
||||
}, false);
|
||||
|
||||
dropzone.on('dragenter', function onDragEnter(e) {
|
||||
dropzone.addClass('hover');
|
||||
});
|
||||
|
||||
dropzone.on('dragleave', function onDragLeave(e) {
|
||||
dropzone.removeClass('hover');
|
||||
});
|
||||
|
||||
}
|
||||
169
index.js
169
index.js
@@ -1,11 +1,11 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
require('./src/ImageSequencer');
|
||||
sequencer = ImageSequencer({ui: false});
|
||||
var Spinner = require('ora')
|
||||
require("./src/ImageSequencer");
|
||||
sequencer = ImageSequencer({ ui: false });
|
||||
var Spinner = require("ora");
|
||||
|
||||
var program = require('commander');
|
||||
var readlineSync = require('readline-sync');
|
||||
var program = require("commander");
|
||||
var readlineSync = require("readline-sync");
|
||||
|
||||
function exit(message) {
|
||||
console.error(message);
|
||||
@@ -13,127 +13,140 @@ function exit(message) {
|
||||
}
|
||||
|
||||
program
|
||||
.version('0.1.0')
|
||||
.option('-i, --image [PATH/URL]', 'Input image URL')
|
||||
.option('-s, --step [step-name]', 'Name of the step to be added.')
|
||||
.option('-o, --output [PATH]', 'Directory where output will be stored.')
|
||||
.option('-b, --basic','Basic mode outputs only final image')
|
||||
.option('-c, --config [Object]', 'Options for the step')
|
||||
.parse(process.argv);
|
||||
.version("0.1.0")
|
||||
.option("-i, --image [PATH/URL]", "Input image URL")
|
||||
.option("-s, --step [step-name]", "Name of the step to be added.")
|
||||
.option("-o, --output [PATH]", "Directory where output will be stored.")
|
||||
.option("-b, --basic", "Basic mode outputs only final image")
|
||||
.option("-c, --config [Object]", "Options for the step")
|
||||
.parse(process.argv);
|
||||
|
||||
// Parse step into an array to allow for multiple steps.
|
||||
if(!program.step) exit("No steps passed")
|
||||
if (!program.step) exit("No steps passed");
|
||||
program.step = program.step.split(" ");
|
||||
|
||||
// User must input an image.
|
||||
if(!program.image) exit("Can't read file.")
|
||||
if (!program.image) exit("Can't read file.");
|
||||
|
||||
// User must input an image.
|
||||
require('fs').access(program.image, function(err){
|
||||
if(err) exit("Can't read file.")
|
||||
require("fs").access(program.image, function(err) {
|
||||
if (err) exit("Can't read file.");
|
||||
});
|
||||
|
||||
// User must input a step. If steps exist, check that every step is a valid step.
|
||||
if(!program.step || !validateSteps(program.step))
|
||||
exit("Please ensure all steps are valid.");
|
||||
if (!program.step || !validateSteps(program.step))
|
||||
exit("Please ensure all steps are valid.");
|
||||
|
||||
// If there's no user defined output directory, select a default directory.
|
||||
program.output = program.output || "./output/";
|
||||
|
||||
// Set sequencer to log module outputs, if any.
|
||||
sequencer.setUI({
|
||||
|
||||
onComplete: function(step) {
|
||||
|
||||
// Get information of outputs.
|
||||
step.info = sequencer.modulesInfo(step.name);
|
||||
|
||||
|
||||
for (var output in step.info.outputs) {
|
||||
console.log("["+program.step+"]: "+output+" = "+step[output]);
|
||||
console.log("[" + program.step + "]: " + output + " = " + step[output]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
// Finally, if everything is alright, load the image, add the steps and run the sequencer.
|
||||
sequencer.loadImages(program.image,function(){
|
||||
console.warn('\x1b[33m%s\x1b[0m', "Please wait \n output directory generated will be empty until the execution is complete")
|
||||
|
||||
sequencer.loadImages(program.image, function() {
|
||||
console.warn(
|
||||
"\x1b[33m%s\x1b[0m",
|
||||
"Please wait \n output directory generated will be empty until the execution is complete"
|
||||
);
|
||||
|
||||
//Generate the Output Directory
|
||||
require('./src/CliUtils').makedir(program.output,()=>{
|
||||
console.log("Files will be exported to \""+program.output+"\"");
|
||||
|
||||
if(program.basic) console.log("Basic mode is enabled, outputting only final image")
|
||||
|
||||
require("./src/CliUtils").makedir(program.output, () => {
|
||||
console.log('Files will be exported to "' + program.output + '"');
|
||||
|
||||
if (program.basic)
|
||||
console.log("Basic mode is enabled, outputting only final image");
|
||||
|
||||
// Iterate through the steps and retrieve their inputs.
|
||||
program.step.forEach(function(step){
|
||||
program.step.forEach(function(step) {
|
||||
var options = Object.assign({}, sequencer.modulesInfo(step).inputs);
|
||||
|
||||
|
||||
// If inputs exists, print to console.
|
||||
if (Object.keys(options).length) {
|
||||
console.log("[" + step + "]: Inputs");
|
||||
}
|
||||
|
||||
|
||||
// If inputs exists, print them out with descriptions.
|
||||
Object.keys(options).forEach(function(input) {
|
||||
// The array below creates a variable number of spaces. This is done with (length + 1).
|
||||
// The array below creates a variable number of spaces. This is done with (length + 1).
|
||||
// The extra 4 that makes it (length + 5) is to account for the []: characters
|
||||
console.log(new Array(step.length + 5).join(' ') + input + ": " + options[input].desc);
|
||||
console.log(
|
||||
new Array(step.length + 5).join(" ") +
|
||||
input +
|
||||
": " +
|
||||
options[input].desc
|
||||
);
|
||||
});
|
||||
|
||||
if(program.config){
|
||||
try{
|
||||
|
||||
if (program.config) {
|
||||
try {
|
||||
program.config = JSON.parse(program.config);
|
||||
console.log(`The parsed options object: `, program.config);
|
||||
}
|
||||
catch(e){
|
||||
console.error('\x1b[31m%s\x1b[0m',`Options(Config) is not a not valid JSON Fallback activate`);
|
||||
} catch (e) {
|
||||
console.error(
|
||||
"\x1b[31m%s\x1b[0m",
|
||||
`Options(Config) is not a not valid JSON Fallback activate`
|
||||
);
|
||||
program.config = false;
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
if(program.config && validateConfig(program.config,options)){
|
||||
if (program.config && validateConfig(program.config, options)) {
|
||||
console.log("Now using Options object");
|
||||
Object.keys(options).forEach(function (input) {
|
||||
Object.keys(options).forEach(function(input) {
|
||||
options[input] = program.config[input];
|
||||
})
|
||||
}
|
||||
else{
|
||||
});
|
||||
} else {
|
||||
// If inputs exist, iterate through them and prompt for values.
|
||||
Object.keys(options).forEach(function(input) {
|
||||
var value = readlineSync.question("[" + step + "]: Enter a value for " + input + " (" + options[input].type + ", default: " + options[input].default + "): ");
|
||||
var value = readlineSync.question(
|
||||
"[" +
|
||||
step +
|
||||
"]: Enter a value for " +
|
||||
input +
|
||||
" (" +
|
||||
options[input].type +
|
||||
", default: " +
|
||||
options[input].default +
|
||||
"): "
|
||||
);
|
||||
options[input] = value;
|
||||
});
|
||||
}
|
||||
// Add the step and its inputs to the sequencer.
|
||||
sequencer.addSteps(step, options);
|
||||
});
|
||||
|
||||
var spinnerObj = Spinner('Your Image is being processed..').start();
|
||||
|
||||
// Run the sequencer.
|
||||
sequencer.run(spinnerObj,function(){
|
||||
|
||||
var spinnerObj = { succeed: () => {}, stop: () => {} };
|
||||
if (!process.env.TEST)
|
||||
spinnerObj = Spinner("Your Image is being processed..").start();
|
||||
|
||||
// Run the sequencer.
|
||||
sequencer.run({progressObj: spinnerObj}, function() {
|
||||
// Export all images or final image as binary files.
|
||||
sequencer.exportBin(program.output,program.basic);
|
||||
sequencer.exportBin(program.output, program.basic);
|
||||
|
||||
//check if spinner was not overriden stop it
|
||||
if(!spinnerObj.overrideFlag) {
|
||||
spinnerObj.succeed()
|
||||
console.log(`\nDone!!`)
|
||||
if (!spinnerObj.overrideFlag) {
|
||||
spinnerObj.succeed();
|
||||
console.log(`\nDone!!`);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
// Takes an array of steps and checks if they are valid steps for the sequencer.
|
||||
function validateSteps(steps) {
|
||||
|
||||
// Assume all are valid in the beginning.
|
||||
// Assume all are valid in the beginning.
|
||||
var valid = true;
|
||||
steps.forEach(function(step) {
|
||||
// If any step in the array is not valid (not a property of modulesInfo), set valid to false.
|
||||
@@ -141,25 +154,29 @@ function validateSteps(steps) {
|
||||
valid = false;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// Return valid. (If all of the steps are valid properties, valid will have remained true).
|
||||
return valid;
|
||||
}
|
||||
|
||||
//Takes config and options object and checks if all the keys exist in config
|
||||
function validateConfig(config_,options_){
|
||||
//Takes config and options object and checks if all the keys exist in config
|
||||
function validateConfig(config_, options_) {
|
||||
options_ = Object.keys(options_);
|
||||
if (
|
||||
(function(){
|
||||
for(var input in options_){
|
||||
if(!config_[options_[input]]){
|
||||
console.error('\x1b[31m%s\x1b[0m',`Options Object does not have the required details "${options_[input]}" not specified. Fallback case activated`);
|
||||
(function() {
|
||||
for (var input in options_) {
|
||||
if (!config_[options_[input]]) {
|
||||
console.error(
|
||||
"\x1b[31m%s\x1b[0m",
|
||||
`Options Object does not have the required details "${
|
||||
options_[input]
|
||||
}" not specified. Fallback case activated`
|
||||
);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
})()
|
||||
== false)
|
||||
})() == false
|
||||
)
|
||||
return false;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
else return true;
|
||||
}
|
||||
|
||||
11
package.json
11
package.json
@@ -1,11 +1,12 @@
|
||||
{
|
||||
"name": "image-sequencer",
|
||||
"version": "1.3.0",
|
||||
"version": "2.0.0",
|
||||
"description": "A modular JavaScript image manipulation library modeled on a storyboard.",
|
||||
"main": "src/ImageSequencer.js",
|
||||
"scripts": {
|
||||
"debug": "node ./index.js -i ./examples/images/monarch.png -s",
|
||||
"test": "tape test/**/*.js test/*.js | tap-spec; browserify test/modules/image-sequencer.js test/modules/chain.js test/modules/replace.js | tape-run --render=\"tap-spec\""
|
||||
"debug": "TEST=true node ./index.js -i ./examples/images/monarch.png -s invert",
|
||||
"test": "TEST=true tape test/**/*.js test/*.js | tap-spec; browserify test/modules/image-sequencer.js test/modules/chain.js test/modules/replace.js test/modules/import-export.js test/modules/run.js | tape-run --render=\"tap-spec\"",
|
||||
"start": "grunt serve"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -28,6 +29,7 @@
|
||||
"fisheyegl": "^0.1.2",
|
||||
"font-awesome": "~4.5.0",
|
||||
"get-pixels": "~3.3.0",
|
||||
"imgareaselect": "git://github.com/jywarren/imgareaselect.git#v1.0.0-rc.2",
|
||||
"jquery": "~2",
|
||||
"jsqr": "^0.2.2",
|
||||
"lodash": "^4.17.5",
|
||||
@@ -42,9 +44,10 @@
|
||||
"browserify": "13.0.0",
|
||||
"data-uri-to-buffer": "^2.0.0",
|
||||
"grunt": "^0.4.5",
|
||||
"grunt-browser-sync": "^2.2.0",
|
||||
"grunt-browserify": "^5.0.0",
|
||||
"grunt-contrib-concat": "^0.5.0",
|
||||
"grunt-contrib-uglify-es": "git://github.com/gruntjs/grunt-contrib-uglify.git#harmony",
|
||||
"grunt-contrib-uglify-es": "^3.3.0",
|
||||
"grunt-contrib-watch": "^0.6.1",
|
||||
"image-filter-core": "~1.0.0",
|
||||
"image-filter-threshold": "~1.0.0",
|
||||
|
||||
@@ -1,34 +1,37 @@
|
||||
// add steps to the sequencer
|
||||
function AddStep(ref, image, name, o) {
|
||||
// TODO: reduce redundancy with InsertStep; this should be a specific usage of InsertStep at the final position
|
||||
function AddStep(_sequencer, image, name, o) {
|
||||
|
||||
function addStep(image, name, o_) {
|
||||
var moduleInfo = ref.modules[name][1];
|
||||
if (_sequencer.modules[name]) var moduleInfo = _sequencer.modules[name][1];
|
||||
else console.log('Module ' + name + ' not found.');
|
||||
|
||||
var o = ref.copy(o_);
|
||||
o.number = ref.options.sequencerCounter++; // gives a unique ID to each step
|
||||
var o = _sequencer.copy(o_);
|
||||
o.number = _sequencer.options.sequencerCounter++; // gives a unique ID to each step
|
||||
o.name = o_.name || name || moduleInfo.name;
|
||||
o.description = o_.description || moduleInfo.description;
|
||||
o.selector = o_.selector || 'ismod-' + name;
|
||||
o.container = o_.container || ref.options.selector;
|
||||
o.container = o_.container || _sequencer.options.selector;
|
||||
o.image = image;
|
||||
o.inBrowser = ref.options.inBrowser;
|
||||
o.inBrowser = _sequencer.options.inBrowser;
|
||||
|
||||
o.step = {
|
||||
name: o.name,
|
||||
description: o.description,
|
||||
ID: o.number,
|
||||
imageName: o.image,
|
||||
inBrowser: ref.options.inBrowser,
|
||||
ui: ref.options.ui,
|
||||
inBrowser: _sequencer.options.inBrowser,
|
||||
ui: _sequencer.options.ui,
|
||||
options: o
|
||||
};
|
||||
var UI = ref.events;
|
||||
var module = ref.modules[name][0](o,UI);
|
||||
ref.images[image].steps.push(module);
|
||||
var UI = _sequencer.events;
|
||||
var module = _sequencer.modules[name][0](o, UI);
|
||||
_sequencer.images[image].steps.push(module);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
addStep(image, name, o);
|
||||
_sequencer.steps = _sequencer.images[image].steps;
|
||||
}
|
||||
module.exports = AddStep;
|
||||
|
||||
@@ -1,23 +1,25 @@
|
||||
if (typeof window !== 'undefined') {window.$ = window.jQuery = require('jquery'); isBrowser = true}
|
||||
if (typeof window !== 'undefined') {isBrowser = true}
|
||||
else {var isBrowser = false}
|
||||
require('./util/getStep.js')
|
||||
|
||||
ImageSequencer = function ImageSequencer(options) {
|
||||
|
||||
|
||||
var sequencer = (this.name == "ImageSequencer")?this:this.sequencer;
|
||||
options = options || {};
|
||||
options.inBrowser = options.inBrowser || isBrowser;
|
||||
options.sequencerCounter = 0;
|
||||
|
||||
|
||||
function objTypeOf(object){
|
||||
return Object.prototype.toString.call(object).split(" ")[1].slice(0,-1)
|
||||
}
|
||||
|
||||
|
||||
function log(color,msg) {
|
||||
if(options.ui!="none") {
|
||||
if(arguments.length==1) console.log(arguments[0]);
|
||||
else if(arguments.length==2) console.log(color,msg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function copy(a) {
|
||||
if (!typeof(a) == "object") return a;
|
||||
if (objTypeOf(a) == "Array") return a.slice();
|
||||
@@ -30,11 +32,11 @@ ImageSequencer = function ImageSequencer(options) {
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
|
||||
function makeArray(input) {
|
||||
return (objTypeOf(input)=="Array")?input:[input];
|
||||
}
|
||||
|
||||
|
||||
var image,
|
||||
steps = [],
|
||||
modules = require('./Modules'),
|
||||
@@ -43,27 +45,27 @@ ImageSequencer = function ImageSequencer(options) {
|
||||
inputlog = [],
|
||||
events = require('./ui/UserInterface')(),
|
||||
fs = require('fs');
|
||||
|
||||
|
||||
// if in browser, prompt for an image
|
||||
// if (options.imageSelect || options.inBrowser) addStep('image-select');
|
||||
// else if (options.imageUrl) loadImage(imageUrl);
|
||||
|
||||
|
||||
function addSteps(){
|
||||
var this_ = (this.name == "ImageSequencer")?this:this.sequencer;
|
||||
var args = (this.name == "ImageSequencer")?[]:[this.images];
|
||||
var json_q = {};
|
||||
for(var arg in arguments){args.push(copy(arguments[arg]));}
|
||||
json_q = formatInput.call(this_,args,"+");
|
||||
|
||||
|
||||
inputlog.push({method:"addSteps", json_q:copy(json_q)});
|
||||
|
||||
|
||||
for (var i in json_q)
|
||||
for (var j in json_q[i])
|
||||
require("./AddStep")(this_,i,json_q[i][j].name,json_q[i][j].o);
|
||||
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
function removeStep(image,index) {
|
||||
//remove the step from images[image].steps and redraw remaining images
|
||||
if(index>0) {
|
||||
@@ -73,16 +75,16 @@ ImageSequencer = function ImageSequencer(options) {
|
||||
}
|
||||
//tell the UI a step has been removed
|
||||
}
|
||||
|
||||
|
||||
function removeSteps(image,index) {
|
||||
var run = {}, indices;
|
||||
var this_ = (this.name == "ImageSequencer")?this:this.sequencer;
|
||||
var args = (this.name == "ImageSequencer")?[]:[this.images];
|
||||
for(var arg in arguments) args.push(copy(arguments[arg]));
|
||||
|
||||
|
||||
var json_q = formatInput.call(this_,args,"-");
|
||||
inputlog.push({method:"removeSteps", json_q:copy(json_q)});
|
||||
|
||||
|
||||
for (var img in json_q) {
|
||||
indices = json_q[img].sort(function(a,b){return b-a});
|
||||
run[img] = indices[indices.length-1];
|
||||
@@ -92,16 +94,16 @@ ImageSequencer = function ImageSequencer(options) {
|
||||
// this.run(run); // This is creating problems
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
function insertSteps(image, index, name, o) {
|
||||
var run = {};
|
||||
var this_ = (this.name == "ImageSequencer")?this:this.sequencer;
|
||||
var args = (this.name == "ImageSequencer")?[]:[this.images];
|
||||
for (var arg in arguments) args.push(arguments[arg]);
|
||||
|
||||
|
||||
var json_q = formatInput.call(this_,args,"^");
|
||||
inputlog.push({method:"insertSteps", json_q:copy(json_q)});
|
||||
|
||||
|
||||
for (var img in json_q) {
|
||||
var details = json_q[img];
|
||||
details = details.sort(function(a,b){return b.index-a.index});
|
||||
@@ -112,39 +114,49 @@ ImageSequencer = function ImageSequencer(options) {
|
||||
// this.run(run); // This is Creating issues
|
||||
return this;
|
||||
}
|
||||
|
||||
function run(spinnerObj,t_image,t_from) {
|
||||
let progressObj;
|
||||
if(arguments[0] != 'test'){
|
||||
progressObj = spinnerObj
|
||||
delete arguments['0']
|
||||
|
||||
// Config is an object which contains the runtime configuration like progress bar
|
||||
// information and index from which the sequencer should run
|
||||
function run(config,t_image,t_from) {
|
||||
let progressObj,index=0;
|
||||
config = config || {mode: 'no-arg'};
|
||||
if(config.index) index = config.index;
|
||||
|
||||
if(config.mode != 'test'){
|
||||
if(config.mode != "no-arg" && typeof config != 'function'){
|
||||
if(config.progressObj) progressObj = config.progressObj;
|
||||
delete arguments['0'];
|
||||
}
|
||||
}
|
||||
else{
|
||||
arguments['0'] = config.mode;
|
||||
}
|
||||
|
||||
var this_ = (this.name == "ImageSequencer")?this:this.sequencer;
|
||||
var args = (this.name == "ImageSequencer")?[]:[this.images];
|
||||
for (var arg in arguments) args.push(copy(arguments[arg]));
|
||||
|
||||
|
||||
var callback = function() {};
|
||||
for (var arg in args)
|
||||
if(objTypeOf(args[arg]) == "Function")
|
||||
callback = args.splice(arg,1)[0];
|
||||
|
||||
|
||||
var json_q = formatInput.call(this_,args,"r");
|
||||
|
||||
require('./Run')(this_, json_q, callback,progressObj);
|
||||
|
||||
|
||||
require('./Run')(this_, json_q, callback,index,progressObj);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
function loadImages() {
|
||||
var args = [];
|
||||
var sequencer = this;
|
||||
for (var arg in arguments) args.push(copy(arguments[arg]));
|
||||
var json_q = formatInput.call(this,args,"l");
|
||||
|
||||
|
||||
inputlog.push({method:"loadImages", json_q:copy(json_q)});
|
||||
var loadedimages = this.copy(json_q.loadedimages);
|
||||
|
||||
|
||||
var ret = {
|
||||
name: "ImageSequencer Wrapper",
|
||||
sequencer: this,
|
||||
@@ -156,7 +168,7 @@ ImageSequencer = function ImageSequencer(options) {
|
||||
setUI: this.setUI,
|
||||
images: loadedimages
|
||||
};
|
||||
|
||||
|
||||
function load(i) {
|
||||
if(i==loadedimages.length) {
|
||||
json_q.callback.call(ret);
|
||||
@@ -167,24 +179,24 @@ ImageSequencer = function ImageSequencer(options) {
|
||||
load(++i);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
load(0);
|
||||
}
|
||||
|
||||
|
||||
function replaceImage(selector,steps,options) {
|
||||
options = options || {};
|
||||
options.callback = options.callback || function() {};
|
||||
return require('./ReplaceImage')(this,selector,steps,options);
|
||||
}
|
||||
|
||||
|
||||
function setUI(UI) {
|
||||
this.events = require('./ui/UserInterface')(UI);
|
||||
}
|
||||
|
||||
|
||||
var exportBin = function(dir,basic) {
|
||||
return require('./ExportBin')(dir,this,basic);
|
||||
}
|
||||
|
||||
|
||||
function modulesInfo(name) {
|
||||
var modulesdata = {}
|
||||
if(name == "load-image") return {};
|
||||
@@ -195,7 +207,91 @@ ImageSequencer = function ImageSequencer(options) {
|
||||
else modulesdata = modules[name][1];
|
||||
return modulesdata;
|
||||
}
|
||||
|
||||
|
||||
// Strigifies the current sequence
|
||||
function toString(step) {
|
||||
if(step) {
|
||||
return stepToString(step);
|
||||
} else {
|
||||
return copy(this.images.image1.steps).map(stepToString).slice(1).join(',');
|
||||
}
|
||||
}
|
||||
|
||||
// Stringifies one step of the sequence
|
||||
function stepToString(step) {
|
||||
let inputs = copy(modulesInfo(step.options.name).inputs);
|
||||
inputs = inputs || {};
|
||||
|
||||
for(let input in inputs) {
|
||||
inputs[input] = step.options[input] || inputs[input].default;
|
||||
inputs[input] = encodeURIComponent(inputs[input]);
|
||||
}
|
||||
|
||||
var configurations = Object.keys(inputs).map(key => key + ':' + inputs[key]).join('|');
|
||||
return `${step.options.name}(${configurations})`;
|
||||
}
|
||||
|
||||
// exports the current sequence as an array of JSON steps
|
||||
function toJSON(str){
|
||||
return this.stringToJSON(this.toString());
|
||||
}
|
||||
|
||||
// Coverts stringified sequence into an array of JSON steps
|
||||
function stringToJSON(str){
|
||||
let steps = str.split(',');
|
||||
return steps.map(stringToJSONstep);
|
||||
}
|
||||
|
||||
// Converts one stringified step into JSON
|
||||
function stringToJSONstep(str){
|
||||
if(str.indexOf('(') === -1) { // if there are no settings specified
|
||||
var moduleName = str.substr(0);
|
||||
stepSettings = "";
|
||||
} else {
|
||||
var moduleName = str.substr(0, str.indexOf('('));
|
||||
stepSettings = str.slice(str.indexOf('(') + 1, -1);
|
||||
}
|
||||
|
||||
stepSettings = stepSettings.split('|').reduce(function formatSettings(accumulator, current, i){
|
||||
var settingName = current.substr(0, current.indexOf(':')),
|
||||
settingValue = current.substr(current.indexOf(':') + 1);
|
||||
settingValue = settingValue.replace(/^\(/, '').replace(/\)$/, ''); // strip () at start/end
|
||||
settingValue = decodeURIComponent(settingValue);
|
||||
current = [
|
||||
settingName,
|
||||
settingValue
|
||||
];
|
||||
if (!!settingName) accumulator[settingName] = settingValue;
|
||||
return accumulator;
|
||||
}, {});
|
||||
|
||||
return {
|
||||
name : moduleName,
|
||||
options: stepSettings
|
||||
}
|
||||
}
|
||||
|
||||
// imports a string into the sequencer steps
|
||||
function importString(str){
|
||||
let sequencer = this;
|
||||
if(this.name != "ImageSequencer")
|
||||
sequencer = this.sequencer;
|
||||
var stepsFromString = stringToJSON(str);
|
||||
stepsFromString.forEach(function eachStep(stepObj) {
|
||||
sequencer.addSteps(stepObj.name,stepObj.options);
|
||||
});
|
||||
}
|
||||
|
||||
// imports a array of JSON steps into the sequencer steps
|
||||
function importJSON(obj){
|
||||
let sequencer = this;
|
||||
if(this.name != "ImageSequencer")
|
||||
sequencer = this.sequencer;
|
||||
obj.forEach(function eachStep(stepObj) {
|
||||
sequencer.addSteps(stepObj.name,stepObj.options);
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
//literals and objects
|
||||
name: "ImageSequencer",
|
||||
@@ -204,7 +300,7 @@ ImageSequencer = function ImageSequencer(options) {
|
||||
modules: modules,
|
||||
images: images,
|
||||
events: events,
|
||||
|
||||
|
||||
//user functions
|
||||
loadImages: loadImages,
|
||||
loadImage: loadImages,
|
||||
@@ -216,12 +312,21 @@ ImageSequencer = function ImageSequencer(options) {
|
||||
setUI: setUI,
|
||||
exportBin: exportBin,
|
||||
modulesInfo: modulesInfo,
|
||||
|
||||
toString: toString,
|
||||
stepToString: stepToString,
|
||||
toJSON: toJSON,
|
||||
stringToJSON: stringToJSON,
|
||||
stringToJSONstep: stringToJSONstep,
|
||||
importString: importString,
|
||||
importJSON: importJSON,
|
||||
|
||||
//other functions
|
||||
log: log,
|
||||
objTypeOf: objTypeOf,
|
||||
copy: copy
|
||||
copy: copy,
|
||||
|
||||
setInputStep: require('./ui/SetInputStep')(sequencer)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
module.exports = ImageSequencer;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
const getStepUtils = require('./util/getStep.js');
|
||||
|
||||
// insert one or more steps at a given index in the sequencer
|
||||
function InsertStep(ref, image, index, name, o) {
|
||||
|
||||
@@ -9,7 +11,7 @@ function InsertStep(ref, image, index, name, o) {
|
||||
o.container = o_.container || ref.options.selector;
|
||||
o.image = image;
|
||||
|
||||
if(index==-1) index = ref.images[image].steps.length;
|
||||
if (index == -1) index = ref.images[image].steps.length;
|
||||
|
||||
o.step = {
|
||||
name: o.name,
|
||||
@@ -22,8 +24,8 @@ function InsertStep(ref, image, index, name, o) {
|
||||
options: o
|
||||
};
|
||||
var UI = ref.events;
|
||||
var module = ref.modules[name][0](o,UI);
|
||||
ref.images[image].steps.splice(index,0,module);
|
||||
var module = ref.modules[name][0](o, UI);
|
||||
ref.images[image].steps.splice(index, 0, module);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -3,39 +3,48 @@
|
||||
*/
|
||||
module.exports = {
|
||||
'channel': [
|
||||
require('./modules/Channel/Module'),require('./modules/Channel/info')
|
||||
require('./modules/Channel/Module'), require('./modules/Channel/info')
|
||||
],
|
||||
'brightness': [
|
||||
require('./modules/Brightness/Module'),require('./modules/Brightness/info')
|
||||
require('./modules/Brightness/Module'), require('./modules/Brightness/info')
|
||||
],
|
||||
'edge-detect':[
|
||||
require('./modules/EdgeDetect/Module'),require('./modules/EdgeDetect/info')
|
||||
'edge-detect': [
|
||||
require('./modules/EdgeDetect/Module'), require('./modules/EdgeDetect/info')
|
||||
],
|
||||
'ndvi': [
|
||||
require('./modules/Ndvi/Module'),require('./modules/Ndvi/info')
|
||||
require('./modules/Ndvi/Module'), require('./modules/Ndvi/info')
|
||||
],
|
||||
'invert': [
|
||||
require('./modules/Invert/Module'),require('./modules/Invert/info')
|
||||
require('./modules/Invert/Module'), require('./modules/Invert/info')
|
||||
],
|
||||
'crop': [
|
||||
require('./modules/Crop/Module'),require('./modules/Crop/info')
|
||||
require('./modules/Crop/Module'), require('./modules/Crop/info')
|
||||
],
|
||||
'colormap': [
|
||||
require('./modules/Colormap/Module'),require('./modules/Colormap/info')
|
||||
require('./modules/Colormap/Module'), require('./modules/Colormap/info')
|
||||
],
|
||||
'decode-qr': [
|
||||
require('./modules/DecodeQr/Module'),require('./modules/DecodeQr/info')
|
||||
require('./modules/DecodeQr/Module'), require('./modules/DecodeQr/info')
|
||||
],
|
||||
'fisheye-gl': [
|
||||
require('./modules/FisheyeGl/Module'),require('./modules/FisheyeGl/info')
|
||||
require('./modules/FisheyeGl/Module'), require('./modules/FisheyeGl/info')
|
||||
],
|
||||
'dynamic': [
|
||||
require('./modules/Dynamic/Module'),require('./modules/Dynamic/info')
|
||||
require('./modules/Dynamic/Module'), require('./modules/Dynamic/info')
|
||||
],
|
||||
'blur': [
|
||||
require('./modules/Blur/Module'),require('./modules/Blur/info')
|
||||
require('./modules/Blur/Module'), require('./modules/Blur/info')
|
||||
],
|
||||
'saturation': [
|
||||
require('./modules/Saturation/Module'),require('./modules/Saturation/info')
|
||||
require('./modules/Saturation/Module'), require('./modules/Saturation/info')
|
||||
],
|
||||
'average': [
|
||||
require('./modules/Average/Module'), require('./modules/Average/info')
|
||||
],
|
||||
'blend': [
|
||||
require('./modules/Blend/Module'), require('./modules/Blend/info')
|
||||
],
|
||||
'import-image': [
|
||||
require('./modules/ImportImage/Module'), require('./modules/ImportImage/info')
|
||||
]
|
||||
}
|
||||
|
||||
@@ -4,7 +4,8 @@ function ReplaceImage(ref,selector,steps,options) {
|
||||
if(!ref.options.inBrowser) return false; // This isn't for Node.js
|
||||
var tempSequencer = ImageSequencer({ui: false});
|
||||
var this_ = ref;
|
||||
var input = document.querySelectorAll(selector);
|
||||
if (window.hasOwnProperty('$')) var input = $(selector);
|
||||
else var input = document.querySelectorAll(selector);
|
||||
var images = [];
|
||||
for (var i = 0; i < input.length; i++) {
|
||||
if (input[i] instanceof HTMLImageElement) images.push(input[i]);
|
||||
@@ -12,14 +13,25 @@ function ReplaceImage(ref,selector,steps,options) {
|
||||
|
||||
function replaceImage (img, steps) {
|
||||
var url = img.src;
|
||||
var ext = url.split('.').pop();
|
||||
// refactor to filetypeFromUrl()
|
||||
var ext = url.split('?')[0].split('.').pop();
|
||||
|
||||
var xmlHTTP = new XMLHttpRequest();
|
||||
xmlHTTP.open('GET', url, true);
|
||||
xmlHTTP.responseType = 'arraybuffer';
|
||||
xmlHTTP.onload = function(e) {
|
||||
var arr = new Uint8Array(this.response);
|
||||
var raw = String.fromCharCode.apply(null,arr);
|
||||
|
||||
// in chunks to avoid "RangeError: Maximum call stack exceeded"
|
||||
// https://github.com/publiclab/image-sequencer/issues/241
|
||||
// https://stackoverflow.com/a/20048852/1116657
|
||||
var raw = '';
|
||||
var i,j,subArray,chunk = 5000;
|
||||
for (i=0,j=arr.length; i<j; i+=chunk) {
|
||||
subArray = arr.subarray(i,i+chunk);
|
||||
raw += String.fromCharCode.apply(null, subArray);
|
||||
}
|
||||
|
||||
var base64 = btoa(raw);
|
||||
var dataURL="data:image/"+ext+";base64," + base64;
|
||||
make(dataURL);
|
||||
|
||||
60
src/Run.js
60
src/Run.js
@@ -1,56 +1,80 @@
|
||||
function Run(ref, json_q, callback,progressObj) {
|
||||
if(!progressObj) progressObj = {stop: function(){}}
|
||||
const getStepUtils = require('./util/getStep.js');
|
||||
|
||||
function Run(ref, json_q, callback,ind, progressObj) {
|
||||
if (!progressObj) progressObj = { stop: function () { } };
|
||||
|
||||
function drawStep(drawarray, pos) {
|
||||
if (pos == drawarray.length && drawarray[pos - 1] !== undefined) {
|
||||
var image = drawarray[pos-1].image;
|
||||
if(ref.objTypeOf(callback) == 'Function') {
|
||||
var image = drawarray[pos - 1].image;
|
||||
if (ref.objTypeOf(callback) == "Function" && ref.images[image].steps.slice(-1)[0].output) {
|
||||
var steps = ref.images[image].steps;
|
||||
var out = steps[steps.length-1].output.src;
|
||||
var out = steps[steps.length - 1].output.src;
|
||||
callback(out);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// so we don't run on the loadImage module:
|
||||
if (drawarray[pos] !== undefined) {
|
||||
var image = drawarray[pos].image;
|
||||
var i = drawarray[pos].i;
|
||||
var input = ref.images[image].steps[i - 1].output;
|
||||
ref.images[image].steps[i].draw(ref.copy(input), function onEachStep() {
|
||||
drawStep(drawarray, ++pos);
|
||||
},progressObj);
|
||||
|
||||
ref.images[image].steps[i].getStep = function getStep(offset) {
|
||||
if(i + offset >= ref.images[image].steps.length) return {options:{name:undefined}};
|
||||
else return ref.images[image].steps.slice(i + offset)[0];
|
||||
};
|
||||
ref.images[image].steps[i].getIndex = function getIndex(){
|
||||
return i;
|
||||
}
|
||||
|
||||
for (var util in getStepUtils) {
|
||||
if (getStepUtils.hasOwnProperty(util)) {
|
||||
ref.images[image].steps[i][util] = getStepUtils[util];
|
||||
}
|
||||
}
|
||||
|
||||
ref.images[image].steps[i].draw(
|
||||
ref.copy(input),
|
||||
function onEachStep() {
|
||||
drawStep(drawarray, ++pos);
|
||||
},
|
||||
progressObj
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function drawSteps(json_q) {
|
||||
var drawarray = [];
|
||||
for (var image in json_q) {
|
||||
var no_steps = ref.images[image].steps.length;
|
||||
var init = json_q[image];
|
||||
for(var i = 0; i < no_steps - init; i++) {
|
||||
for (var i = 0; i < no_steps - init; i++) {
|
||||
drawarray.push({ image: image, i: init + i });
|
||||
}
|
||||
}
|
||||
drawStep(drawarray,0);
|
||||
drawStep(drawarray, ind);
|
||||
}
|
||||
|
||||
function filter(json_q){
|
||||
|
||||
function filter(json_q) {
|
||||
for (var image in json_q) {
|
||||
if (json_q[image] == 0 && ref.images[image].steps.length == 1)
|
||||
delete json_q[image];
|
||||
delete json_q[image];
|
||||
else if (json_q[image] == 0) json_q[image]++;
|
||||
}
|
||||
for (var image in json_q) {
|
||||
var prevstep = ref.images[image].steps[json_q[image] - 1];
|
||||
while (typeof(prevstep) == "undefined" || typeof(prevstep.output) == "undefined") {
|
||||
prevstep = ref.images[image].steps[(--json_q[image]) - 1];
|
||||
while (
|
||||
typeof prevstep == "undefined" ||
|
||||
typeof prevstep.output == "undefined"
|
||||
) {
|
||||
prevstep = ref.images[image].steps[--json_q[image] - 1];
|
||||
}
|
||||
}
|
||||
return json_q;
|
||||
}
|
||||
|
||||
|
||||
var json_q = filter(json_q);
|
||||
return drawSteps(json_q);
|
||||
|
||||
}
|
||||
module.exports = Run;
|
||||
|
||||
88
src/modules/Average/Module.js
Executable file
88
src/modules/Average/Module.js
Executable file
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Average all pixel colors
|
||||
*/
|
||||
module.exports = function Average(options, UI){
|
||||
options = options || {};
|
||||
options.blur = options.blur || 2
|
||||
|
||||
//Tell the UI that a step has been set up
|
||||
UI.onSetup(options.step);
|
||||
var output;
|
||||
|
||||
options.step.metadata = options.step.metadata || {};
|
||||
|
||||
function draw(input,callback,progressObj){
|
||||
|
||||
progressObj.stop(true);
|
||||
progressObj.overrideFlag = true;
|
||||
|
||||
// Tell the UI that a step is being drawn
|
||||
UI.onDraw(options.step);
|
||||
|
||||
var step = this;
|
||||
|
||||
function changePixel(r, g, b, a){
|
||||
return [r,g,b,a]
|
||||
}
|
||||
|
||||
// do the averaging
|
||||
function extraManipulation(pixels){
|
||||
var sum = [0,0,0,0];
|
||||
for (var i = 0; i < pixels.data.length; i += 4) {
|
||||
sum[0] += pixels.data[i + 0];
|
||||
sum[1] += pixels.data[i + 1];
|
||||
sum[2] += pixels.data[i + 2];
|
||||
sum[3] += pixels.data[i + 3];
|
||||
}
|
||||
|
||||
sum[0] = parseInt(sum[0] / (pixels.data.length / 4));
|
||||
sum[1] = parseInt(sum[1] / (pixels.data.length / 4));
|
||||
sum[2] = parseInt(sum[2] / (pixels.data.length / 4));
|
||||
sum[3] = parseInt(sum[3] / (pixels.data.length / 4));
|
||||
|
||||
for (var i = 0; i < pixels.data.length; i += 4) {
|
||||
pixels.data[i + 0] = sum[0];
|
||||
pixels.data[i + 1] = sum[1];
|
||||
pixels.data[i + 2] = sum[2];
|
||||
pixels.data[i + 3] = sum[3];
|
||||
}
|
||||
// report back and store average in metadata:
|
||||
options.step.metadata.averages = sum;
|
||||
console.log("average: ", sum);
|
||||
// TODO: refactor into a new "display()" method as per https://github.com/publiclab/image-sequencer/issues/242
|
||||
if (options.step.inBrowser && options.step.ui) $(options.step.ui).find('.details').append("<p><b>Averages</b> (r, g, b, a): " + sum.join(', ') + "</p>");
|
||||
return pixels;
|
||||
}
|
||||
|
||||
function output(image, datauri, mimetype){
|
||||
|
||||
// This output is accessible by Image Sequencer
|
||||
step.output = {
|
||||
src: datauri,
|
||||
format: mimetype
|
||||
};
|
||||
|
||||
// This output is accessible by UI
|
||||
options.step.output = datauri;
|
||||
|
||||
// Tell UI that step has been drawn.
|
||||
UI.onComplete(options.step);
|
||||
}
|
||||
|
||||
return require('../_nomodule/PixelManipulation.js')(input, {
|
||||
output: output,
|
||||
changePixel: changePixel,
|
||||
extraManipulation: extraManipulation,
|
||||
format: input.format,
|
||||
image: options.image,
|
||||
callback: callback
|
||||
});
|
||||
|
||||
}
|
||||
return {
|
||||
options: options,
|
||||
draw: draw,
|
||||
output: output,
|
||||
UI: UI
|
||||
}
|
||||
}
|
||||
6
src/modules/Average/info.json
Executable file
6
src/modules/Average/info.json
Executable file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"name": "Average",
|
||||
"description": "Average all pixel color",
|
||||
"inputs": {
|
||||
}
|
||||
}
|
||||
74
src/modules/Blend/Module.js
Normal file
74
src/modules/Blend/Module.js
Normal file
@@ -0,0 +1,74 @@
|
||||
module.exports = function Dynamic(options, UI, util) {
|
||||
|
||||
options = options || {};
|
||||
options.func = options.func || "function(r1, g1, b1, a1, r2, g2, b2, a2) { return [ r1, g2, b2, a2 ] }";
|
||||
|
||||
// Tell the UI that a step has been set up.
|
||||
UI.onSetup(options.step);
|
||||
var output;
|
||||
|
||||
// This function is called on every draw.
|
||||
function draw(input, callback, progressObj) {
|
||||
|
||||
progressObj.stop(true);
|
||||
progressObj.overrideFlag = true;
|
||||
|
||||
// Tell the UI that the step is being drawn
|
||||
UI.onDraw(options.step);
|
||||
var step = this;
|
||||
|
||||
// convert to runnable code:
|
||||
if (typeof options.func === "string") eval('options.func = ' + options.func);
|
||||
|
||||
var getPixels = require('get-pixels');
|
||||
|
||||
// save first image's pixels
|
||||
var priorStep = this.getStep(-2);
|
||||
|
||||
getPixels(priorStep.output.src, function (err, pixels) {
|
||||
options.firstImagePixels = pixels;
|
||||
|
||||
function changePixel(r2, g2, b2, a2, x, y) {
|
||||
// blend!
|
||||
var p = options.firstImagePixels;
|
||||
return options.func(
|
||||
r2, g2, b2, a2,
|
||||
p.get(x, y, 0),
|
||||
p.get(x, y, 1),
|
||||
p.get(x, y, 2),
|
||||
p.get(x, y, 3)
|
||||
)
|
||||
}
|
||||
|
||||
function output(image, datauri, mimetype) {
|
||||
|
||||
// This output is accessible by Image Sequencer
|
||||
step.output = { src: datauri, format: mimetype };
|
||||
|
||||
// This output is accessible by the UI
|
||||
options.step.output = datauri;
|
||||
|
||||
// Tell the UI that the draw is complete
|
||||
UI.onComplete(options.step);
|
||||
|
||||
}
|
||||
|
||||
// run PixelManipulatin on second image's pixels
|
||||
return require('../_nomodule/PixelManipulation.js')(input, {
|
||||
output: output,
|
||||
changePixel: changePixel,
|
||||
format: input.format,
|
||||
image: options.image,
|
||||
inBrowser: options.inBrowser,
|
||||
callback: callback
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
options: options,
|
||||
draw: draw,
|
||||
output: output,
|
||||
UI: UI
|
||||
}
|
||||
}
|
||||
11
src/modules/Blend/info.json
Executable file
11
src/modules/Blend/info.json
Executable file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"name": "Blend",
|
||||
"description": "Blend the past two image steps with the given function. Defaults to using the red channel from image 1 and the green and blue and alpha channels of image 2. Easier to use interfaces coming soon!",
|
||||
"inputs": {
|
||||
"blend": {
|
||||
"type": "input",
|
||||
"desc": "Function to use to blend the two images.",
|
||||
"default": "function(r1, g1, b1, a1, r2, g2, b2, a2) { return [ r1, g2, b2, a2 ] }"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,47 +13,65 @@
|
||||
* y = options.y
|
||||
* y = options.y + options.h
|
||||
*/
|
||||
module.exports = function CropModule(options,UI) {
|
||||
module.exports = function CropModule(options, UI) {
|
||||
|
||||
// TODO: we could also set this to {} if nil in AddModule.js to avoid this line:
|
||||
options = options || {};
|
||||
// TODO: we could also set this to {} if nil in AddModule.js to avoid this line:
|
||||
options = options || {};
|
||||
|
||||
// Tell the UI that a step has been added
|
||||
UI.onSetup(options.step);
|
||||
var output;
|
||||
// Tell the UI that a step has been added
|
||||
UI.onSetup(options.step); // we should get UI to return the image thumbnail so we can attach our own UI extensions
|
||||
|
||||
// This function is caled everytime the step has to be redrawn
|
||||
function draw(input,callback) {
|
||||
// add our custom in-module html ui:
|
||||
if (options.step.inBrowser) var ui = require('./Ui.js')(options.step, UI);
|
||||
var output,
|
||||
setupComplete = false;
|
||||
|
||||
// Tell the UI that the step has been triggered
|
||||
UI.onDraw(options.step);
|
||||
var step = this;
|
||||
// This function is caled everytime the step has to be redrawn
|
||||
function draw(input,callback) {
|
||||
|
||||
require('./Crop')(input,options,function(out,format){
|
||||
// Tell the UI that the step has been triggered
|
||||
UI.onDraw(options.step);
|
||||
var step = this;
|
||||
|
||||
// This output is accessible to Image Sequencer
|
||||
step.output = {
|
||||
src: out,
|
||||
format: format
|
||||
}
|
||||
// save the input image;
|
||||
// TODO: this should be moved to module API to persist the input image
|
||||
options.step.input = input.src;
|
||||
|
||||
// This output is accessible to the UI
|
||||
options.step.output = out;
|
||||
require('./Crop')(input, options, function(out, format){
|
||||
|
||||
// Tell the UI that the step has been drawn
|
||||
UI.onComplete(options.step);
|
||||
// This output is accessible to Image Sequencer
|
||||
step.output = {
|
||||
src: out,
|
||||
format: format
|
||||
}
|
||||
|
||||
// Tell Image Sequencer that step has been drawn
|
||||
callback();
|
||||
// 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();
|
||||
|
||||
return {
|
||||
options: options,
|
||||
draw: draw,
|
||||
output: output,
|
||||
UI: UI
|
||||
}
|
||||
}
|
||||
// start custom UI setup (draggable UI)
|
||||
// only once we have an input image
|
||||
if (setupComplete === false && options.step.inBrowser) {
|
||||
setupComplete = true;
|
||||
ui.setup();
|
||||
}
|
||||
|
||||
// Tell Image Sequencer that step has been drawn
|
||||
callback();
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
return {
|
||||
options: options,
|
||||
draw: draw,
|
||||
output: output,
|
||||
UI: UI
|
||||
}
|
||||
}
|
||||
|
||||
97
src/modules/Crop/Ui.js
Normal file
97
src/modules/Crop/Ui.js
Normal file
@@ -0,0 +1,97 @@
|
||||
// hide on save
|
||||
module.exports = function CropModuleUi(step, ui) {
|
||||
|
||||
let inputWidth = 0,
|
||||
inputHeight = 0;
|
||||
|
||||
// We don't have input image dimensions at the
|
||||
// time of setting up the UI; that comes when draw() is triggered.
|
||||
// So we trigger setup only on first run of draw()
|
||||
// TODO: link this to an event rather than an explicit call in Module.js
|
||||
function setup() {
|
||||
let x = 0,
|
||||
y = 0;
|
||||
|
||||
// display original uncropped input image on initial setup
|
||||
showOriginal()
|
||||
|
||||
inputWidth = Math.floor(imgEl().naturalWidth);
|
||||
inputHeight = Math.floor(imgEl().naturalHeight);
|
||||
|
||||
// display with 50%/50% default crop:
|
||||
setOptions(x, y, inputWidth, inputHeight);
|
||||
|
||||
$(imgEl()).imgAreaSelect({
|
||||
handles: true,
|
||||
x1: x,
|
||||
y1: y,
|
||||
x2: x + inputWidth / 2,
|
||||
y2: y + inputHeight / 2,
|
||||
// when selection is complete
|
||||
onSelectEnd: function onSelectEnd(img, selection) {
|
||||
// assign crop values to module UI form inputs:
|
||||
let converted = convertToNatural(
|
||||
selection.x1,
|
||||
selection.y1,
|
||||
selection.width,
|
||||
selection.height
|
||||
);
|
||||
setOptions(
|
||||
converted[0],
|
||||
converted[1],
|
||||
converted[2],
|
||||
converted[3]
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function convertToNatural(_x, _y, _width, _height) {
|
||||
let displayWidth = $(imgEl()).width(),
|
||||
displayHeight = $(imgEl()).height();
|
||||
// return in same order [ x, y, width, height ]:
|
||||
return [
|
||||
Math.floor(( _x / displayWidth ) * inputWidth),
|
||||
Math.floor(( _y / displayHeight ) * inputHeight),
|
||||
Math.floor(( _width / displayWidth ) * inputWidth),
|
||||
Math.floor(( _height / displayHeight ) * inputHeight)
|
||||
]
|
||||
}
|
||||
|
||||
function remove() {
|
||||
$(imgEl()).imgAreaSelect({
|
||||
remove: true
|
||||
});
|
||||
}
|
||||
|
||||
function hide() {
|
||||
// then hide the draggable UI
|
||||
$(imgEl()).imgAreaSelect({
|
||||
hide: true
|
||||
});
|
||||
}
|
||||
|
||||
// step.imgSelector is not defined, imgElement is:
|
||||
function imgEl() {
|
||||
return step.imgElement;
|
||||
}
|
||||
|
||||
function setOptions(x1, y1, width, height) {
|
||||
let options = $($(imgEl()).parents()[2]).find("input");
|
||||
options[0].value = x1;
|
||||
options[1].value = y1;
|
||||
options[2].value = width;
|
||||
options[3].value = height;
|
||||
}
|
||||
|
||||
// replaces currently displayed output thumbnail with the input image, for ui dragging purposes
|
||||
function showOriginal() {
|
||||
step.imgElement.src = step.input;
|
||||
}
|
||||
|
||||
return {
|
||||
setup: setup,
|
||||
remove: remove,
|
||||
hide: hide
|
||||
}
|
||||
}
|
||||
68
src/modules/ImportImage/Module.js
Normal file
68
src/modules/ImportImage/Module.js
Normal file
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Import Image module; this fetches a given remote or local image via URL
|
||||
* or data-url, and overwrites the current one. It saves the original as
|
||||
* step.metadata.input for use in future modules such as blending.
|
||||
* TODO: we could accept an operation for blending like "screen" or "overlay",
|
||||
* or a function with blend(r1,g1,b1,a1,r2,g2,b2,a2), OR we could simply allow
|
||||
* subsequent modules to do this blending and keep this one simple.
|
||||
*/
|
||||
module.exports = function ImportImageModule(options, UI) {
|
||||
|
||||
options = options || {};
|
||||
options.imageUrl = options.url || "./images/monarch.png";
|
||||
|
||||
var output,
|
||||
imgObj = new Image();
|
||||
|
||||
// Tell the UI that a step has been added
|
||||
UI.onSetup(options.step); // we should get UI to return the image thumbnail so we can attach our own UI extensions
|
||||
|
||||
// add our custom in-module html ui:
|
||||
if (options.step.inBrowser) {
|
||||
var ui = require('./Ui.js')(options.step, UI);
|
||||
ui.setup();
|
||||
}
|
||||
|
||||
// This function is caled everytime the step has to be redrawn
|
||||
function draw(input,callback) {
|
||||
|
||||
// Tell the UI that the step has been triggered
|
||||
UI.onDraw(options.step);
|
||||
var step = this;
|
||||
|
||||
step.metadata = step.metadata || {};
|
||||
// TODO: develop a standard API method for saving each input state,
|
||||
// for reference in future steps (for blending, for example)
|
||||
step.metadata.input = input;
|
||||
|
||||
function onLoad() {
|
||||
|
||||
// This output is accessible to Image Sequencer
|
||||
step.output = {
|
||||
src: imgObj.src,
|
||||
format: options.format
|
||||
}
|
||||
|
||||
// This output is accessible to the UI
|
||||
options.step.output = imgObj.src;
|
||||
|
||||
// Tell the UI that the step has been drawn
|
||||
UI.onComplete(options.step);
|
||||
|
||||
// Tell Image Sequencer that step has been drawn
|
||||
callback();
|
||||
}
|
||||
|
||||
options.format = require('../../util/GetFormat')(options.imageUrl);
|
||||
imgObj.onload = onLoad;
|
||||
imgObj.src = options.imageUrl;
|
||||
|
||||
}
|
||||
|
||||
return {
|
||||
options: options,
|
||||
draw: draw,
|
||||
output: output,
|
||||
UI: UI
|
||||
}
|
||||
}
|
||||
54
src/modules/ImportImage/Ui.js
Normal file
54
src/modules/ImportImage/Ui.js
Normal file
@@ -0,0 +1,54 @@
|
||||
// hide on save
|
||||
module.exports = function ImportImageModuleUi(step, ui) {
|
||||
|
||||
function setup(setImage, onLoad) {
|
||||
|
||||
// generate a unique timestamp based id for the dropzone
|
||||
var dropzoneId = 'dropzone-import-image-' + step.ID;
|
||||
|
||||
// add a file input listener
|
||||
var dropZone ='\
|
||||
<div style="padding: 30px;margin: 10px 20% 30px;border: 4px dashed #ccc;border-radius: 8px;text-align: center;color: #444;" id="' + dropzoneId + '">\
|
||||
<p>\
|
||||
<i>Select or drag in an image to overlay.</i>\
|
||||
</p>\
|
||||
<center>\
|
||||
<input type="file" class="file-input" value="">\
|
||||
</center>\
|
||||
</div>';
|
||||
|
||||
// insert into .details area
|
||||
// TODO: develop API-based consistent way to add UI elements
|
||||
$(step.ui)
|
||||
.find('.details')
|
||||
.prepend(dropZone);
|
||||
|
||||
// setup file input listener
|
||||
sequencer.setInputStep({
|
||||
dropZoneSelector: "#" + dropzoneId,
|
||||
fileInputSelector: "#" + dropzoneId + " .file-input",
|
||||
onLoad: function onLoadFromInput(progress) {
|
||||
var reader = progress.target;
|
||||
step.options.imageUrl = reader.result;
|
||||
step.options.url = reader.result;
|
||||
sequencer.run();
|
||||
setUrlHashParameter("steps", sequencer.toString());
|
||||
}
|
||||
});
|
||||
|
||||
$(step.ui)
|
||||
.find('.btn-save').on('click', function onClickSave() {
|
||||
|
||||
var src = $(step.ui)
|
||||
.find('.det input').val();
|
||||
step.options.imageUrl = src;
|
||||
sequencer.run();
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
return {
|
||||
setup: setup
|
||||
}
|
||||
}
|
||||
12
src/modules/ImportImage/info.json
Normal file
12
src/modules/ImportImage/info.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"name": "Import Image",
|
||||
"description": "Import a new image and replace the original with it. Future versions may enable a blend mode. Specify an image by URL or by file selector.",
|
||||
"url": "https://github.com/publiclab/image-sequencer/tree/master/MODULES.md",
|
||||
"inputs": {
|
||||
"url": {
|
||||
"type": "string",
|
||||
"desc": "URL of image to import",
|
||||
"default": "./images/monarch.png"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Invert the image
|
||||
*/
|
||||
module.exports = function Invert(options,UI) {
|
||||
module.exports = function Invert(options, UI) {
|
||||
|
||||
options = options || {};
|
||||
|
||||
@@ -10,7 +10,7 @@ module.exports = function Invert(options,UI) {
|
||||
var output;
|
||||
|
||||
// The function which is called on every draw.
|
||||
function draw(input,callback,progressObj) {
|
||||
function draw(input, callback, progressObj) {
|
||||
|
||||
progressObj.stop(true);
|
||||
progressObj.overrideFlag = true;
|
||||
@@ -20,13 +20,13 @@ module.exports = function Invert(options,UI) {
|
||||
var step = this;
|
||||
|
||||
function changePixel(r, g, b, a) {
|
||||
return [255-r, 255-g, 255-b, a];
|
||||
return [255 - r, 255 - g, 255 - b, a];
|
||||
}
|
||||
|
||||
function output(image,datauri,mimetype){
|
||||
function output(image, datauri, mimetype) {
|
||||
|
||||
// This output is accessible by Image Sequencer
|
||||
step.output = {src:datauri,format:mimetype};
|
||||
step.output = { src: datauri, format: mimetype };
|
||||
|
||||
// This output is accessible by UI
|
||||
options.step.output = datauri;
|
||||
@@ -48,7 +48,7 @@ module.exports = function Invert(options,UI) {
|
||||
|
||||
return {
|
||||
options: options,
|
||||
draw: draw,
|
||||
draw: draw,
|
||||
output: output,
|
||||
UI: UI
|
||||
}
|
||||
|
||||
@@ -3,82 +3,86 @@
|
||||
* accepting a changePixel() method to remix a pixel's channels
|
||||
*/
|
||||
module.exports = function PixelManipulation(image, options) {
|
||||
|
||||
options = options || {};
|
||||
options.changePixel = options.changePixel || function changePixel(r, g, b, a) {
|
||||
return [r, g, b, a];
|
||||
};
|
||||
options.extraManipulation = options.extraManipulation||function extraManipulation(pixels){
|
||||
return pixels;
|
||||
}
|
||||
|
||||
var getPixels = require('get-pixels'),
|
||||
savePixels = require('save-pixels');
|
||||
|
||||
getPixels(image.src, function(err, pixels) {
|
||||
|
||||
if(err) {
|
||||
console.log('Bad image path', image);
|
||||
options.changePixel =
|
||||
options.changePixel ||
|
||||
function changePixel(r, g, b, a) {
|
||||
return [r, g, b, a];
|
||||
};
|
||||
|
||||
//
|
||||
options.extraManipulation =
|
||||
options.extraManipulation ||
|
||||
function extraManipulation(pixels) {
|
||||
return pixels;
|
||||
};
|
||||
|
||||
var getPixels = require("get-pixels"),
|
||||
savePixels = require("save-pixels");
|
||||
|
||||
getPixels(image.src, function (err, pixels) {
|
||||
if (err) {
|
||||
console.log("Bad image path", image);
|
||||
return;
|
||||
}
|
||||
|
||||
if(options.getNeighbourPixel){
|
||||
options.getNeighbourPixel.fun = function (distX,distY) {
|
||||
return options.getNeighbourPixel(pixels,x,y,distX,distY);
|
||||
if (options.getNeighbourPixel) {
|
||||
options.getNeighbourPixel.fun = function getNeighborPixel(distX, distY) {
|
||||
return options.getNeighbourPixel(pixels, x, y, distX, distY);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
// iterate through pixels;
|
||||
// this could possibly be more efficient; see
|
||||
// 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.inBrowser){
|
||||
try{
|
||||
var pace = require('pace')((pixels.shape[0] * pixels.shape[1]));
|
||||
}
|
||||
catch(e){
|
||||
|
||||
if (!options.inBrowser && !process.env.TEST) {
|
||||
try {
|
||||
var pace = require("pace")(pixels.shape[0] * pixels.shape[1]);
|
||||
} catch (e) {
|
||||
options.inBrowser = true;
|
||||
}
|
||||
}
|
||||
|
||||
for(var x = 0; x < pixels.shape[0]; x++) {
|
||||
for(var y = 0; y < pixels.shape[1]; y++) {
|
||||
|
||||
|
||||
for (var x = 0; x < pixels.shape[0]; x++) {
|
||||
for (var y = 0; y < pixels.shape[1]; y++) {
|
||||
var pixel = options.changePixel(
|
||||
pixels.get(x, y, 0),
|
||||
pixels.get(x, y, 1),
|
||||
pixels.get(x, y, 2),
|
||||
pixels.get(x, y, 3)
|
||||
);
|
||||
|
||||
pixels.get(x, y, 0),
|
||||
pixels.get(x, y, 1),
|
||||
pixels.get(x, y, 2),
|
||||
pixels.get(x, y, 3),
|
||||
x,
|
||||
y
|
||||
);
|
||||
|
||||
pixels.set(x, y, 0, pixel[0]);
|
||||
pixels.set(x, y, 1, pixel[1]);
|
||||
pixels.set(x, y, 2, pixel[2]);
|
||||
pixels.set(x, y, 3, pixel[3]);
|
||||
|
||||
if(!options.inBrowser)
|
||||
pace.op()
|
||||
|
||||
if (!options.inBrowser && !process.env.TEST) pace.op();
|
||||
}
|
||||
}
|
||||
|
||||
if(options.extraManipulation)
|
||||
pixels = options.extraManipulation(pixels)
|
||||
|
||||
|
||||
// perform any extra operations on the entire array:
|
||||
if (options.extraManipulation) pixels = options.extraManipulation(pixels);
|
||||
|
||||
// there may be a more efficient means to encode an image object,
|
||||
// but node modules and their documentation are essentially arcane on this point
|
||||
var chunks = [];
|
||||
var totalLength = 0;
|
||||
var r = savePixels(pixels, options.format, {quality: 100});
|
||||
|
||||
r.on('data', function(chunk){
|
||||
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);
|
||||
|
||||
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);
|
||||
if (options.callback) options.callback();
|
||||
});
|
||||
});
|
||||
|
||||
50
src/ui/SetInputStep.js
Normal file
50
src/ui/SetInputStep.js
Normal file
@@ -0,0 +1,50 @@
|
||||
// TODO: potentially move this into ImportImage module
|
||||
function setInputStepInit() {
|
||||
|
||||
return function setInputStep(options) {
|
||||
|
||||
var dropzone = $(options.dropZoneSelector);
|
||||
var fileInput = $(options.fileInputSelector);
|
||||
|
||||
var onLoad = options.onLoad;
|
||||
|
||||
var reader = new FileReader();
|
||||
|
||||
function handleFile(e) {
|
||||
|
||||
e.preventDefault();
|
||||
e.stopPropagation(); // stops the browser from redirecting.
|
||||
|
||||
if (e.target && e.target.files) var file = e.target.files[0];
|
||||
else var file = e.dataTransfer.files[0];
|
||||
if(!file) return;
|
||||
|
||||
var reader = new FileReader();
|
||||
|
||||
reader.onload = onLoad;
|
||||
|
||||
reader.readAsDataURL(file);
|
||||
}
|
||||
|
||||
fileInput.on('change', handleFile);
|
||||
|
||||
dropzone[0].addEventListener('drop', handleFile, false);
|
||||
|
||||
dropzone.on('dragover', function onDragover(e) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
e.dataTransfer.dropEffect = 'copy'; // Explicitly show this is a copy.
|
||||
}, false);
|
||||
|
||||
dropzone.on('dragenter', function onDragEnter(e) {
|
||||
dropzone.addClass('hover');
|
||||
});
|
||||
|
||||
dropzone.on('dragleave', function onDragLeave(e) {
|
||||
dropzone.removeClass('hover');
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
module.exports = setInputStepInit;
|
||||
@@ -5,14 +5,12 @@
|
||||
module.exports = function UserInterface(events = {}) {
|
||||
|
||||
events.onSetup = events.onSetup || function(step) {
|
||||
if(step.ui == false) {
|
||||
if (step.ui == false) {
|
||||
// No UI
|
||||
}
|
||||
else if(step.inBrowser) {
|
||||
} else if(step.inBrowser) {
|
||||
// Create and append an HTML Element
|
||||
console.log("Added Step \""+step.name+"\" to \""+step.imageName+"\".");
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
// Create a NodeJS Object
|
||||
console.log('\x1b[36m%s\x1b[0m',"Added Step \""+step.name+"\" to \""+step.imageName+"\".");
|
||||
}
|
||||
@@ -21,12 +19,10 @@ module.exports = function UserInterface(events = {}) {
|
||||
events.onDraw = events.onDraw || function(step) {
|
||||
if (step.ui == false) {
|
||||
// No UI
|
||||
}
|
||||
else if(step.inBrowser) {
|
||||
} else if(step.inBrowser) {
|
||||
// Overlay a loading spinner
|
||||
console.log("Drawing Step \""+step.name+"\" on \""+step.imageName+"\".");
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
// Don't do anything
|
||||
console.log('\x1b[33m%s\x1b[0m',"Drawing Step \""+step.name+"\" on \""+step.imageName+"\".");
|
||||
}
|
||||
@@ -35,27 +31,23 @@ module.exports = function UserInterface(events = {}) {
|
||||
events.onComplete = events.onComplete || function(step) {
|
||||
if (step.ui == false) {
|
||||
// No UI
|
||||
}
|
||||
else if(step.inBrowser) {
|
||||
} else if(step.inBrowser) {
|
||||
// Update the DIV Element
|
||||
// Hide the laoding spinner
|
||||
console.log("Drawn Step \""+step.name+"\" on \""+step.imageName+"\".");
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
// Update the NodeJS Object
|
||||
console.log('\x1b[32m%s\x1b[0m',"Drawn Step \""+step.name+"\" on \""+step.imageName+"\".");
|
||||
}
|
||||
}
|
||||
|
||||
events.onRemove = events.onRemove || function(step) {
|
||||
if(step.ui == false){
|
||||
if (step.ui == false){
|
||||
// No UI
|
||||
}
|
||||
else if(step.inBrowser) {
|
||||
} else if(step.inBrowser) {
|
||||
// Remove the DIV Element
|
||||
console.log("Removing Step \""+step.name+"\" of \""+step.imageName+"\".");
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
// Delete the NodeJS Object
|
||||
console.log('\x1b[31m%s\x1b[0m',"Removing Step \""+step.name+"\" of \""+step.imageName+"\".");
|
||||
}
|
||||
|
||||
29
src/util/GetFormat.js
Normal file
29
src/util/GetFormat.js
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Determine format from a URL or data-url, return "jpg" "png" "gif" etc
|
||||
* TODO: write a test for this using the examples
|
||||
*/
|
||||
module.exports = function GetFormat(src) {
|
||||
|
||||
var format = undefined; // haha default
|
||||
|
||||
// EXAMPLE: "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAYEBQYFBAYGBQYHBwYIChAKCgkJChQODwwQFxQYGBcUFhYaHSUfGhsjHBYWICwgIyYnKSopGR8tMC0oMCUoKSj/2wBDAQcHBwoIChMKChMoGhYaKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCj/wAARCAAQABADASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAf/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFQEBAQAAAAAAAAAAAAAAAAAABgj/xAAUEQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIRAxEAPwCdABykX//Z";
|
||||
// EXAMPLE: "http://example.com/example.png"
|
||||
// EXAMPLE: "/example.png"
|
||||
|
||||
if (isDataUrl(src)) {
|
||||
format = src.split(';')[0].split('/').pop();
|
||||
} else {
|
||||
format = src.split('.').pop();
|
||||
}
|
||||
|
||||
function isDataUrl(src) {
|
||||
return src.substr(0, 10) === "data:image"
|
||||
}
|
||||
|
||||
format = format.toLowerCase();
|
||||
|
||||
if (format === "jpeg") format = "jpg";
|
||||
|
||||
return format;
|
||||
|
||||
}
|
||||
18
src/util/getStep.js
Normal file
18
src/util/getStep.js
Normal file
@@ -0,0 +1,18 @@
|
||||
module.exports = {
|
||||
getPreviousStep : function () {
|
||||
return this.getStep(-1);
|
||||
},
|
||||
|
||||
getNextStep : function() {
|
||||
return this.getStep(1);
|
||||
},
|
||||
|
||||
getInput : function(offset){
|
||||
if(offset + this.getIndex() === 0) offset++;
|
||||
return this.getStep(offset - 1).output;
|
||||
},
|
||||
|
||||
getOuput : function(offset){
|
||||
return this.getStep(offset).output;
|
||||
}
|
||||
}
|
||||
@@ -6,8 +6,8 @@ const test = require('tape');
|
||||
test('Output directory is correctly generated',function(t){
|
||||
cliUtils.makedir('./output/',function(){
|
||||
require('fs').access('./output/.',function(err){
|
||||
t.true(!err,"Access the created dir")
|
||||
t.end()
|
||||
t.true(!err,"Access the created dir");
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -15,13 +15,12 @@ var sequencer = ImageSequencer({ ui: false });
|
||||
var qr = require('./images/IS-QR.js');
|
||||
var test_png = require('./images/test.png.js');
|
||||
var test_gif = require('./images/test.gif.js');
|
||||
var spinner = require('ora')('').start()
|
||||
|
||||
sequencer.loadImages(test_png);
|
||||
sequencer.addSteps(['invert','invert']);
|
||||
|
||||
test("Preload", function(t) {
|
||||
sequencer.run(spinner,function(){
|
||||
sequencer.run({mode:'test'},function(){
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
@@ -52,7 +51,7 @@ test("Twice inverted image is identical to original image", function (t) {
|
||||
|
||||
test("Decode QR module works properly :: setup", function (t) {
|
||||
sequencer.loadImage(qr,function(){
|
||||
this.addSteps('decode-qr').run(spinner.start(),function(){
|
||||
this.addSteps('decode-qr').run({mode:'test'},function(){
|
||||
t.end();
|
||||
});
|
||||
})
|
||||
@@ -65,7 +64,7 @@ test("Decode QR module works properly :: teardown", function (t) {
|
||||
|
||||
test("PixelManipulation works for PNG images", function (t) {
|
||||
sequencer.loadImages(test_png,function(){
|
||||
this.addSteps('invert').run(spinner.start(),function(out){
|
||||
this.addSteps('invert').run({mode:'test'},function(out){
|
||||
t.equal(1,1)
|
||||
t.end();
|
||||
});
|
||||
@@ -74,10 +73,9 @@ test("PixelManipulation works for PNG images", function (t) {
|
||||
|
||||
test("PixelManipulation works for GIF images", function (t) {
|
||||
sequencer.loadImages(test_gif,function(){
|
||||
this.addSteps('invert').run(spinner,function(out){
|
||||
this.addSteps('invert').run({mode:'test'},function(out){
|
||||
t.equal(1,1)
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
});
|
||||
spinner.stop(true)
|
||||
});
|
||||
@@ -9,21 +9,21 @@ var test = require('tape');
|
||||
require('../../src/ImageSequencer.js');
|
||||
|
||||
// This function is used to test whether or not any additional global variables are being created
|
||||
function copy(g,a) {
|
||||
function copy(g, a) {
|
||||
var b = {};
|
||||
var i = 0;
|
||||
for (var v in a)
|
||||
if(g) {
|
||||
if(v != "sequencer") b[v] = a[v];
|
||||
}
|
||||
else {
|
||||
if(v != "sequencer" && v!="global1" && v!="global2" && !global1.hasOwnProperty(v)) i++;
|
||||
}
|
||||
if(g) return b;
|
||||
if (g) {
|
||||
if (v != "sequencer") b[v] = a[v];
|
||||
}
|
||||
else {
|
||||
if (v != "sequencer" && v != "global1" && v != "global2" && !global1.hasOwnProperty(v)) i++;
|
||||
}
|
||||
if (g) return b;
|
||||
else return i;
|
||||
}
|
||||
var parent = (typeof(global)==="undefined")?window:global;
|
||||
var global1 = copy(true,parent);
|
||||
var parent = (typeof (global) === "undefined") ? window : global;
|
||||
var global1 = copy(true, parent);
|
||||
|
||||
var sequencer = ImageSequencer({ ui: false });
|
||||
var red = "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAYEBQYFBAYGBQYHBwYIChAKCgkJChQODwwQFxQYGBcUFhYaHSUfGhsjHBYWICwgIyYnKSopGR8tMC0oMCUoKSj/2wBDAQcHBwoIChMKChMoGhYaKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCj/wAARCAAQABADASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAf/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFQEBAQAAAAAAAAAAAAAAAAAABgj/xAAUEQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIRAxEAPwCdABykX//Z";
|
||||
@@ -33,14 +33,14 @@ test('Image Sequencer has tests', function (t) {
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('loadImages loads a DataURL image and creates a step.', function (t){
|
||||
sequencer.loadImages('test',red);
|
||||
test('loadImages loads a DataURL image and creates a step.', function (t) {
|
||||
sequencer.loadImages('test', red);
|
||||
t.equal(sequencer.images.test.steps.length, 1, "Initial Step Created");
|
||||
t.equal(typeof(sequencer.images.test.steps[0].output.src), "string", "Initial output exists");
|
||||
t.equal(typeof (sequencer.images.test.steps[0].output.src), "string", "Initial output exists");
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('modulesInfo() returns info for each module', function (t){
|
||||
test('modulesInfo() returns info for each module', function (t) {
|
||||
var info = sequencer.modulesInfo();
|
||||
t.equal(Object.keys(info).length, Object.keys(sequencer.modules).length);
|
||||
t.equal(info.hasOwnProperty(Object.keys(sequencer.modules)[0]), true);
|
||||
@@ -49,40 +49,40 @@ test('modulesInfo() returns info for each module', function (t){
|
||||
t.end();
|
||||
});
|
||||
|
||||
if(!sequencer.options.inBrowser)
|
||||
test('loadImage loads an image from URL and creates a step. (NodeJS)', function (t){
|
||||
require('dns').resolve('www.github.com', function(err) {
|
||||
if (err) {
|
||||
console.log("Test aborted due to no internet");
|
||||
t.end();
|
||||
}
|
||||
else {
|
||||
sequencer.loadImage('URL','https://ccpandhare.github.io/image-sequencer/examples/images/red.jpg', function(){
|
||||
t.equal(sequencer.images.URL.steps.length, 1, "Initial Step Created");
|
||||
t.equal(typeof(sequencer.images.URL.steps[0].output.src), "string", "Initial output exists");
|
||||
t.end();
|
||||
});
|
||||
}
|
||||
if (!sequencer.options.inBrowser)
|
||||
test('loadImage loads an image from URL and creates a step. (NodeJS)', function (t) {
|
||||
require('dns').resolve('www.github.com', function (err) {
|
||||
if (err) {
|
||||
console.log("Test aborted due to no internet");
|
||||
t.end();
|
||||
}
|
||||
else {
|
||||
sequencer.loadImage('URL', 'https://ccpandhare.github.io/image-sequencer/examples/images/red.jpg', function () {
|
||||
t.equal(sequencer.images.URL.steps.length, 1, "Initial Step Created");
|
||||
t.equal(typeof (sequencer.images.URL.steps[0].output.src), "string", "Initial output exists");
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
if(!sequencer.options.inBrowser)
|
||||
test('loadImages loads an image from PATH and creates a step. (NodeJS)', function (t){
|
||||
sequencer.loadImages('examples/images/red.jpg');
|
||||
t.equal(sequencer.images.image1.steps.length, 1, "Initial Step Created");
|
||||
t.equal(typeof(sequencer.images.image1.steps[0].output.src), "string", "Initial output exists");
|
||||
t.end();
|
||||
});
|
||||
if (!sequencer.options.inBrowser)
|
||||
test('loadImages loads an image from PATH and creates a step. (NodeJS)', function (t) {
|
||||
sequencer.loadImages('examples/images/red.jpg');
|
||||
t.equal(sequencer.images.image1.steps.length, 1, "Initial Step Created");
|
||||
t.equal(typeof (sequencer.images.image1.steps[0].output.src), "string", "Initial output exists");
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('loadImage works too.', function (t){
|
||||
sequencer.loadImage('test2',red);
|
||||
test('loadImage works too.', function (t) {
|
||||
sequencer.loadImage('test2', red);
|
||||
t.equal(sequencer.images.test2.steps.length, 1, "Initial Step Created");
|
||||
t.equal(typeof(sequencer.images.test2.steps[0].output.src), "string", "Initial output exists");
|
||||
t.equal(typeof (sequencer.images.test2.steps[0].output.src), "string", "Initial output exists");
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('addSteps("image","name") adds a step', function (t) {
|
||||
sequencer.addSteps('test','channel');
|
||||
sequencer.addSteps('test', 'channel');
|
||||
t.equal(sequencer.images.test.steps.length, 2, "Length of steps increased")
|
||||
t.equal(sequencer.images.test.steps[1].options.name, "channel", "Correct Step Added");
|
||||
t.equal(sequencer.images.test.steps[1].options.description, "Displays only one color channel of an image -- default is green", "Step description shown");
|
||||
@@ -97,7 +97,7 @@ test('addSteps("name") adds a step', function (t) {
|
||||
});
|
||||
|
||||
test('addSteps(["name"]) adds a step', function (t) {
|
||||
sequencer.addSteps(['channel','invert']);
|
||||
sequencer.addSteps(['channel', 'invert']);
|
||||
t.equal(sequencer.images.test.steps.length, 5, "Length of steps increased by two")
|
||||
t.equal(sequencer.images.test.steps[3].options.name, "channel", "Correct Step Added");
|
||||
t.equal(sequencer.images.test.steps[4].options.name, "invert", "Correct Step Added");
|
||||
@@ -105,81 +105,92 @@ test('addSteps(["name"]) adds a step', function (t) {
|
||||
});
|
||||
|
||||
test('addSteps("name",o) adds a step', function (t) {
|
||||
sequencer.addSteps('channel',{});
|
||||
sequencer.addSteps('channel', {});
|
||||
t.equal(sequencer.images.test.steps.length, 6, "Length of steps increased");
|
||||
t.equal(sequencer.images.test.steps[5].options.name, "channel", "Correct Step Added");
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('addSteps("image","name",o) adds a step', function (t) {
|
||||
sequencer.addSteps('test','channel',{});
|
||||
sequencer.addSteps('test', 'channel', {});
|
||||
t.equal(sequencer.images.test.steps.length, 7, "Length of steps increased");
|
||||
t.equal(sequencer.images.test.steps[6].options.name, "channel", "Correct Step Added");
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('removeSteps("image",position) removes a step', function (t) {
|
||||
sequencer.removeSteps('test',1);
|
||||
sequencer.removeSteps('test', 1);
|
||||
t.equal(sequencer.images.test.steps.length, 6, "Length of steps reduced");
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('removeSteps({image: [positions]}) removes steps', function (t) {
|
||||
sequencer.removeSteps('test',[1,2]);
|
||||
sequencer.removeSteps('test', [1, 2]);
|
||||
t.equal(sequencer.images.test.steps.length, 4, "Length of steps reduced");
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('removeSteps(position) removes steps', function (t) {
|
||||
sequencer.removeSteps([1,2]);
|
||||
sequencer.removeSteps([1, 2]);
|
||||
t.equal(sequencer.images.test.steps.length, 2, "Length of steps reduced");
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('insertSteps("image",position,"module",options) inserts a step', function (t) {
|
||||
sequencer.insertSteps('test',1,'channel',{});
|
||||
sequencer.insertSteps('test', 1, 'channel', {});
|
||||
t.equal(sequencer.images.test.steps.length, 3, "Length of Steps increased");
|
||||
t.equal(sequencer.images.test.steps[1].options.name, "channel", "Correct Step Inserted");
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('insertSteps("image",position,"module") inserts a step', function (t) {
|
||||
sequencer.insertSteps('test',1,'channel');
|
||||
sequencer.insertSteps('test', 1, 'channel');
|
||||
t.equal(sequencer.images.test.steps.length, 4, "Length of Steps increased");
|
||||
t.equal(sequencer.images.test.steps[1].options.name, "channel", "Correct Step Inserted");
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('insertSteps(position,"module") inserts a step', function (t) {
|
||||
sequencer.insertSteps(1,'channel');
|
||||
sequencer.insertSteps(1, 'channel');
|
||||
t.equal(sequencer.images.test.steps.length, 5, "Length of Steps increased");
|
||||
t.equal(sequencer.images.test.steps[1].options.name, "channel", "Correct Step Inserted");
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('insertSteps({image: {index: index, name: "module", o: options} }) inserts a step', function (t) {
|
||||
sequencer.insertSteps({test: {index:1, name:'channel', o:{} } });
|
||||
sequencer.insertSteps({ test: { index: 1, name: 'channel', o: {} } });
|
||||
t.equal(sequencer.images.test.steps.length, 6, "Length of Steps increased");
|
||||
t.equal(sequencer.images.test.steps[1].options.name, "channel", "Correct Step Inserted");
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('run() runs the sequencer and returns output to callback', function (t) {
|
||||
sequencer.run('test',function(out){
|
||||
t.equal(typeof(sequencer.images.test.steps[sequencer.images.test.steps.length - 1].output), "object", "Output is Generated");
|
||||
t.equal(out,sequencer.images.test.steps[sequencer.images.test.steps.length - 1].output.src, "Output callback works");
|
||||
sequencer.run({mode:'test'}, function (out) {
|
||||
t.equal(typeof (sequencer.images.test.steps[sequencer.images.test.steps.length - 1].output), "object", "Output is Generated");
|
||||
t.equal(out, sequencer.images.test.steps[sequencer.images.test.steps.length - 1].output.src, "Output callback works");
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
test('getStep(offset) returns the step at offset distance relative to current step',function(t){
|
||||
sequencer.addSteps('test','invert',{});
|
||||
sequencer.addSteps('test','blend',{});
|
||||
sequencer.run({mode:'test'},function(out){
|
||||
t.equal(!!out,true,"Blend generates output");
|
||||
t.end();
|
||||
});
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('replaceImage returns false in NodeJS', function (t) {
|
||||
var returnvalue = (sequencer.options.inBrowser)?false:sequencer.replaceImage("#selector","test");
|
||||
t.equal(returnvalue,false,"It does.");
|
||||
var returnvalue = (sequencer.options.inBrowser) ? false : sequencer.replaceImage("#selector", "test");
|
||||
t.equal(returnvalue, false, "It does.");
|
||||
t.end();
|
||||
});
|
||||
|
||||
var global2 = copy(false,parent);
|
||||
var global2 = copy(false, parent);
|
||||
test('No Global Variables Created', function (t) {
|
||||
t.equal(0,global2,"None Created.");
|
||||
t.equal(0, global2, "None Created.");
|
||||
t.end();
|
||||
});
|
||||
|
||||
58
test/modules/import-export.js
Normal file
58
test/modules/import-export.js
Normal file
@@ -0,0 +1,58 @@
|
||||
var test = require('tape');
|
||||
require('../../src/ImageSequencer.js');
|
||||
|
||||
var sequencer = ImageSequencer({ ui: false });
|
||||
var red = "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAYEBQYFBAYGBQYHBwYIChAKCgkJChQODwwQFxQYGBcUFhYaHSUfGhsjHBYWICwgIyYnKSopGR8tMC0oMCUoKSj/2wBDAQcHBwoIChMKChMoGhYaKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCj/wAARCAAQABADASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAf/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFQEBAQAAAAAAAAAAAAAAAAAABgj/xAAUEQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIRAxEAPwCdABykX//Z";
|
||||
|
||||
test('toString() and stepToString() return the step/steps in string format', function(t){
|
||||
sequencer.loadImages('image1', red);
|
||||
sequencer.addSteps('channel');
|
||||
sequencer.addSteps('invert');
|
||||
t.equal(sequencer.toString(),"channel(channel:green),invert()","toString works");
|
||||
t.equal(sequencer.stepToString(sequencer.steps[1]),"channel(channel:green)","stepToString works");
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('stringToJSON() and stringToJSONstep() return the step/steps from a string', function(t){
|
||||
t.deepEqual(sequencer.stringToJSON('channel(channel:green),invert(),crop(x:0|y:0|w:50%25|h:50%25),blend(blend:function(r1%2C%20g1%2C%20b1%2C%20a1%2C%20r2%2C%20g2%2C%20b2%2C%20a2)%20%7B%20return%20%5B%20r1%2C%20g2%2C%20b2%2C%20a2%20%5D%20%7D)'),[
|
||||
{ name: 'channel', options: { channel: 'green' } },
|
||||
{ name: 'invert', options: {} },
|
||||
{ name: 'crop', options: { x: '0', y: '0', w: '50%', h: '50%'} },
|
||||
{ name: 'blend', options: { blend: 'function(r1, g1, b1, a1, r2, g2, b2, a2) { return [ r1, g2, b2, a2 ] }' } }
|
||||
]);
|
||||
t.deepEqual(sequencer.stringToJSONstep("channel(channel:green)"),{ name: 'channel', options: { channel: 'green' } });
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('stringToJSON() and stringToJSONstep() return the step/steps from a string without configs, using defaults', function(t){
|
||||
t.deepEqual(sequencer.stringToJSON('channel,blur,invert,blend'),[
|
||||
{ name: 'channel', options: { } },
|
||||
{ name: 'blur', options: { } },
|
||||
{ name: 'invert', options: { } },
|
||||
{ name: 'blend', options: { } }
|
||||
]);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('toJSON() returns the right sequence of steps',function(t){
|
||||
t.deepEqual(sequencer.toJSON(), [
|
||||
{ name: 'channel', options: { channel: 'green' } },
|
||||
{ name: 'invert', options: {} }
|
||||
]);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('importString() imports a string of steps into the sequencer', function(t){
|
||||
sequencer.importString('brightness(brightness:50),invert');
|
||||
t.equal(sequencer.toString(),"channel(channel:green),invert(),brightness(brightness:50),invert()");
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('importJSON() imports a JSON array of steps into the sequencer', function(t){
|
||||
sequencer.importJSON([
|
||||
{ name: 'blur',options: {}}
|
||||
]);
|
||||
t.equal(sequencer.toString(),"channel(channel:green),invert(),brightness(brightness:50),invert(),blur(blur:2)")
|
||||
t.end();
|
||||
});
|
||||
|
||||
29
test/modules/run.js
Normal file
29
test/modules/run.js
Normal file
@@ -0,0 +1,29 @@
|
||||
'use strict';
|
||||
|
||||
var fs = require('fs');
|
||||
var test = require('tape');
|
||||
var DataURItoBuffer = require('data-uri-to-buffer');
|
||||
|
||||
require('../../src/ImageSequencer.js');
|
||||
|
||||
var sequencer = ImageSequencer({ ui: false });
|
||||
var red = "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAYEBQYFBAYGBQYHBwYIChAKCgkJChQODwwQFxQYGBcUFhYaHSUfGhsjHBYWICwgIyYnKSopGR8tMC0oMCUoKSj/2wBDAQcHBwoIChMKChMoGhYaKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCj/wAARCAAQABADASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAf/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFQEBAQAAAAAAAAAAAAAAAAAABgj/xAAUEQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIRAxEAPwCdABykX//Z";
|
||||
|
||||
sequencer.loadImages('image1', red);
|
||||
sequencer.addSteps('invert');
|
||||
sequencer.addSteps('invert');
|
||||
sequencer.addSteps('invert');
|
||||
|
||||
test('run() works with all possible argument combinations',function(t){
|
||||
sequencer.run(function (out) {
|
||||
var output1 = DataURItoBuffer(sequencer.images.image1.steps.slice(-1)[0].output.src);
|
||||
sequencer.images.image1.steps.splice(1,1);
|
||||
sequencer.run({index: 1},function(out){
|
||||
var output2 = DataURItoBuffer(sequencer.images.image1.steps.slice(-1)[0].output.src);
|
||||
t.deepEqual(output1,output2,"output remains same after removing a step and running sequencer from a greater index");
|
||||
sequencer.run(function(out){
|
||||
t.end();
|
||||
})
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user