mirror of
https://github.com/publiclab/image-sequencer.git
synced 2025-12-07 17:00:02 +01:00
Adding easier to use interface for blend module and different blend modes (#1453)
* Bump data-uri-to-buffer from 2.0.1 to 3.0.0 Bumps [data-uri-to-buffer](https://github.com/TooTallNate/node-data-uri-to-buffer) from 2.0.1 to 3.0.0. - [Release notes](https://github.com/TooTallNate/node-data-uri-to-buffer/releases) - [Commits](https://github.com/TooTallNate/node-data-uri-to-buffer/compare/2.0.1...3.0.0) Signed-off-by: dependabot-preview[bot] <support@dependabot.com> * add blend modes * fix toCliString test * add docs link for blend modes * fix crop module * undo last commit * minor change * change default to custom * add docs link Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> Co-authored-by: Harsh Khandeparkar <34770591+HarshKhandeparkar@users.noreply.github.com> Co-authored-by: Jeffrey Warren <jeff@unterbahn.com>
This commit is contained in:
committed by
Jeffrey Warren
parent
1fa8c6b8c1
commit
c22c6c70d0
@@ -78,7 +78,8 @@ This module is used for averaging all the pixels of the image.
|
|||||||
|
|
||||||
## blend-module
|
## blend-module
|
||||||
|
|
||||||
This module is used for blending two images .
|
This module is used for blending two images. For More info read: _[wiki](https://en.wikipedia.org/wiki/Blend_modes)_
|
||||||
|
|
||||||
#### Usage
|
#### Usage
|
||||||
|
|
||||||
```js
|
```js
|
||||||
@@ -88,8 +89,12 @@ This module is used for blending two images .
|
|||||||
```
|
```
|
||||||
|
|
||||||
where `options` is an object with the following properties:
|
where `options` is an object with the following properties:
|
||||||
* offset: step of image with which current image is to be blended(Two steps back is -2, three steps back is -3 etc; default -2)
|
* offset: step of image with which current image is to be blended(Two steps back is -2, three steps back is -3 etc; default -2)
|
||||||
* func: function used to blend two images (default : function(r1, g1, b1, a1, r2, g2, b2, a2) { return [ r1, g2, b2, a2 ] })
|
* blendMode: Blending mode to use for blending two images by default it uses the given function
|
||||||
|
* func: function used to blend two images (default : function(r1, g1, b1, a1, r2, g2, b2, a2) { return [ r1, g2, b2, a2 ] })
|
||||||
|
|
||||||
|
[More info for different blend modes can be found here](http://docs.gimp.org/en/gimp-concepts-layer-modes.html)
|
||||||
|
|
||||||
|
|
||||||
## Blob Analysis
|
## Blob Analysis
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ module.exports = function Blend(options, UI, util) {
|
|||||||
|
|
||||||
options.func = options.blend || defaults.blend;
|
options.func = options.blend || defaults.blend;
|
||||||
options.offset = options.offset || defaults.offset;
|
options.offset = options.offset || defaults.offset;
|
||||||
|
options.blendMode = options.blendMode || defaults.blendMode;
|
||||||
|
|
||||||
var output;
|
var output;
|
||||||
|
|
||||||
@@ -29,22 +30,79 @@ module.exports = function Blend(options, UI, util) {
|
|||||||
callback();
|
callback();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// see http://docs.gimp.org/en/gimp-concepts-layer-modes.html for other blend modes
|
||||||
|
|
||||||
|
const multiply_mode = function (i, m) {
|
||||||
|
return ~~( (i * m) / 255 );
|
||||||
|
};
|
||||||
|
const divide_mode = function (i, m) {
|
||||||
|
return ~~( (256 * i) / (m + 1) );
|
||||||
|
};
|
||||||
|
|
||||||
|
const overlay_mode = function (i, m) {
|
||||||
|
return ~~( (i / 255) * (i + ((2 * m) / 255) * (255 - i)) );
|
||||||
|
};
|
||||||
|
|
||||||
|
const screen_mode = function (i, m) {
|
||||||
|
return ~~( 255 - ((255 - m) * (255 - i)) / 255 );
|
||||||
|
};
|
||||||
|
|
||||||
|
const sof_light_mode = function (i, m) {
|
||||||
|
var Rs = screen_mode(i, m);
|
||||||
|
return ~~( ((((255 - i) * m) + Rs) * i) / 255 );
|
||||||
|
};
|
||||||
|
|
||||||
|
const color_dodge = function (i, m) {
|
||||||
|
return ~~( (256 * i) / (255 - m + 1) );
|
||||||
|
};
|
||||||
|
|
||||||
|
const burn_mode = function (i, m) {
|
||||||
|
return ~~( 255 - (256 * (255 - i)) / (m + 1));
|
||||||
|
};
|
||||||
|
|
||||||
|
const grain_extract_mode = function (i, m) {
|
||||||
|
return ~~( i - m + 128 );
|
||||||
|
};
|
||||||
|
|
||||||
|
const grain_merge_mode = function (i, m) {
|
||||||
|
return ~~( i + m - 128 );
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
getPixels(priorStep.output.src, function(err, pixels) {
|
getPixels(priorStep.output.src, function(err, pixels) {
|
||||||
options.firstImagePixels = pixels;
|
options.firstImagePixels = pixels;
|
||||||
|
|
||||||
// Convert to runnable code.
|
// Convert to runnable code.
|
||||||
if (typeof options.func === 'string') eval('options.func = ' + options.func);
|
if (typeof options.func === 'string') eval('options.func = ' + options.func);
|
||||||
|
|
||||||
function changePixel(r2, g2, b2, a2, x, y) {
|
function changePixel(r2, g2, b2, a2, x, y) {
|
||||||
// blend!
|
// blend!
|
||||||
let p = options.firstImagePixels;
|
let p = options.firstImagePixels;
|
||||||
return options.func(
|
let r1 = p.get(x, y, 0),
|
||||||
r2, g2, b2, a2,
|
g1 = p.get(x, y, 1),
|
||||||
p.get(x, y, 0),
|
b1 = p.get(x, y, 2),
|
||||||
p.get(x, y, 1),
|
a1 = p.get(x, y, 3);
|
||||||
p.get(x, y, 2),
|
|
||||||
p.get(x, y, 3),
|
const blends = {
|
||||||
x,
|
'Color Dodge': () => [color_dodge(r2, r1), color_dodge(g2, g1), color_dodge(b2, b1), 255],
|
||||||
y
|
'Multiply': () => [multiply_mode(r2, r1), multiply_mode(g2, g1), multiply_mode(b2, b1), multiply_mode(a2, a1)],
|
||||||
);
|
'Divide': () => [divide_mode(r2, r1), divide_mode(g2, g1), divide_mode(b2, b1), 255],
|
||||||
|
'Overlay': () => [overlay_mode(r2, r1), overlay_mode(g2, g1), overlay_mode(b2, b1), 255],
|
||||||
|
'Screen': () => [screen_mode(r2, r1), screen_mode(g2, g1), screen_mode(b2, b1), 255],
|
||||||
|
'Soft Light': () => [sof_light_mode(r2, r1), sof_light_mode(g2, g1), sof_light_mode(b2, b1), 255],
|
||||||
|
'Color Burn': () => [burn_mode(r2, r1), burn_mode(g2, g1), burn_mode(b2, b1), 255],
|
||||||
|
'Grain Extract': () => [grain_extract_mode(r2, r1), grain_extract_mode(g2, g1), grain_extract_mode(b2, b1), 255],
|
||||||
|
'Grain Merge': () => [grain_merge_mode(r2, r1), grain_merge_mode(g2, g1), grain_merge_mode(b2, b1), 255]
|
||||||
|
};
|
||||||
|
|
||||||
|
if(options.blendMode == 'custom')
|
||||||
|
return options.func(
|
||||||
|
r2, g2, b2, a2, r1, g1, b1, a1
|
||||||
|
);
|
||||||
|
else {
|
||||||
|
return blends[options.blendMode]();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function output(image, datauri, mimetype, wasmSuccess) {
|
function output(image, datauri, mimetype, wasmSuccess) {
|
||||||
|
|||||||
@@ -1,17 +1,34 @@
|
|||||||
{
|
{
|
||||||
"name": "blend",
|
"name": "blend",
|
||||||
"description": "Blend two chosen 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!",
|
"description": "Blend two chosen 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.",
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"offset": {
|
"offset": {
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
"desc": "Choose which image to blend the current image with. Two steps back is -2, three steps back is -3 etc.",
|
"desc": "Choose which image to blend the current image with. Two steps back is -2, three steps back is -3 etc.",
|
||||||
"default": -2
|
"default": -2
|
||||||
},
|
},
|
||||||
|
"blendMode": {
|
||||||
|
"type": "select",
|
||||||
|
"desc": "Name of the Blend Mode to use",
|
||||||
|
"default": "custom",
|
||||||
|
"values": [
|
||||||
|
"custom",
|
||||||
|
"Multiply",
|
||||||
|
"Divide",
|
||||||
|
"Overlay",
|
||||||
|
"Screen",
|
||||||
|
"Soft Light",
|
||||||
|
"Color Burn",
|
||||||
|
"Color Dodge",
|
||||||
|
"Grain Extract",
|
||||||
|
"Grain Merge"
|
||||||
|
]
|
||||||
|
},
|
||||||
"blend": {
|
"blend": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"desc": "Function to use to blend the two images.",
|
"desc": "Function to use to blend the two images.",
|
||||||
"default": "function(r1, g1, b1, a1, r2, g2, b2, a2, x, y) { return [ r2, g2, b2, a2 ] }"
|
"default": "function(r1, g1, b1, a1, r2, g2, b2, a2, x, y) { return [ r2, g2, b2, a2 ] }"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"docs-link":"https://github.com/publiclab/image-sequencer/blob/main/docs/MODULES.md#blend-module"
|
"docs-link": "https://github.com/publiclab/image-sequencer/blob/main/docs/MODULES.md#blend-module"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -192,7 +192,7 @@ test('getStep(offset) returns the step at offset distance relative to current st
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('toCliString() returns the CLI command for the sequence', function(t) {
|
test('toCliString() returns the CLI command for the sequence', function(t) {
|
||||||
t.deepEqual(sequencer.toCliString(), 'sequencer -i [PATH] -s "channel channel channel channel invert brightness average brightness invert blend" -d \'{"channel":"green","brightness":"1","offset":-2}\'', 'works correctly');
|
t.deepEqual(sequencer.toCliString(), 'sequencer -i [PATH] -s "channel channel channel channel invert brightness average brightness invert blend" -d \'{"channel":"green","brightness":"1","offset":-2,"blendMode":"custom"}\'', 'works correctly');
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user