mirror of
https://github.com/publiclab/image-sequencer.git
synced 2025-12-15 21:00:02 +01:00
initial stab at architecture
This commit is contained in:
59
Gruntfile.js
Normal file
59
Gruntfile.js
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
module.exports = function(grunt) {
|
||||||
|
|
||||||
|
grunt.loadNpmTasks('grunt-browserify');
|
||||||
|
|
||||||
|
require('matchdep').filterDev('grunt-*').forEach(grunt.loadNpmTasks);
|
||||||
|
|
||||||
|
grunt.initConfig({
|
||||||
|
pkg: grunt.file.readJSON('package.json'),
|
||||||
|
|
||||||
|
watch: {
|
||||||
|
options : {
|
||||||
|
livereload: true
|
||||||
|
},
|
||||||
|
source: {
|
||||||
|
files: [
|
||||||
|
'src/*.js',
|
||||||
|
'src/*/*.js',
|
||||||
|
'Gruntfile.js'
|
||||||
|
],
|
||||||
|
tasks: [ 'build:js' ]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
browserify: {
|
||||||
|
dist: {
|
||||||
|
src: [
|
||||||
|
'src/ImageBoard.js'
|
||||||
|
],
|
||||||
|
dest: 'dist/imageboard.js'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/*
|
||||||
|
jasmine: {
|
||||||
|
imageboard: {
|
||||||
|
src: 'dist/*.js',
|
||||||
|
options: {
|
||||||
|
specs: 'spec/javascripts/*spec.js',
|
||||||
|
vendor: [
|
||||||
|
'node_modules/jquery/dist/jquery.min.js',
|
||||||
|
'node_modules/jasmine-jquery/lib/jasmine-jquery.js'
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
/* Default (development): Watch files and build on change. */
|
||||||
|
grunt.registerTask('default', ['watch']);
|
||||||
|
|
||||||
|
grunt.registerTask('build', [
|
||||||
|
'browserify:dist'
|
||||||
|
]);
|
||||||
|
|
||||||
|
// grunt.loadNpmTasks('grunt-contrib-jasmine');
|
||||||
|
|
||||||
|
};
|
||||||
24
README.md
Normal file
24
README.md
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
ImageFlow
|
||||||
|
ImageBoard
|
||||||
|
|
||||||
|
========
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
* [ ] figure out UI/functional separation -- ui is in module wrapper?
|
||||||
|
* [ ] is there a module for generating forms from parameters?
|
||||||
|
* [ ] commandline runnability?
|
||||||
|
* [ ] tests - modules headless; unit tests
|
||||||
|
* [ ] comparisons with diff
|
||||||
|
* [ ] standardize panel addition with submodule that offers Panel.display(image)
|
||||||
|
|
||||||
|
* [ ] make an Infragram module that accepts a math expression
|
||||||
|
* [ ] click to expand for all images
|
||||||
|
* [ ] "add a new step" menu
|
||||||
|
|
||||||
|
* [ ] allow passing data as data-uri or Image object, if both of neighboring pair has ability?
|
||||||
|
* [ ] ...could we directly include package.json for module descriptions? At least as a fallback.
|
||||||
|
|
||||||
|
* [ ] BUG: this doesn't work for defaults: imageboard.loadImage('examples/grid.png', function() {
|
||||||
|
* we should make defaults a config of the first module
|
||||||
|
|
||||||
26
dist/imageboard.css
vendored
26
dist/imageboard.css
vendored
@@ -8,17 +8,33 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.panel {
|
.panel {
|
||||||
padding: 10px;
|
padding: 20px 0;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
border-bottom: 1px dashed #ccc;
|
||||||
}
|
}
|
||||||
|
|
||||||
#drop {
|
.instructions {
|
||||||
|
color: #aaa;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ImageSelect styles */
|
||||||
|
|
||||||
|
|
||||||
|
.mod-image-select #drop {
|
||||||
background: #efefef;
|
background: #efefef;
|
||||||
padding: 10px;
|
color: #aaa;
|
||||||
|
padding: 20px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 50px;
|
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
|
border: 4px dashed #ccc;
|
||||||
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
#drop.hover {
|
.mod-image-select #drop.hover {
|
||||||
background: #ddd;
|
background: #ddd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mod-image-select #drop img {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 800px;
|
||||||
|
}
|
||||||
|
|||||||
24611
dist/imageboard.js
vendored
24611
dist/imageboard.js
vendored
File diff suppressed because it is too large
Load Diff
BIN
examples/grid.png
Normal file
BIN
examples/grid.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 26 KiB |
34
index.html
34
index.html
@@ -12,7 +12,6 @@
|
|||||||
|
|
||||||
<script src="node_modules/jquery/dist/jquery.min.js"></script>
|
<script src="node_modules/jquery/dist/jquery.min.js"></script>
|
||||||
<script src="node_modules/bootstrap/dist/js/bootstrap.min.js"></script>
|
<script src="node_modules/bootstrap/dist/js/bootstrap.min.js"></script>
|
||||||
<script src="lib/imageselect.js"></script>
|
|
||||||
<script src="dist/imageboard.js"></script>
|
<script src="dist/imageboard.js"></script>
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
@@ -27,17 +26,36 @@
|
|||||||
|
|
||||||
<p style="display:none;" class="spinner"><i class="fa fa-spinner fa-spin"></i></p>
|
<p style="display:none;" class="spinner"><i class="fa fa-spinner fa-spin"></i></p>
|
||||||
|
|
||||||
<div id="drop"></div>
|
<div class="panels">
|
||||||
<span id="file-select" class="upload"><input type="file" /></span>
|
|
||||||
<p class="instructions">Select or drop an image here to begin.</p>
|
<div class="panel mod-image-select">
|
||||||
|
<div id="drop">Drag image here</div>
|
||||||
|
<p class="instructions">Select or drop an image here to begin.</p>
|
||||||
|
<span class="file-select" class="upload"><input type="file" /></span>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div style="display:none;" class="controls">
|
|
||||||
<a class="btn btn-lg btn-inverse upload"><i class="fa fa-upload"></i></a>
|
|
||||||
<a style="display:none;" title="vectorcam.svg" class="btn btn-lg btn-inverse save" target="_blank"><i class="fa fa-save"></i></a>
|
|
||||||
<a style="display:none;" class="btn btn-lg btn-inverse btn-options"><i class="fa fa-cog"></i></a>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
|
var pix;
|
||||||
|
var imageboard;
|
||||||
|
|
||||||
|
jQuery(document).ready(function($) {
|
||||||
|
|
||||||
|
imageboard = ImageBoard();
|
||||||
|
|
||||||
|
imageboard.loadImage('examples/grid.png', function() {
|
||||||
|
|
||||||
|
$('body').append(imageboard.run());
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
imageboard.addStep('passthrough');
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
17
package.json
17
package.json
@@ -22,6 +22,21 @@
|
|||||||
"bootstrap": "~3.2.0",
|
"bootstrap": "~3.2.0",
|
||||||
"jquery": "~2"
|
"jquery": "~2"
|
||||||
},
|
},
|
||||||
"devDependencies": {},
|
"devDependencies": {
|
||||||
|
"get-pixels": "^3.3.0",
|
||||||
|
"save-pixels": "^2.3.4",
|
||||||
|
"base64-stream": "^0.1.3",
|
||||||
|
"buffer": "^5.0.2",
|
||||||
|
|
||||||
|
"image-filter-threshold": "^0.0.8",
|
||||||
|
"babelify": "^7.2.0",
|
||||||
|
|
||||||
|
"browserify": "13.0.0",
|
||||||
|
"grunt": "^0.4.5",
|
||||||
|
"grunt-browserify": "^5.0.0",
|
||||||
|
"grunt-contrib-concat": "^0.5.0",
|
||||||
|
"grunt-contrib-watch": "^0.6.1",
|
||||||
|
"matchdep": "^0.3.0"
|
||||||
|
},
|
||||||
"homepage": "https://github.com/jywarren/imageboard"
|
"homepage": "https://github.com/jywarren/imageboard"
|
||||||
}
|
}
|
||||||
|
|||||||
74
src/ImageBoard.js
Normal file
74
src/ImageBoard.js
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
ImageBoard = function ImageBoard(options) {
|
||||||
|
|
||||||
|
options = options || {};
|
||||||
|
options.container = options.container || '.panels';
|
||||||
|
|
||||||
|
var image;
|
||||||
|
var modules = require('./Modules');
|
||||||
|
var steps = [];
|
||||||
|
|
||||||
|
function addStep(name, stepOptions) {
|
||||||
|
steps.push({
|
||||||
|
module: modules[name]({
|
||||||
|
container: options.container // this is a bit redundant
|
||||||
|
}),
|
||||||
|
options: stepOptions
|
||||||
|
});
|
||||||
|
steps[steps.length - 1].module.setup();
|
||||||
|
}
|
||||||
|
|
||||||
|
addStep('image-select');
|
||||||
|
|
||||||
|
function setup() {
|
||||||
|
|
||||||
|
steps.forEach(function forEachStep(step, index) {
|
||||||
|
|
||||||
|
if (step.module.setup) step.module.setup();
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
setup();
|
||||||
|
|
||||||
|
// need prev() next() functions
|
||||||
|
|
||||||
|
function run() {
|
||||||
|
|
||||||
|
steps.forEach(function forEachStep(step) {
|
||||||
|
|
||||||
|
// step.module.run(onComplete);// initial image
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
// return image;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// load default starting image
|
||||||
|
// i.e. from parameter
|
||||||
|
function loadImage(src, callback) {
|
||||||
|
|
||||||
|
image = new Image();
|
||||||
|
|
||||||
|
image.onload = function() {
|
||||||
|
|
||||||
|
run();
|
||||||
|
|
||||||
|
if (callback) callback(image);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
image.src = src;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
loadImage: loadImage,
|
||||||
|
addStep: addStep,
|
||||||
|
run: run,
|
||||||
|
modules: modules,
|
||||||
|
steps: steps
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
160
src/Modules.js
Normal file
160
src/Modules.js
Normal file
@@ -0,0 +1,160 @@
|
|||||||
|
/*
|
||||||
|
* Core modules; externalized these wrapper modules with:
|
||||||
|
* 'image-select': require('./modules/ImageSelect.js'),
|
||||||
|
*/
|
||||||
|
module.exports = {
|
||||||
|
|
||||||
|
// How much of this wrapper is necessary?
|
||||||
|
// Could it be for UI, and the actual module is for functionality?
|
||||||
|
// But 'image-select' is not set up that way; it's UI. But it's special.
|
||||||
|
'image-select': function ImageSelect() {
|
||||||
|
|
||||||
|
var imageselect,
|
||||||
|
image;
|
||||||
|
|
||||||
|
function setup(onComplete) {
|
||||||
|
imageselect = require('./modules/ImageSelect.js')({
|
||||||
|
output: onComplete,
|
||||||
|
selector: '#drop'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function run() {
|
||||||
|
image = imageselect.getImage();
|
||||||
|
return image;
|
||||||
|
}
|
||||||
|
|
||||||
|
function get() {
|
||||||
|
return imageselect.getImage();
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
title: "Select image",
|
||||||
|
run: run,
|
||||||
|
setup: setup,
|
||||||
|
get: get
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
'passthrough': function Passthrough(options) {
|
||||||
|
|
||||||
|
options = options || {};
|
||||||
|
|
||||||
|
var image,
|
||||||
|
selector = 'mod-passthrough',
|
||||||
|
random = options.random || parseInt(Math.random() * (new Date()).getTime() / 1000000),
|
||||||
|
uniqueSelector = selector + '-' + random,
|
||||||
|
el;
|
||||||
|
|
||||||
|
// should we just run setup on constructor?
|
||||||
|
function setup() {
|
||||||
|
|
||||||
|
$(options.container).append('<div class="panel ' + selector + ' ' + uniqueSelector + '"></div>');
|
||||||
|
el = $(uniqueSelector);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function run(_image, onComplete, options) {
|
||||||
|
|
||||||
|
options = options || {};
|
||||||
|
options.format = options.format || "jpg";
|
||||||
|
|
||||||
|
// is global necessary? this is for browsers only
|
||||||
|
//global.Buffer = require('buffer');
|
||||||
|
|
||||||
|
var getPixels = require("get-pixels"),
|
||||||
|
savePixels = require("save-pixels"),
|
||||||
|
base64 = require('base64-stream');
|
||||||
|
|
||||||
|
getPixels(_image.src, function(err, pixels) {
|
||||||
|
|
||||||
|
if(err) {
|
||||||
|
console.log("Bad image path")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// iterate through pixels
|
||||||
|
for(var x = 1; x < pixels.shape[0]; x++) {
|
||||||
|
for(var y = 1; y < pixels.shape[1]; y++) {
|
||||||
|
|
||||||
|
// set each channel r, g, b, a
|
||||||
|
pixels.set(x, y, 0, pixels.get(x, y, 0));
|
||||||
|
pixels.set(x, y, 1, pixels.get(x, y, 0));
|
||||||
|
pixels.set(x, y, 2, pixels.get(x, y, 0));
|
||||||
|
pixels.set(x, y, 3, pixels.get(x, y, 3));
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var buffer = base64.encode();
|
||||||
|
savePixels(pixels, options.format)
|
||||||
|
.pipe(buffer)
|
||||||
|
|
||||||
|
// so this line needs time to run asynchronously. Look into how stream callbacks work, or if we can chain a .something(function(){}) to do the rest
|
||||||
|
|
||||||
|
pix = buffer;
|
||||||
|
var image = new Image();
|
||||||
|
|
||||||
|
/*
|
||||||
|
// these two won't work if run on the same line -- something needs to load
|
||||||
|
imageboard.steps[1].module.run(imageboard.steps[0].module.get());
|
||||||
|
$('.panel').last().html('<img src="data:image/jpeg;base64,'+pix.read().toString()+'" />')
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
// asynchronicity problem;
|
||||||
|
// this doesn't work, what's a real event:
|
||||||
|
buffer.on('write', function() {
|
||||||
|
|
||||||
|
image.src = buffer.read().toString();
|
||||||
|
|
||||||
|
console.log(image)
|
||||||
|
el.html(image)
|
||||||
|
if (onComplete) onComplete(image);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
title: "Pass through",
|
||||||
|
run: run,
|
||||||
|
setup: setup,
|
||||||
|
image: image
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
'image-threshold': {
|
||||||
|
name: "Threshold image",
|
||||||
|
run: function imageThreshold(image, onComplete, options) {
|
||||||
|
|
||||||
|
options = options || {};
|
||||||
|
options.threshold = options.threshold || 30;
|
||||||
|
|
||||||
|
var canvas = document.createElement('canvas');
|
||||||
|
var context = canvas.getContext('2d');
|
||||||
|
context.drawImage(image, 0, 0 );
|
||||||
|
var imageData = context.getImageData(0, 0, element.width, element.height);
|
||||||
|
|
||||||
|
var imageThreshold = require('../node_modules/image-filter-threshold/src/index.js');
|
||||||
|
|
||||||
|
var result = imageThreshold({
|
||||||
|
data: imageData,
|
||||||
|
threshold: options.threshold
|
||||||
|
}).then(function (result) {
|
||||||
|
var image = new Image();
|
||||||
|
image.onload = function onLoad() {
|
||||||
|
onComplete(image);
|
||||||
|
}
|
||||||
|
image.src = result;
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
@@ -1,12 +1,11 @@
|
|||||||
function imageselect(options) {
|
/*
|
||||||
|
* Special module to kick off the sequence
|
||||||
// fake jQuery-like DOM selector
|
* -- depends on jQuery for interface setup & drag & drop
|
||||||
$ = $ || function $(query){
|
*/
|
||||||
return document.querySelector(query);
|
module.exports = function ImageSelect(options) {
|
||||||
}
|
|
||||||
|
|
||||||
options = options || {};
|
options = options || {};
|
||||||
options.selector = options.selector || "#dropzone";
|
options.selector = options.selector || "#drop";
|
||||||
options.output = options.output || function output(image) {
|
options.output = options.output || function output(image) {
|
||||||
return image;
|
return image;
|
||||||
}
|
}
|
||||||
@@ -15,11 +14,11 @@ function imageselect(options) {
|
|||||||
|
|
||||||
// CSS UI
|
// CSS UI
|
||||||
|
|
||||||
$(options.selector).on('dragenter',function(e) {
|
$(options.selector).on('dragenter', function(e) {
|
||||||
$(options.selector).addClass('hover');
|
$(options.selector).addClass('hover');
|
||||||
});
|
});
|
||||||
|
|
||||||
$(options.selector).on('dragleave',function(e) {
|
$(options.selector).on('dragleave', function(e) {
|
||||||
$(options.selector).removeClass('hover');
|
$(options.selector).removeClass('hover');
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -37,8 +36,13 @@ function imageselect(options) {
|
|||||||
reader.onload = function(e) {
|
reader.onload = function(e) {
|
||||||
// we should trigger "load" event here
|
// we should trigger "load" event here
|
||||||
|
|
||||||
|
image = new Image();
|
||||||
|
image.src = event.target.result;
|
||||||
|
|
||||||
|
$(options.selector).html(image);
|
||||||
|
|
||||||
// this is done once per image:
|
// this is done once per image:
|
||||||
options.output(event.target.result);
|
options.output(image);
|
||||||
}
|
}
|
||||||
reader.readAsDataURL(f);
|
reader.readAsDataURL(f);
|
||||||
|
|
||||||
@@ -54,4 +58,12 @@ function imageselect(options) {
|
|||||||
$(options.selector).on('dragover', onDragOver, false);
|
$(options.selector).on('dragover', onDragOver, false);
|
||||||
$(options.selector)[0].addEventListener('drop', onDrop, false);
|
$(options.selector)[0].addEventListener('drop', onDrop, false);
|
||||||
|
|
||||||
|
function getImage() {
|
||||||
|
return image;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
getImage: getImage
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user