mirror of
https://github.com/konvajs/konva.git
synced 2026-03-03 16:58:33 +08:00
Added new filters from filter branch.
This commit is contained in:
12
Gruntfile.js
12
Gruntfile.js
@@ -37,13 +37,23 @@ module.exports = function(grunt) {
|
|||||||
'src/plugins/Label.js',
|
'src/plugins/Label.js',
|
||||||
|
|
||||||
// filters
|
// filters
|
||||||
|
'src/filters/FilterWrapper.js',
|
||||||
'src/filters/Grayscale.js',
|
'src/filters/Grayscale.js',
|
||||||
'src/filters/Brighten.js',
|
'src/filters/Brighten.js',
|
||||||
'src/filters/Invert.js',
|
'src/filters/Invert.js',
|
||||||
'src/filters/Blur.js',
|
'src/filters/Blur.js',
|
||||||
'src/filters/Mask.js',
|
'src/filters/Mask.js',
|
||||||
'src/filters/ColorPack.js',
|
'src/filters/ColorPack.js',
|
||||||
'src/filters/ConvolvePack.js'
|
'src/filters/ConvolvePack.js',
|
||||||
|
'src/filters/ColorStretch.js',
|
||||||
|
'src/filters/Flip.js',
|
||||||
|
'src/filters/Levels.js',
|
||||||
|
'src/filters/Mirror.js',
|
||||||
|
'src/filters/Noise.js',
|
||||||
|
'src/filters/Pixelate.js',
|
||||||
|
'src/filters/Polar.js',
|
||||||
|
'src/filters/Threshold.js'
|
||||||
|
|
||||||
];
|
];
|
||||||
|
|
||||||
// Project configuration.
|
// Project configuration.
|
||||||
|
|||||||
@@ -1,342 +1,318 @@
|
|||||||
/*
|
(function () {
|
||||||
the Gauss filter
|
|
||||||
master repo: https://github.com/pavelpower/kineticjsGaussFilter/
|
/**
|
||||||
*/
|
* BlurX Filter. Blurs the image in the X direction (horizontally). It
|
||||||
(function() {
|
* performs w*h pixel reads, and w*h pixel writes.
|
||||||
/*
|
* @function
|
||||||
|
* @author ippo615
|
||||||
StackBlur - a fast almost Gaussian Blur For Canvas
|
* @memberof Kinetic.Filters
|
||||||
|
* @param {ImageData} src, the source image data (what will be transformed)
|
||||||
Version: 0.5
|
* @param {ImageData} dst, the destination image data (where it will be saved)
|
||||||
Author: Mario Klingemann
|
* @param {Object} opt
|
||||||
Contact: mario@quasimondo.com
|
* @param {Number} [opt.blurWidth] how many neighboring pixels to will affect the
|
||||||
Website: http://www.quasimondo.com/StackBlurForCanvas
|
* blurred pixel, default: 5
|
||||||
Twitter: @quasimondo
|
*/
|
||||||
|
|
||||||
In case you find this class useful - especially in commercial projects -
|
var BlurX = function(src,dst,opt){
|
||||||
I am not totally unhappy for a small donation to my PayPal account
|
|
||||||
mario@quasimondo.de
|
var srcPixels = src.data,
|
||||||
|
dstPixels = dst.data,
|
||||||
Or support me on flattr:
|
xSize = src.width,
|
||||||
https://flattr.com/thing/72791/StackBlur-a-fast-almost-Gaussian-Blur-Effect-for-CanvasJavascript
|
ySize = src.height,
|
||||||
|
i, m, x, y, k, tmp, r=0,g=0,b=0,a=0;
|
||||||
Copyright (c) 2010 Mario Klingemann
|
|
||||||
|
// DONT USE: kSize = opt.blurWidth || 5;
|
||||||
Permission is hereby granted, free of charge, to any person
|
// HINT: consider when (opt.blurWidth = 0)
|
||||||
obtaining a copy of this software and associated documentation
|
var kSize = 5;
|
||||||
files (the "Software"), to deal in the Software without
|
if( opt.hasOwnProperty('blurWidth') ){
|
||||||
restriction, including without limitation the rights to use,
|
kSize = Math.round( Math.abs(opt.blurWidth) )+1;
|
||||||
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
}
|
||||||
copies of the Software, and to permit persons to whom the
|
var kMid = Math.floor(kSize/2);
|
||||||
Software is furnished to do so, subject to the following
|
|
||||||
conditions:
|
//console.info('Blur Width: '+kSize);
|
||||||
|
//console.info('Blur Middle: '+kMid);
|
||||||
The above copyright notice and this permission notice shall be
|
|
||||||
included in all copies or substantial portions of the Software.
|
var xEnd = xSize - kMid;
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
for (y = 0; y < ySize; y += 1) {
|
||||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
r=0;g=0;b=0;a=0;
|
||||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
for (x=-kMid; x<kMid; x+=1 ){
|
||||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
// Add the new
|
||||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
//if( y === 0 ){ console.info('Loading pixel at: '+((x+xSize)%xSize) ); }
|
||||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
i = (y * xSize + (x+xSize)%xSize ) * 4;
|
||||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
r += srcPixels[i+0];
|
||||||
OTHER DEALINGS IN THE SOFTWARE.
|
g += srcPixels[i+1];
|
||||||
*/
|
b += srcPixels[i+2];
|
||||||
|
a += srcPixels[i+3];
|
||||||
function BlurStack() {
|
}
|
||||||
this.r = 0;
|
|
||||||
this.g = 0;
|
for (x = 0; x < xSize; x += 1) {
|
||||||
this.b = 0;
|
//if( y === 0 ){ console.info('Added pixel at: '+(x+kMid)); }
|
||||||
this.a = 0;
|
//if( y === 0 ){ console.info('Recorded pixel at: '+x); }
|
||||||
this.next = null;
|
//if( y === 0 ){ console.info('Removed pixel at: '+((x-kMid+xSize)%xSize) ); }
|
||||||
}
|
// Add the new
|
||||||
|
i = (y * xSize + (x+kMid)%xSize ) * 4;
|
||||||
var mul_table = [
|
r += srcPixels[i+0];
|
||||||
512,512,456,512,328,456,335,512,405,328,271,456,388,335,292,512,
|
g += srcPixels[i+1];
|
||||||
454,405,364,328,298,271,496,456,420,388,360,335,312,292,273,512,
|
b += srcPixels[i+2];
|
||||||
482,454,428,405,383,364,345,328,312,298,284,271,259,496,475,456,
|
a += srcPixels[i+3];
|
||||||
437,420,404,388,374,360,347,335,323,312,302,292,282,273,265,512,
|
// Store the result
|
||||||
497,482,468,454,441,428,417,405,394,383,373,364,354,345,337,328,
|
i = (y * xSize + x) * 4;
|
||||||
320,312,305,298,291,284,278,271,265,259,507,496,485,475,465,456,
|
dstPixels[i+0] = r/kSize;
|
||||||
446,437,428,420,412,404,396,388,381,374,367,360,354,347,341,335,
|
dstPixels[i+1] = g/kSize;
|
||||||
329,323,318,312,307,302,297,292,287,282,278,273,269,265,261,512,
|
dstPixels[i+2] = b/kSize;
|
||||||
505,497,489,482,475,468,461,454,447,441,435,428,422,417,411,405,
|
dstPixels[i+3] = a/kSize;
|
||||||
399,394,389,383,378,373,368,364,359,354,350,345,341,337,332,328,
|
// Subtract the old
|
||||||
324,320,316,312,309,305,301,298,294,291,287,284,281,278,274,271,
|
i = (y * xSize + (x-kMid+xSize)%xSize ) * 4;
|
||||||
268,265,262,259,257,507,501,496,491,485,480,475,470,465,460,456,
|
r -= srcPixels[i+0];
|
||||||
451,446,442,437,433,428,424,420,416,412,408,404,400,396,392,388,
|
g -= srcPixels[i+1];
|
||||||
385,381,377,374,370,367,363,360,357,354,350,347,344,341,338,335,
|
b -= srcPixels[i+2];
|
||||||
332,329,326,323,320,318,315,312,310,307,304,302,299,297,294,292,
|
a -= srcPixels[i+3];
|
||||||
289,287,285,282,280,278,275,273,271,269,267,265,263,261,259];
|
}
|
||||||
|
|
||||||
var shg_table = [
|
}
|
||||||
9, 11, 12, 13, 13, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 17,
|
|
||||||
17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 18, 19,
|
};
|
||||||
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 20, 20, 20,
|
|
||||||
20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 21,
|
var _BlurX = function(src,dst,opt){
|
||||||
21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
|
|
||||||
21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22,
|
var srcPixels = src.data,
|
||||||
22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
|
dstPixels = dst.data,
|
||||||
22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23,
|
xSize = src.width,
|
||||||
23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
|
ySize = src.height,
|
||||||
23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
|
i, m, x, y, k, tmp, r=0,g=0,b=0,a=0;
|
||||||
23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
|
|
||||||
23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
|
var kSize = opt.blurWidth || 5,
|
||||||
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
|
kMid = Math.floor(kSize/2);
|
||||||
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
|
|
||||||
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
|
var xEnd = xSize - kMid;
|
||||||
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24 ];
|
|
||||||
|
for (y = 0; y < ySize; y += 1) {
|
||||||
function filterGaussBlurRGBA( imageData, radius) {
|
// Let's pretend we have a row of pixels with values: [1,2,3,4,5,6,7,8]
|
||||||
|
// To blur them, we need to compute a moving average as we move across it
|
||||||
var pixels = imageData.data,
|
// Let's say the moving average will have 5 elements in it, that means
|
||||||
width = imageData.width,
|
// we need a buffer of 5 elements but we're summing them everytime so
|
||||||
height = imageData.height;
|
// we'll just store the sum.
|
||||||
|
// To start we add everything before the (5/2)=2.5, 2nd element
|
||||||
var x, y, i, p, yp, yi, yw, r_sum, g_sum, b_sum, a_sum,
|
// Then we add the 3rd element
|
||||||
r_out_sum, g_out_sum, b_out_sum, a_out_sum,
|
// 12345 12345 12345 12345 12345
|
||||||
r_in_sum, g_in_sum, b_in_sum, a_in_sum,
|
// 123 1234 12345 2345 345
|
||||||
pr, pg, pb, pa, rbs;
|
// 6 6A 6AF 6AFE 6AFEC
|
||||||
|
r=0;g=0;b=0;a=0;
|
||||||
var div = radius + radius + 1,
|
for (x=0; x<kMid; x+=1 ){
|
||||||
widthMinus1 = width - 1,
|
// Add the new
|
||||||
heightMinus1 = height - 1,
|
i = (y * xSize + x ) * 4;
|
||||||
radiusPlus1 = radius + 1,
|
r += srcPixels[i+0];
|
||||||
sumFactor = radiusPlus1 * ( radiusPlus1 + 1 ) / 2,
|
g += srcPixels[i+1];
|
||||||
stackStart = new BlurStack(),
|
b += srcPixels[i+2];
|
||||||
stackEnd = null,
|
a += srcPixels[i+3];
|
||||||
stack = stackStart,
|
}
|
||||||
stackIn = null,
|
for (x=0, tmp=kMid; x<kMid; x+=1,tmp+=1 ){
|
||||||
stackOut = null,
|
// Add the new
|
||||||
mul_sum = mul_table[radius],
|
i = (y * xSize + x+kMid ) * 4;
|
||||||
shg_sum = shg_table[radius];
|
r += srcPixels[i+0];
|
||||||
|
g += srcPixels[i+1];
|
||||||
for ( i = 1; i < div; i++ ) {
|
b += srcPixels[i+2];
|
||||||
stack = stack.next = new BlurStack();
|
a += srcPixels[i+3];
|
||||||
if ( i == radiusPlus1 ) stackEnd = stack;
|
// Store it
|
||||||
}
|
i = (y * xSize + x) * 4;
|
||||||
|
dstPixels[i+0] = r/kSize;
|
||||||
stack.next = stackStart;
|
dstPixels[i+1] = g/kSize;
|
||||||
|
dstPixels[i+2] = b/kSize;
|
||||||
yw = yi = 0;
|
dstPixels[i+3] = a/kSize;
|
||||||
|
}
|
||||||
for ( y = 0; y < height; y++ )
|
for (x = kMid; x < xEnd; x += 1) {
|
||||||
{
|
// Add the new
|
||||||
r_in_sum = g_in_sum = b_in_sum = a_in_sum = r_sum = g_sum = b_sum = a_sum = 0;
|
i = (y * xSize + x+kMid ) * 4;
|
||||||
|
r += srcPixels[i+0];
|
||||||
r_out_sum = radiusPlus1 * ( pr = pixels[yi] );
|
g += srcPixels[i+1];
|
||||||
g_out_sum = radiusPlus1 * ( pg = pixels[yi+1] );
|
b += srcPixels[i+2];
|
||||||
b_out_sum = radiusPlus1 * ( pb = pixels[yi+2] );
|
a += srcPixels[i+3];
|
||||||
a_out_sum = radiusPlus1 * ( pa = pixels[yi+3] );
|
// Subtract the old
|
||||||
|
i = (y * xSize + x-kMid ) * 4;
|
||||||
r_sum += sumFactor * pr;
|
r -= srcPixels[i+0];
|
||||||
g_sum += sumFactor * pg;
|
g -= srcPixels[i+1];
|
||||||
b_sum += sumFactor * pb;
|
b -= srcPixels[i+2];
|
||||||
a_sum += sumFactor * pa;
|
a -= srcPixels[i+3];
|
||||||
|
// Store the result
|
||||||
stack = stackStart;
|
i = (y * xSize + x) * 4;
|
||||||
|
dstPixels[i+0] = r/kSize;
|
||||||
for( i = 0; i < radiusPlus1; i++ )
|
dstPixels[i+1] = g/kSize;
|
||||||
{
|
dstPixels[i+2] = b/kSize;
|
||||||
stack.r = pr;
|
dstPixels[i+3] = a/kSize;
|
||||||
stack.g = pg;
|
}
|
||||||
stack.b = pb;
|
for (x=xEnd; x<xSize; x+=1 ){
|
||||||
stack.a = pa;
|
// Subtract the old
|
||||||
stack = stack.next;
|
i = (y * xSize + x - kMid ) * 4;
|
||||||
}
|
r -= srcPixels[i+0];
|
||||||
|
g -= srcPixels[i+1];
|
||||||
for( i = 1; i < radiusPlus1; i++ )
|
b -= srcPixels[i+2];
|
||||||
{
|
a -= srcPixels[i+3];
|
||||||
p = yi + (( widthMinus1 < i ? widthMinus1 : i ) << 2 );
|
// Store it
|
||||||
r_sum += ( stack.r = ( pr = pixels[p])) * ( rbs = radiusPlus1 - i );
|
i = (y * xSize + x) * 4;
|
||||||
g_sum += ( stack.g = ( pg = pixels[p+1])) * rbs;
|
dstPixels[i+0] = r/kSize;
|
||||||
b_sum += ( stack.b = ( pb = pixels[p+2])) * rbs;
|
dstPixels[i+1] = g/kSize;
|
||||||
a_sum += ( stack.a = ( pa = pixels[p+3])) * rbs;
|
dstPixels[i+2] = b/kSize;
|
||||||
|
dstPixels[i+3] = a/kSize;
|
||||||
r_in_sum += pr;
|
}
|
||||||
g_in_sum += pg;
|
}
|
||||||
b_in_sum += pb;
|
|
||||||
a_in_sum += pa;
|
};
|
||||||
|
|
||||||
stack = stack.next;
|
/**
|
||||||
}
|
* BlurY Filter. Blurs the image in the Y direction (vertically). It
|
||||||
|
* performs w*h pixel reads, and w*h pixel writes.
|
||||||
|
* @function
|
||||||
stackIn = stackStart;
|
* @author ippo615
|
||||||
stackOut = stackEnd;
|
* @memberof Kinetic.Filters
|
||||||
for ( x = 0; x < width; x++ )
|
* @param {ImageData} src, the source image data (what will be transformed)
|
||||||
{
|
* @param {ImageData} dst, the destination image data (where it will be saved)
|
||||||
pixels[yi+3] = pa = (a_sum * mul_sum) >> shg_sum;
|
* @param {Object} opt
|
||||||
if ( pa !== 0 )
|
* @param {Number} [opt.blurHeight] how many neighboring pixels to will affect the
|
||||||
{
|
* blurred pixel, default: 5
|
||||||
pa = 255 / pa;
|
*/
|
||||||
pixels[yi] = ((r_sum * mul_sum) >> shg_sum) * pa;
|
|
||||||
pixels[yi+1] = ((g_sum * mul_sum) >> shg_sum) * pa;
|
var BlurY = function(src,dst,opt){
|
||||||
pixels[yi+2] = ((b_sum * mul_sum) >> shg_sum) * pa;
|
|
||||||
} else {
|
var srcPixels = src.data,
|
||||||
pixels[yi] = pixels[yi+1] = pixels[yi+2] = 0;
|
dstPixels = dst.data,
|
||||||
}
|
xSize = src.width,
|
||||||
|
ySize = src.height,
|
||||||
r_sum -= r_out_sum;
|
i, m, x, y, k, tmp, r=0,g=0,b=0,a=0;
|
||||||
g_sum -= g_out_sum;
|
|
||||||
b_sum -= b_out_sum;
|
var kSize = 5;
|
||||||
a_sum -= a_out_sum;
|
if( opt.hasOwnProperty('blurHeight') ){
|
||||||
|
kSize = Math.round( Math.abs(opt.blurHeight) )+1;
|
||||||
r_out_sum -= stackIn.r;
|
}
|
||||||
g_out_sum -= stackIn.g;
|
var kMid = Math.floor(kSize/2);
|
||||||
b_out_sum -= stackIn.b;
|
|
||||||
a_out_sum -= stackIn.a;
|
var yEnd = ySize - kMid;
|
||||||
|
|
||||||
p = ( yw + ( ( p = x + radius + 1 ) < widthMinus1 ? p : widthMinus1 ) ) << 2;
|
for (x = 0; x < xSize; x += 1) {
|
||||||
|
r=0;g=0;b=0;a=0;
|
||||||
r_in_sum += ( stackIn.r = pixels[p]);
|
for (y=-kMid; y<kMid; y+=1 ){
|
||||||
g_in_sum += ( stackIn.g = pixels[p+1]);
|
// Add the new
|
||||||
b_in_sum += ( stackIn.b = pixels[p+2]);
|
i = ((y+ySize)%ySize * xSize + x ) * 4;
|
||||||
a_in_sum += ( stackIn.a = pixels[p+3]);
|
r += srcPixels[i+0];
|
||||||
|
g += srcPixels[i+1];
|
||||||
r_sum += r_in_sum;
|
b += srcPixels[i+2];
|
||||||
g_sum += g_in_sum;
|
a += srcPixels[i+3];
|
||||||
b_sum += b_in_sum;
|
}
|
||||||
a_sum += a_in_sum;
|
|
||||||
|
for (y = 0; y < ySize; y += 1) {
|
||||||
stackIn = stackIn.next;
|
// Add the new
|
||||||
|
i = ((y+kMid+ySize)%ySize * xSize + x ) * 4;
|
||||||
r_out_sum += ( pr = stackOut.r );
|
r += srcPixels[i+0];
|
||||||
g_out_sum += ( pg = stackOut.g );
|
g += srcPixels[i+1];
|
||||||
b_out_sum += ( pb = stackOut.b );
|
b += srcPixels[i+2];
|
||||||
a_out_sum += ( pa = stackOut.a );
|
a += srcPixels[i+3];
|
||||||
|
// Store the result
|
||||||
r_in_sum -= pr;
|
i = (y * xSize + x) * 4;
|
||||||
g_in_sum -= pg;
|
dstPixels[i+0] = r/kSize;
|
||||||
b_in_sum -= pb;
|
dstPixels[i+1] = g/kSize;
|
||||||
a_in_sum -= pa;
|
dstPixels[i+2] = b/kSize;
|
||||||
|
dstPixels[i+3] = a/kSize;
|
||||||
stackOut = stackOut.next;
|
// Subtract the old
|
||||||
|
i = ((y-kMid+ySize)%ySize * xSize + x ) * 4;
|
||||||
yi += 4;
|
r -= srcPixels[i+0];
|
||||||
}
|
g -= srcPixels[i+1];
|
||||||
yw += width;
|
b -= srcPixels[i+2];
|
||||||
}
|
a -= srcPixels[i+3];
|
||||||
|
}
|
||||||
|
|
||||||
for ( x = 0; x < width; x++ )
|
}
|
||||||
{
|
|
||||||
g_in_sum = b_in_sum = a_in_sum = r_in_sum = g_sum = b_sum = a_sum = r_sum = 0;
|
};
|
||||||
|
|
||||||
yi = x << 2;
|
var _BlurY = function(src,dst,opt){
|
||||||
r_out_sum = radiusPlus1 * ( pr = pixels[yi]);
|
|
||||||
g_out_sum = radiusPlus1 * ( pg = pixels[yi+1]);
|
var srcPixels = src.data,
|
||||||
b_out_sum = radiusPlus1 * ( pb = pixels[yi+2]);
|
dstPixels = dst.data,
|
||||||
a_out_sum = radiusPlus1 * ( pa = pixels[yi+3]);
|
xSize = src.width,
|
||||||
|
ySize = src.height,
|
||||||
r_sum += sumFactor * pr;
|
i, m, x, y, k, tmp, r=0,g=0,b=0,a=0;
|
||||||
g_sum += sumFactor * pg;
|
|
||||||
b_sum += sumFactor * pb;
|
var kSize = opt.blurHeight || 5,
|
||||||
a_sum += sumFactor * pa;
|
kMid = Math.floor(kSize/2);
|
||||||
|
|
||||||
stack = stackStart;
|
var yEnd = ySize - kMid;
|
||||||
|
|
||||||
for( i = 0; i < radiusPlus1; i++ )
|
for (x = 0; x < xSize; x += 1) {
|
||||||
{
|
r=0;g=0;b=0;a=0;
|
||||||
stack.r = pr;
|
for (y=0; y<kMid; y+=1 ){
|
||||||
stack.g = pg;
|
// Add the new
|
||||||
stack.b = pb;
|
i = (y * xSize + x ) * 4;
|
||||||
stack.a = pa;
|
r += srcPixels[i+0];
|
||||||
stack = stack.next;
|
g += srcPixels[i+1];
|
||||||
}
|
b += srcPixels[i+2];
|
||||||
|
a += srcPixels[i+3];
|
||||||
yp = width;
|
}
|
||||||
|
for (y=0, tmp=kMid; y<kMid; y+=1,tmp+=1 ){
|
||||||
for( i = 1; i <= radius; i++ )
|
// Add the new
|
||||||
{
|
i = ((y+kMid) * xSize + x ) * 4;
|
||||||
yi = ( yp + x ) << 2;
|
r += srcPixels[i+0];
|
||||||
|
g += srcPixels[i+1];
|
||||||
r_sum += ( stack.r = ( pr = pixels[yi])) * ( rbs = radiusPlus1 - i );
|
b += srcPixels[i+2];
|
||||||
g_sum += ( stack.g = ( pg = pixels[yi+1])) * rbs;
|
a += srcPixels[i+3];
|
||||||
b_sum += ( stack.b = ( pb = pixels[yi+2])) * rbs;
|
// Store it
|
||||||
a_sum += ( stack.a = ( pa = pixels[yi+3])) * rbs;
|
i = (y * xSize + x) * 4;
|
||||||
|
dstPixels[i+0] = r/kSize;
|
||||||
r_in_sum += pr;
|
dstPixels[i+1] = g/kSize;
|
||||||
g_in_sum += pg;
|
dstPixels[i+2] = b/kSize;
|
||||||
b_in_sum += pb;
|
dstPixels[i+3] = a/kSize;
|
||||||
a_in_sum += pa;
|
}
|
||||||
|
for (y = kMid; y < yEnd; y += 1) {
|
||||||
stack = stack.next;
|
// Add the new
|
||||||
|
i = ((y+kMid) * xSize + x ) * 4;
|
||||||
if( i < heightMinus1 )
|
r += srcPixels[i+0];
|
||||||
{
|
g += srcPixels[i+1];
|
||||||
yp += width;
|
b += srcPixels[i+2];
|
||||||
}
|
a += srcPixels[i+3];
|
||||||
}
|
// Subtract the old
|
||||||
|
i = ((y-kMid) * xSize + x ) * 4;
|
||||||
yi = x;
|
r -= srcPixels[i+0];
|
||||||
stackIn = stackStart;
|
g -= srcPixels[i+1];
|
||||||
stackOut = stackEnd;
|
b -= srcPixels[i+2];
|
||||||
for ( y = 0; y < height; y++ )
|
a -= srcPixels[i+3];
|
||||||
{
|
// Store the result
|
||||||
p = yi << 2;
|
i = (y * xSize + x) * 4;
|
||||||
pixels[p+3] = pa = (a_sum * mul_sum) >> shg_sum;
|
dstPixels[i+0] = r/kSize;
|
||||||
if ( pa > 0 )
|
dstPixels[i+1] = g/kSize;
|
||||||
{
|
dstPixels[i+2] = b/kSize;
|
||||||
pa = 255 / pa;
|
dstPixels[i+3] = a/kSize;
|
||||||
pixels[p] = ((r_sum * mul_sum) >> shg_sum ) * pa;
|
}
|
||||||
pixels[p+1] = ((g_sum * mul_sum) >> shg_sum ) * pa;
|
for (y=yEnd; y<ySize; y+=1 ){
|
||||||
pixels[p+2] = ((b_sum * mul_sum) >> shg_sum ) * pa;
|
// Subtract the old
|
||||||
} else {
|
i = ((y-kMid) * xSize + x ) * 4;
|
||||||
pixels[p] = pixels[p+1] = pixels[p+2] = 0;
|
r -= srcPixels[i+0];
|
||||||
}
|
g -= srcPixels[i+1];
|
||||||
|
b -= srcPixels[i+2];
|
||||||
r_sum -= r_out_sum;
|
a -= srcPixels[i+3];
|
||||||
g_sum -= g_out_sum;
|
// Store it
|
||||||
b_sum -= b_out_sum;
|
i = (y * xSize + x) * 4;
|
||||||
a_sum -= a_out_sum;
|
dstPixels[i+0] = r/kSize;
|
||||||
|
dstPixels[i+1] = g/kSize;
|
||||||
r_out_sum -= stackIn.r;
|
dstPixels[i+2] = b/kSize;
|
||||||
g_out_sum -= stackIn.g;
|
dstPixels[i+3] = a/kSize;
|
||||||
b_out_sum -= stackIn.b;
|
}
|
||||||
a_out_sum -= stackIn.a;
|
}
|
||||||
|
|
||||||
p = ( x + (( ( p = y + radiusPlus1) < heightMinus1 ? p : heightMinus1 ) * width )) << 2;
|
};
|
||||||
|
|
||||||
r_sum += ( r_in_sum += ( stackIn.r = pixels[p]));
|
Kinetic.Filters.BlurX = Kinetic.Util._FilterWrapSingleBuffer(BlurX);
|
||||||
g_sum += ( g_in_sum += ( stackIn.g = pixels[p+1]));
|
Kinetic.Filters.BlurY = Kinetic.Util._FilterWrapSingleBuffer(BlurY);
|
||||||
b_sum += ( b_in_sum += ( stackIn.b = pixels[p+2]));
|
|
||||||
a_sum += ( a_in_sum += ( stackIn.a = pixels[p+3]));
|
Kinetic.Factory.addFilterGetterSetter(Kinetic.Image, 'filterRadius', 5);
|
||||||
|
|
||||||
stackIn = stackIn.next;
|
Kinetic.Filters.Blur = Kinetic.Util._FilterWrapSingleBuffer(function(src,dst,opt){
|
||||||
|
opt = opt || {
|
||||||
r_out_sum += ( pr = stackOut.r );
|
blurWidth: this.getFilterRadius(),
|
||||||
g_out_sum += ( pg = stackOut.g );
|
blurHeight: this.getFilterRadius()
|
||||||
b_out_sum += ( pb = stackOut.b );
|
};
|
||||||
a_out_sum += ( pa = stackOut.a );
|
Kinetic.Filters.BlurX(src,src,opt);
|
||||||
|
Kinetic.Filters.BlurY(src,dst,opt);
|
||||||
r_in_sum -= pr;
|
// Move the destination to the source
|
||||||
g_in_sum -= pg;
|
//Kinetic.Util._FilterReplaceBuffer(dst,src);
|
||||||
b_in_sum -= pb;
|
});
|
||||||
a_in_sum -= pa;
|
|
||||||
|
})();
|
||||||
stackOut = stackOut.next;
|
|
||||||
|
|
||||||
yi += width;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Blur Filter
|
|
||||||
* @function
|
|
||||||
* @memberof Kinetic.Filters
|
|
||||||
* @param {Object} imageData
|
|
||||||
*/
|
|
||||||
Kinetic.Filters.Blur = function(imageData) {
|
|
||||||
var radius = this.getFilterRadius() | 0;
|
|
||||||
|
|
||||||
if (radius > 0) {
|
|
||||||
filterGaussBlurRGBA(imageData, radius);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Kinetic.Factory.addFilterGetterSetter(Kinetic.Image, 'filterRadius', 0);
|
|
||||||
|
|
||||||
})();
|
|
||||||
|
|||||||
@@ -1,152 +1,228 @@
|
|||||||
(function() {
|
(function () {
|
||||||
|
|
||||||
var rgb_to_hsl = function(r,g,b){
|
/**
|
||||||
// Input colors are in 0-255, calculations are between 0-1
|
* HSV Filter. Adjusts the hue, saturation and value of an image.
|
||||||
r /= 255; g /= 255; b /= 255;
|
* Performs w*h pixel reads and w*h pixel writes.
|
||||||
|
* @function
|
||||||
// http://en.wikipedia.org/wiki/HSL_and_HSV
|
* @author ippo615
|
||||||
// Convert to HSL
|
* @memberof Kinetic.Filters
|
||||||
var max = Math.max(r,g,b),
|
* @param {ImageData} src, the source image data (what will be transformed)
|
||||||
min = Math.min(r,g,b),
|
* @param {ImageData} dst, the destination image data (where it will be saved)
|
||||||
chroma = max - min,
|
* @param {Object} opt
|
||||||
luminance = chroma / 2,
|
* @param {Number} [opt.hue] amount to shift to the hue (in degrees)
|
||||||
saturation = chroma / (1 - Math.abs(2*luminance-1)),
|
* 0 represents no shift, while 360 is the maximum. Default: 0
|
||||||
hue = 0;
|
* @param {Number} [opt.saturation] amount to scale the saturation.
|
||||||
|
* 1 means no change, 0.5 halves (more gray), 2.0 doubles
|
||||||
if( max == r ){ hue = ((g-b)/chroma) % 6; }else
|
* (more color), etc... Default is 1.
|
||||||
if( max == g ){ hue = (b-r)/chroma + 2; }else
|
* @param {Number} [opt.value] amount to scale the value.
|
||||||
if( max == b ){ hue = (r-g)/chroma + 4; }
|
* 1 means no change, 0.5 halves (darker), 2.0 doubles (lighter), etc..
|
||||||
|
* Default is 1.
|
||||||
return [(hue*60+360) % 360, saturation, luminance];
|
*/
|
||||||
};
|
|
||||||
|
var HSV = function (src, dst, opt) {
|
||||||
var pixelShiftHue = function(r,g,b,deg){
|
var srcPixels = src.data,
|
||||||
|
dstPixels = dst.data,
|
||||||
// Input colors are in 0-255, calculations are between 0-1
|
nPixels = srcPixels.length,
|
||||||
r /= 255; g /= 255; b /= 255;
|
i;
|
||||||
|
|
||||||
// http://en.wikipedia.org/wiki/HSL_and_HSV
|
var v = opt.value || 1,
|
||||||
// Convert to HSL
|
s = opt.saturation || 1,
|
||||||
var max = Math.max(r,g,b),
|
h = Math.abs((opt.hue || 0) + 360) % 360;
|
||||||
min = Math.min(r,g,b),
|
|
||||||
chroma = max - min,
|
// Basis for the technique used:
|
||||||
luminance = chroma / 2,
|
// http://beesbuzz.biz/code/hsv_color_transforms.php
|
||||||
saturation = chroma / (1 - Math.abs(2*luminance-1)),
|
// V is the value multiplier (1 for none, 2 for double, 0.5 for half)
|
||||||
hue = 0;
|
// S is the saturation multiplier (1 for none, 2 for double, 0.5 for half)
|
||||||
|
// H is the hue shift in degrees (0 to 360)
|
||||||
if( max == r ){ hue = ((g-b)/chroma) % 6; }else
|
// vsu = V*S*cos(H*PI/180);
|
||||||
if( max == g ){ hue = (b-r)/chroma + 2; }else
|
// vsw = V*S*sin(H*PI/180);
|
||||||
if( max == b ){ hue = (r-g)/chroma + 4; }
|
//[ .299V+.701vsu+.168vsw .587V-.587vsu+.330vsw .114V-.114vsu-.497vsw ] [R]
|
||||||
|
//[ .299V-.299vsu-.328vsw .587V+.413vsu+.035vsw .114V-.114vsu+.292vsw ]*[G]
|
||||||
hue *= 60;
|
//[ .299V-.300vsu+1.25vsw .587V-.588vsu-1.05vsw .114V+.886vsu-.203vsw ] [B]
|
||||||
hue %= 360;
|
|
||||||
|
// Precompute the values in the matrix:
|
||||||
// Shift hue
|
var vsu = v*s*Math.cos(h*Math.PI/180),
|
||||||
hue += deg;
|
vsw = v*s*Math.sin(h*Math.PI/180);
|
||||||
hue %= 360;
|
// (result spot)(source spot)
|
||||||
//hue /= 360;
|
var rr = 0.299*v+0.701*vsu+0.167*vsw,
|
||||||
|
rg = 0.587*v-0.587*vsu+0.330*vsw,
|
||||||
// hsl to rgb:
|
rb = 0.114*v-0.114*vsu-0.497*vsw;
|
||||||
hue /= 60;
|
var gr = 0.299*v-0.299*vsu-0.328*vsw,
|
||||||
var rR = 0, rG = 0, rB = 0,
|
gg = 0.587*v+0.413*vsu+0.035*vsw,
|
||||||
//chroma = saturation*(1 - Math.abs(2*luminance - 1)),
|
gb = 0.114*v-0.114*vsu+0.293*vsw;
|
||||||
tmp = chroma * (1-Math.abs(hue % 2 - 1)),
|
var br = 0.299*v-0.300*vsu+1.250*vsw,
|
||||||
m = luminance - chroma/2;
|
bg = 0.587*v-0.586*vsu-1.050*vsw,
|
||||||
|
bb = 0.114*v+0.886*vsu-0.200*vsw;
|
||||||
if( 0 <= hue && hue < 1 ){ rR = chroma; rG = tmp; }else
|
|
||||||
if( 1 <= hue && hue < 2 ){ rG = chroma; rR = tmp; }else
|
var r,g,b,a;
|
||||||
if( 2 <= hue && hue < 3 ){ rG = chroma; rB = tmp; }else
|
|
||||||
if( 3 <= hue && hue < 4 ){ rB = chroma; rG = tmp; }else
|
for (i = 0; i < nPixels; i += 4) {
|
||||||
if( 4 <= hue && hue < 5 ){ rB = chroma; rR = tmp; }else
|
r = srcPixels[i+0];
|
||||||
if( 5 <= hue && hue < 6 ){ rR = chroma; rB = tmp; }
|
g = srcPixels[i+1];
|
||||||
|
b = srcPixels[i+2];
|
||||||
rR += m; rG += m; rB += m;
|
a = srcPixels[i+3];
|
||||||
rR = (255*rR);
|
|
||||||
rG = (255*rG);
|
dstPixels[i+0] = rr*r + rg*g + rb*b;
|
||||||
rB = (255*rB);
|
dstPixels[i+1] = gr*r + gg*g + gb*b;
|
||||||
|
dstPixels[i+2] = br*r + bg*g + bb*b;
|
||||||
return [rR,rG,rB];
|
dstPixels[i+3] = a; // alpha
|
||||||
};
|
}
|
||||||
|
|
||||||
var shift_hue = function(imageData,deg){
|
};
|
||||||
var data = imageData.data,
|
|
||||||
pixel;
|
Kinetic.Filters.HSV = Kinetic.Util._FilterWrapSingleBuffer(HSV);
|
||||||
for(var i = 0; i < data.length; i += 4) {
|
|
||||||
pixel = pixelShiftHue(data[i+0],data[i+1],data[i+2],deg);
|
})();
|
||||||
data[i+0] = pixel[0];
|
|
||||||
data[i+1] = pixel[1];
|
(function() {
|
||||||
data[i+2] = pixel[2];
|
|
||||||
}
|
var rgb_to_hsl = function(r,g,b){
|
||||||
};
|
// Input colors are in 0-255, calculations are between 0-1
|
||||||
|
r /= 255; g /= 255; b /= 255;
|
||||||
/**
|
|
||||||
* Shift Hue Filter.
|
// http://en.wikipedia.org/wiki/HSL_and_HSV
|
||||||
* @function
|
// Convert to HSL
|
||||||
* @memberof Kinetic.Filters
|
var max = Math.max(r,g,b),
|
||||||
* @param {Object} imageData
|
min = Math.min(r,g,b),
|
||||||
* @author ippo615
|
chroma = max - min,
|
||||||
*/
|
luminance = chroma / 2,
|
||||||
Kinetic.Filters.ShiftHue = function(imageData) {
|
saturation = chroma / (1 - Math.abs(2*luminance-1)),
|
||||||
shift_hue(imageData, this.getFilterHueShiftDeg() % 360 );
|
hue = 0;
|
||||||
};
|
|
||||||
|
if( max == r ){ hue = ((g-b)/chroma) % 6; }else
|
||||||
Kinetic.Factory.addFilterGetterSetter(Kinetic.Image, 'filterHueShiftDeg', 0);
|
if( max == g ){ hue = (b-r)/chroma + 2; }else
|
||||||
/**
|
if( max == b ){ hue = (r-g)/chroma + 4; }
|
||||||
* get hue shift amount. The shift amount is a number between 0 and 360.
|
|
||||||
* @name getFilterBrightness
|
return [(hue*60+360) % 360, saturation, luminance];
|
||||||
* @method
|
};
|
||||||
* @memberof Kinetic.Image.prototype
|
|
||||||
*/
|
var pixelShiftHue = function(r,g,b,deg){
|
||||||
|
|
||||||
/**
|
// Input colors are in 0-255, calculations are between 0-1
|
||||||
* set hue shift amount
|
r /= 255; g /= 255; b /= 255;
|
||||||
* @name setFilterBrightness
|
|
||||||
* @method
|
// http://en.wikipedia.org/wiki/HSL_and_HSV
|
||||||
* @memberof Kinetic.Image.prototype
|
// Convert to HSL
|
||||||
*/
|
var max = Math.max(r,g,b),
|
||||||
|
min = Math.min(r,g,b),
|
||||||
|
chroma = max - min,
|
||||||
/**
|
luminance = chroma / 2,
|
||||||
* Colorize Filter.
|
saturation = chroma / (1 - Math.abs(2*luminance-1)),
|
||||||
* colorizes the image so that it is just varying shades of the specified color
|
hue = 0;
|
||||||
* @function
|
|
||||||
* @memberof Kinetic.Filters
|
if( max == r ){ hue = ((g-b)/chroma) % 6; }else
|
||||||
* @param {Object} imageData
|
if( max == g ){ hue = (b-r)/chroma + 2; }else
|
||||||
* @author ippo615
|
if( max == b ){ hue = (r-g)/chroma + 4; }
|
||||||
*/
|
|
||||||
Kinetic.Filters.Colorize = function(imageData) {
|
hue *= 60;
|
||||||
var data = imageData.data;
|
hue %= 360;
|
||||||
|
|
||||||
// First we'll colorize it red, then shift by the hue specified
|
// Shift hue
|
||||||
var color = this.getFilterColorizeColor(),
|
hue += deg;
|
||||||
hsl = rgb_to_hsl(color[0],color[1],color[2]),
|
hue %= 360;
|
||||||
hue = hsl[0];
|
//hue /= 360;
|
||||||
|
|
||||||
// Color it red, by removing green and blue
|
// hsl to rgb:
|
||||||
for(var i = 0; i < data.length; i += 4) {
|
hue /= 60;
|
||||||
data[i + 1] = 0;
|
var rR = 0, rG = 0, rB = 0,
|
||||||
data[i + 2] = 0;
|
//chroma = saturation*(1 - Math.abs(2*luminance - 1)),
|
||||||
}
|
tmp = chroma * (1-Math.abs(hue % 2 - 1)),
|
||||||
|
m = luminance - chroma/2;
|
||||||
// Shift by the hue
|
|
||||||
shift_hue(imageData,hue);
|
if( 0 <= hue && hue < 1 ){ rR = chroma; rG = tmp; }else
|
||||||
};
|
if( 1 <= hue && hue < 2 ){ rG = chroma; rR = tmp; }else
|
||||||
|
if( 2 <= hue && hue < 3 ){ rG = chroma; rB = tmp; }else
|
||||||
Kinetic.Factory.addFilterGetterSetter(Kinetic.Image, 'filterColorizeColor', [255,0,0] );
|
if( 3 <= hue && hue < 4 ){ rB = chroma; rG = tmp; }else
|
||||||
/**
|
if( 4 <= hue && hue < 5 ){ rB = chroma; rR = tmp; }else
|
||||||
* Gets the colorizing color.
|
if( 5 <= hue && hue < 6 ){ rR = chroma; rB = tmp; }
|
||||||
* @name getFilterColorizeColor
|
|
||||||
* @method
|
rR += m; rG += m; rB += m;
|
||||||
* @memberof Kinetic.Image.prototype
|
rR = (255*rR);
|
||||||
*/
|
rG = (255*rG);
|
||||||
|
rB = (255*rB);
|
||||||
/**
|
|
||||||
* Gets the colorizing color. Should be an array [r,g,b] ie [255,0,128].
|
return [rR,rG,rB];
|
||||||
* note that white [255,255,255] black [0,0,0] and greys [r,r,r] get treated as red.
|
};
|
||||||
* @name setFilterColorizeColor
|
|
||||||
* @method
|
var shift_hue = function(imageData,deg){
|
||||||
* @memberof Kinetic.Image.prototype
|
var data = imageData.data,
|
||||||
*/
|
pixel;
|
||||||
|
for(var i = 0; i < data.length; i += 4) {
|
||||||
})();
|
pixel = pixelShiftHue(data[i+0],data[i+1],data[i+2],deg);
|
||||||
|
data[i+0] = pixel[0];
|
||||||
|
data[i+1] = pixel[1];
|
||||||
|
data[i+2] = pixel[2];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shift Hue Filter.
|
||||||
|
* @function
|
||||||
|
* @memberof Kinetic.Filters
|
||||||
|
* @param {Object} imageData
|
||||||
|
* @author ippo615
|
||||||
|
*/
|
||||||
|
Kinetic.Filters.ShiftHue = function(imageData) {
|
||||||
|
shift_hue(imageData, this.getFilterHueShiftDeg() % 360 );
|
||||||
|
};
|
||||||
|
|
||||||
|
Kinetic.Factory.addFilterGetterSetter(Kinetic.Image, 'filterHueShiftDeg', 0);
|
||||||
|
/**
|
||||||
|
* get hue shift amount. The shift amount is a number between 0 and 360.
|
||||||
|
* @name getFilterBrightness
|
||||||
|
* @method
|
||||||
|
* @memberof Kinetic.Image.prototype
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* set hue shift amount
|
||||||
|
* @name setFilterBrightness
|
||||||
|
* @method
|
||||||
|
* @memberof Kinetic.Image.prototype
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Colorize Filter.
|
||||||
|
* colorizes the image so that it is just varying shades of the specified color
|
||||||
|
* @function
|
||||||
|
* @memberof Kinetic.Filters
|
||||||
|
* @param {Object} imageData
|
||||||
|
* @author ippo615
|
||||||
|
*/
|
||||||
|
Kinetic.Filters.Colorize = function(imageData) {
|
||||||
|
var data = imageData.data;
|
||||||
|
|
||||||
|
// First we'll colorize it red, then shift by the hue specified
|
||||||
|
var color = this.getFilterColorizeColor(),
|
||||||
|
hsl = rgb_to_hsl(color[0],color[1],color[2]),
|
||||||
|
hue = hsl[0];
|
||||||
|
|
||||||
|
// Color it red, by removing green and blue
|
||||||
|
for(var i = 0; i < data.length; i += 4) {
|
||||||
|
data[i + 1] = 0;
|
||||||
|
data[i + 2] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shift by the hue
|
||||||
|
shift_hue(imageData,hue);
|
||||||
|
};
|
||||||
|
|
||||||
|
Kinetic.Factory.addFilterGetterSetter(Kinetic.Image, 'filterColorizeColor', [255,0,0] );
|
||||||
|
/**
|
||||||
|
* Gets the colorizing color.
|
||||||
|
* @name getFilterColorizeColor
|
||||||
|
* @method
|
||||||
|
* @memberof Kinetic.Image.prototype
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the colorizing color. Should be an array [r,g,b] ie [255,0,128].
|
||||||
|
* note that white [255,255,255] black [0,0,0] and greys [r,r,r] get treated as red.
|
||||||
|
* @name setFilterColorizeColor
|
||||||
|
* @method
|
||||||
|
* @memberof Kinetic.Image.prototype
|
||||||
|
*/
|
||||||
|
|
||||||
|
})();
|
||||||
|
|
||||||
|
|||||||
95
src/filters/ColorStretch.js
Normal file
95
src/filters/ColorStretch.js
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
(function () {
|
||||||
|
function remap(fromValue, fromMin, fromMax, toMin, toMax) {
|
||||||
|
|
||||||
|
// Make sure min is less than max (covered outside)
|
||||||
|
/*
|
||||||
|
var swap;
|
||||||
|
if (fromMin > fromMax) {
|
||||||
|
swap = fromMax;
|
||||||
|
fromMin = fromMax;
|
||||||
|
fromMin = swap;
|
||||||
|
}
|
||||||
|
if (toMin > toMax) {
|
||||||
|
swap = toMax;
|
||||||
|
toMin = toMax;
|
||||||
|
toMin = swap;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Compute the range of the data
|
||||||
|
var fromRange = fromMax - fromMin;
|
||||||
|
var toRange = toMax - toMin;
|
||||||
|
|
||||||
|
// If either range is 0, then the value can only be mapped to 1 value
|
||||||
|
if (fromRange === 0) {
|
||||||
|
return toMin + toRange / 2;
|
||||||
|
}
|
||||||
|
if (toRange === 0) {
|
||||||
|
return toMin;
|
||||||
|
}
|
||||||
|
|
||||||
|
// (1) untranslate, (2) unscale, (3) rescale, (4) retranslate
|
||||||
|
var toValue = (fromValue - fromMin) / fromRange;
|
||||||
|
toValue = (toRange * toValue) + toMin;
|
||||||
|
|
||||||
|
return toValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ColorStretch Filter. Adjusts the colors so that they span the widest
|
||||||
|
* possible range (ie 0-255). Performs w*h pixel reads and w*h pixel
|
||||||
|
* writes.
|
||||||
|
* @function
|
||||||
|
* @author ippo615
|
||||||
|
* @memberof Kinetic.Filters
|
||||||
|
* @param {ImageData} src, the source image data (what will be transformed)
|
||||||
|
* @param {ImageData} dst, the destination image data (where it will be saved)
|
||||||
|
* @param {Object} opt, There are no options for this filter
|
||||||
|
*/
|
||||||
|
|
||||||
|
var ColorStretch = function (src, dst, opt) {
|
||||||
|
var srcPixels = src.data,
|
||||||
|
dstPixels = dst.data,
|
||||||
|
nPixels = srcPixels.length,
|
||||||
|
i;
|
||||||
|
|
||||||
|
// 1st Pass - find the min and max for each channel:
|
||||||
|
var rMin = srcPixels[0], rMax = rMin, r,
|
||||||
|
gMin = srcPixels[1], gMax = gMin, g,
|
||||||
|
bMin = srcPixels[3], bMax = bMin, b,
|
||||||
|
aMin = srcPixels[4], aMax = aMin, a;
|
||||||
|
for (i = 0; i < nPixels; i += 4) {
|
||||||
|
r = srcPixels[i + 0];
|
||||||
|
if (r < rMin) { rMin = r; } else
|
||||||
|
if (r > rMax) { rMax = r; }
|
||||||
|
g = srcPixels[i + 1];
|
||||||
|
if (g < gMin) { gMin = g; } else
|
||||||
|
if (g > gMax) { gMax = g; }
|
||||||
|
b = srcPixels[i + 2];
|
||||||
|
if (b < bMin) { bMin = b; } else
|
||||||
|
if (b > bMax) { bMax = b; }
|
||||||
|
a = srcPixels[i + 3];
|
||||||
|
if (a < aMin) { aMin = a; } else
|
||||||
|
if (a > aMax) { aMax = a; }
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there is only 1 level - don't remap
|
||||||
|
if( rMax === rMin ){ rMax = 255; rMin = 0; }
|
||||||
|
if( gMax === gMin ){ gMax = 255; gMin = 0; }
|
||||||
|
if( bMax === bMin ){ bMax = 255; bMin = 0; }
|
||||||
|
if( aMax === aMin ){ aMax = 255; aMin = 0; }
|
||||||
|
|
||||||
|
// Pass 2 - remap everything to fill the full range
|
||||||
|
for (i = 0; i < nPixels; i += 1) {
|
||||||
|
dstPixels[i + 0] = remap(srcPixels[i + 0], rMin, rMax, 0, 255);
|
||||||
|
dstPixels[i + 1] = remap(srcPixels[i + 1], gMin, gMax, 0, 255);
|
||||||
|
dstPixels[i + 2] = remap(srcPixels[i + 2], bMin, bMax, 0, 255);
|
||||||
|
dstPixels[i + 3] = remap(srcPixels[i + 3], aMin, aMax, 0, 255);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
Kinetic.Filters.ColorStretch = Kinetic.Util._FilterWrapSingleBuffer(ColorStretch);
|
||||||
|
|
||||||
|
})();
|
||||||
@@ -1,71 +1,5 @@
|
|||||||
(function() {
|
(function() {
|
||||||
|
|
||||||
var convolve_internal = function(imageData,matrix){
|
|
||||||
// Input data
|
|
||||||
var pixels = imageData.data,
|
|
||||||
imageSizeX = imageData.width,
|
|
||||||
imageSizeY = imageData.height,
|
|
||||||
nPixels = imageSizeX*imageSizeY,
|
|
||||||
pixel;
|
|
||||||
|
|
||||||
// An array for storing the result
|
|
||||||
var result = [];
|
|
||||||
result.length = imageSizeX*imageSizeY*4;
|
|
||||||
|
|
||||||
// Determine the size and demsionality of the matrix
|
|
||||||
// Note: it should be square and odd (3,5,7,9 etc...)
|
|
||||||
var matrixSizeX = matrix.length,
|
|
||||||
matrixSizeY = matrix[0].length,
|
|
||||||
matrixMidX = Math.floor(matrix.length/2),
|
|
||||||
matrixMidY = Math.floor(matrix[0].length/2);
|
|
||||||
|
|
||||||
// Accumlators and positions for iterating
|
|
||||||
var r,g,b,a, x,y, px,py, pos, i,j;
|
|
||||||
|
|
||||||
// Handle the 2D matrix
|
|
||||||
for( y=0; y<imageSizeY; y+=1){
|
|
||||||
for( x=0; x<imageSizeX; x+=1){
|
|
||||||
|
|
||||||
// Perform the convolution
|
|
||||||
r = 0; g = 0; b = 0; a = 0;
|
|
||||||
for( i=0; i<matrixSizeX; i+=1){
|
|
||||||
for( j=0; j<matrixSizeY; j+=1){
|
|
||||||
|
|
||||||
// tile the image to account for pixels past the
|
|
||||||
// edge (and make sure they are positive)
|
|
||||||
px = (x+i-matrixMidX) % imageSizeX;
|
|
||||||
px = (px>0)?px:-px;
|
|
||||||
py = (y+i-matrixMidY) % imageSizeY;
|
|
||||||
py = (py>0)?py:-py;
|
|
||||||
|
|
||||||
// get the pixel and convolve
|
|
||||||
pos = (py*imageSizeX + px)*4;
|
|
||||||
r += matrix[j][i]*pixels[pos+0];
|
|
||||||
g += matrix[j][i]*pixels[pos+1];
|
|
||||||
b += matrix[j][i]*pixels[pos+2];
|
|
||||||
//a += matrix[j][i]*pixels[pos+3];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Store the result
|
|
||||||
pos = (y*imageSizeX+x)*4;
|
|
||||||
result[pos+0] = r;
|
|
||||||
result[pos+1] = g;
|
|
||||||
result[pos+2] = b;
|
|
||||||
//result[pos+3] = a;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// copy the result to the original canvas
|
|
||||||
var lastPos = nPixels*4;
|
|
||||||
for( pos=0; pos<lastPos; pos+=4 ){
|
|
||||||
pixels[pos+0] = result[pos+0];
|
|
||||||
pixels[pos+1] = result[pos+1];
|
|
||||||
pixels[pos+2] = result[pos+2];
|
|
||||||
//pixels[pos+3] = result[pos+3];
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Definition of a gaussian function
|
// Definition of a gaussian function
|
||||||
var gaussian = function(x,mean,sigma){
|
var gaussian = function(x,mean,sigma){
|
||||||
var dx = x - mean;
|
var dx = x - mean;
|
||||||
@@ -156,11 +90,7 @@
|
|||||||
* @param {Object} imageData
|
* @param {Object} imageData
|
||||||
* @author ippo615
|
* @author ippo615
|
||||||
*/
|
*/
|
||||||
Kinetic.Filters.UnsharpMask = function(imageData) {
|
|
||||||
convolve_internal(imageData,
|
|
||||||
make_unsharp_kernel(5,this.getFilterAmount()/100)
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Soft Blur Filter.
|
* Soft Blur Filter.
|
||||||
@@ -169,11 +99,6 @@
|
|||||||
* @param {Object} imageData
|
* @param {Object} imageData
|
||||||
* @author ippo615
|
* @author ippo615
|
||||||
*/
|
*/
|
||||||
Kinetic.Filters.SoftBlur = function(imageData) {
|
|
||||||
convolve_internal(imageData,
|
|
||||||
make_soft_blur_kernel(5,this.getFilterAmount()/100)
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -184,15 +109,6 @@
|
|||||||
* @param {Object} imageData
|
* @param {Object} imageData
|
||||||
* @author ippo615
|
* @author ippo615
|
||||||
*/
|
*/
|
||||||
Kinetic.Filters.Edge = function(imageData) {
|
|
||||||
var s = this.getFilterAmount()/100;
|
|
||||||
if( s === 0 ){ return; }
|
|
||||||
convolve_internal(imageData,[
|
|
||||||
[ 0, -1*s, 0],
|
|
||||||
[-1*s,(1-s)+4*s,-1*s],
|
|
||||||
[ 0, -1*s, 0]
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Emboss Filter.
|
* Emboss Filter.
|
||||||
@@ -202,14 +118,84 @@
|
|||||||
* @param {Object} imageData
|
* @param {Object} imageData
|
||||||
* @author ippo615
|
* @author ippo615
|
||||||
*/
|
*/
|
||||||
Kinetic.Filters.Emboss = function(imageData) {
|
|
||||||
var s = this.getFilterAmount()/100;
|
var convolve = function (src, dst, opt) {
|
||||||
if( s === 0 ){ return; }
|
var xSize = src.width,
|
||||||
convolve_internal(imageData,[
|
ySize = src.height,
|
||||||
[-1*s,-0.5*s, 0],
|
srcPixels = src.data,
|
||||||
[-0.5*s,1+0.5*s, 0.5*s],
|
dstPixels = dst.data;
|
||||||
[ 0, 0.5*s, 1*s]
|
|
||||||
]);
|
// Determine the size and demsionality of the matrix
|
||||||
};
|
// Note: it should be square and odd (3,5,7,9 etc...)
|
||||||
|
var matrix = opt.kernel;
|
||||||
|
var matrixSizeX = matrix.length,
|
||||||
|
matrixSizeY = matrix[0].length,
|
||||||
|
matrixMidX = Math.floor(matrix.length / 2),
|
||||||
|
matrixMidY = Math.floor(matrix[0].length / 2);
|
||||||
|
|
||||||
|
// Accumlators and positions for iterating
|
||||||
|
var r, g, b, a, x, y, px, py, pos, i, j;
|
||||||
|
|
||||||
|
for (y = 0; y < ySize; y += 1) {
|
||||||
|
for (x = 0; x < xSize; x += 1) {
|
||||||
|
|
||||||
|
// Perform the convolution
|
||||||
|
r = 0; g = 0; b = 0; a = 0;
|
||||||
|
for (i = 0; i < matrixSizeX; i += 1) {
|
||||||
|
for (j = 0; j < matrixSizeY; j += 1) {
|
||||||
|
|
||||||
|
// tile the image to account for pixels past the
|
||||||
|
// edge (and make sure they are positive)
|
||||||
|
px = (x + i - matrixMidX) % xSize;
|
||||||
|
px = (px > 0) ? px : -px;
|
||||||
|
py = (y + i - matrixMidY) % ySize;
|
||||||
|
py = (py > 0) ? py : -py;
|
||||||
|
|
||||||
|
// get the pixel and convolve
|
||||||
|
pos = (py * xSize + px) * 4;
|
||||||
|
r += matrix[j][i] * srcPixels[pos + 0];
|
||||||
|
g += matrix[j][i] * srcPixels[pos + 1];
|
||||||
|
b += matrix[j][i] * srcPixels[pos + 2];
|
||||||
|
//a += matrix[j][i]*srcPixels[pos+3];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store the result
|
||||||
|
pos = (y * xSize + x) * 4;
|
||||||
|
dstPixels[pos + 0] = r;
|
||||||
|
dstPixels[pos + 1] = g;
|
||||||
|
dstPixels[pos + 2] = b;
|
||||||
|
dstPixels[pos + 3] = srcPixels[pos + 3];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Kinetic.Filters.Emboss = Kinetic.Util._FilterWrapDoubleBuffer(function(src,dst,opt){
|
||||||
|
var s = this.getFilterAmount()/100;
|
||||||
|
convolve(src,dst,{kernel:[
|
||||||
|
[-1*s, -0.5*s, 0],
|
||||||
|
[-0.5*s,1+0.5*s, 0.5*s],
|
||||||
|
[ 0, 0.5*s, 1*s]
|
||||||
|
]});
|
||||||
|
});
|
||||||
|
|
||||||
|
Kinetic.Filters.Edge = Kinetic.Util._FilterWrapDoubleBuffer(function(src,dst,opt){
|
||||||
|
var s = this.getFilterAmount()/100;
|
||||||
|
convolve(src,dst,{kernel:[
|
||||||
|
[ 0, -1*s, 0],
|
||||||
|
[-1*s,(1-s)+4*s,-1*s],
|
||||||
|
[ 0, -1*s, 0]
|
||||||
|
]});
|
||||||
|
});
|
||||||
|
|
||||||
|
Kinetic.Filters.SoftBlur = Kinetic.Util._FilterWrapDoubleBuffer(function(src,dst,opt){
|
||||||
|
var s = this.getFilterAmount()/100;
|
||||||
|
convolve(src,dst,{kernel:make_soft_blur_kernel(5,s)});
|
||||||
|
});
|
||||||
|
|
||||||
|
Kinetic.Filters.UnsharpMask = Kinetic.Util._FilterWrapDoubleBuffer(function(src,dst,opt){
|
||||||
|
var s = this.getFilterAmount()/100;
|
||||||
|
convolve(src,dst,{kernel:make_unsharp_kernel(5,s)});
|
||||||
|
});
|
||||||
|
|
||||||
})();
|
})();
|
||||||
|
|||||||
50
src/filters/FilterWrapper.js
Normal file
50
src/filters/FilterWrapper.js
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
(function () {
|
||||||
|
Kinetic.Util._FilterWrapDoubleBuffer = function(filter,defaultOpt){
|
||||||
|
return function(src,dst,opt) {
|
||||||
|
// If no dst imageData is provided: make an imitation
|
||||||
|
// blank one, the same size as the src image data
|
||||||
|
var isOnlySrc = ! dst;
|
||||||
|
var data = [],
|
||||||
|
srcData = src.data,
|
||||||
|
l = srcData.length, i;
|
||||||
|
if( isOnlySrc ){
|
||||||
|
dst = {
|
||||||
|
width: src.width,
|
||||||
|
height: src.height
|
||||||
|
};
|
||||||
|
for( i=0; i<l; i+=1 ){
|
||||||
|
data.push(0);
|
||||||
|
}
|
||||||
|
dst.data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
filter.call(this, src, dst, opt || defaultOpt);
|
||||||
|
|
||||||
|
// Copy the dst to the src if this was called the old way
|
||||||
|
if( isOnlySrc ){
|
||||||
|
var dstData = dst.data;
|
||||||
|
for( i=0; i<l; i+=1 ){
|
||||||
|
srcData[i] = dstData[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
Kinetic.Util._FilterWrapSingleBuffer = function(filter,defaultOpt){
|
||||||
|
return function(src,dst,opt) {
|
||||||
|
// If no dst imageData is provided: use the src imageData
|
||||||
|
filter.call(this, src, dst||src, opt || defaultOpt);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
Kinetic.Util._FilterReplaceBuffer = function(src,dst){
|
||||||
|
var i, l = src.length;
|
||||||
|
for( i=0; i<l; ){
|
||||||
|
dst[i] = src[i]; i++;
|
||||||
|
dst[i] = src[i]; i++;
|
||||||
|
dst[i] = src[i]; i++;
|
||||||
|
dst[i] = src[i]; i++;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
})();
|
||||||
65
src/filters/Flip.js
Normal file
65
src/filters/Flip.js
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
(function () {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FlipX Filter. Flips the image horizontally so that the
|
||||||
|
* left-most pixels become the right-most pixels and vice-versa.
|
||||||
|
* Performs w*h pixel reads, 0 computations, and w*h pixel writes.
|
||||||
|
* @function
|
||||||
|
* @author ippo615
|
||||||
|
* @memberof Kinetic.Filters
|
||||||
|
* @param {ImageData} src, the source image data (what will be transformed)
|
||||||
|
* @param {ImageData} dst, the destination image data (where it will be saved)
|
||||||
|
* @param {Object} opt, There are no options for this filter
|
||||||
|
*/
|
||||||
|
|
||||||
|
var FlipX = function (src, dst, opt) {
|
||||||
|
var srcPixels = src.data,
|
||||||
|
dstPixels = dst.data,
|
||||||
|
xSize = src.width,
|
||||||
|
ySize = src.height,
|
||||||
|
i, m, x, y;
|
||||||
|
for (x = 0; x < xSize; x += 1) {
|
||||||
|
for (y = 0; y < ySize; y += 1) {
|
||||||
|
i = (y * xSize + x) * 4; // original
|
||||||
|
m = (y * xSize + (xSize-1) - x) * 4; // flipped
|
||||||
|
dstPixels[m + 0] = srcPixels[i + 0];
|
||||||
|
dstPixels[m + 1] = srcPixels[i + 1];
|
||||||
|
dstPixels[m + 2] = srcPixels[i + 2];
|
||||||
|
dstPixels[m + 3] = srcPixels[i + 3];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FlipY Filter. Flips the image vertically so that the top-most
|
||||||
|
* pixels become the bottom-most pixels and vice-versa.
|
||||||
|
* Performs w*h pixel reads, 0 computations, and w*h pixel writes.
|
||||||
|
* @function
|
||||||
|
* @author ippo615
|
||||||
|
* @memberof Kinetic.Filters
|
||||||
|
* @param {ImageData} src, the source image data (what will be transformed)
|
||||||
|
* @param {ImageData} dst, the destination image data (where it will be saved)
|
||||||
|
* @param {Object} opt, There are no options for this filter
|
||||||
|
*/
|
||||||
|
|
||||||
|
var FlipY = function (src, dst, opt) {
|
||||||
|
var srcPixels = src.data,
|
||||||
|
dstPixels = dst.data,
|
||||||
|
xSize = src.width,
|
||||||
|
ySize = src.height,
|
||||||
|
i, m, x, y;
|
||||||
|
for (x = 0; x < xSize; x += 1) {
|
||||||
|
for (y = 0; y < ySize; y += 1) {
|
||||||
|
i = (y * xSize + x) * 4; // original
|
||||||
|
m = ((ySize-1 - y) * xSize + x) * 4; // flipped
|
||||||
|
dstPixels[m + 0] = srcPixels[i + 0];
|
||||||
|
dstPixels[m + 1] = srcPixels[i + 1];
|
||||||
|
dstPixels[m + 2] = srcPixels[i + 2];
|
||||||
|
dstPixels[m + 3] = srcPixels[i + 3];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Kinetic.Filters.FlipX = Kinetic.Util._FilterWrapSingleBuffer(FlipX);
|
||||||
|
Kinetic.Filters.FlipY = Kinetic.Util._FilterWrapSingleBuffer(FlipY);
|
||||||
|
})();
|
||||||
@@ -1,20 +1,30 @@
|
|||||||
(function() {
|
(function () {
|
||||||
/**
|
|
||||||
* Grayscale Filter
|
/**
|
||||||
* @function
|
* Grayscale Filter. Converts the image to shades of gray.
|
||||||
* @memberof Kinetic.Filters
|
* Performs w*h pixel reads and w*h pixel writes.
|
||||||
* @param {Object} imageData
|
* @function
|
||||||
*/
|
* @author ippo615
|
||||||
Kinetic.Filters.Grayscale = function(imageData) {
|
* @memberof Kinetic.Filters
|
||||||
var data = imageData.data;
|
* @param {ImageData} src, the source image data (what will be transformed)
|
||||||
for(var i = 0; i < data.length; i += 4) {
|
* @param {ImageData} dst, the destination image data (where it will be saved)
|
||||||
var brightness = 0.34 * data[i] + 0.5 * data[i + 1] + 0.16 * data[i + 2];
|
* @param {Object} opt, There are no options for this filter
|
||||||
// red
|
*/
|
||||||
data[i] = brightness;
|
|
||||||
// green
|
var Grayscale = function (src, dst, opt) {
|
||||||
data[i + 1] = brightness;
|
var srcPixels = src.data,
|
||||||
// blue
|
dstPixels = dst.data,
|
||||||
data[i + 2] = brightness;
|
nPixels = srcPixels.length,
|
||||||
}
|
i, brightness;
|
||||||
};
|
for (i = 0; i < nPixels; i += 4) {
|
||||||
})();
|
brightness = 0.34 * srcPixels[i] + 0.5 * srcPixels[i + 1] + 0.16 * srcPixels[i + 2];
|
||||||
|
dstPixels[i] = brightness; // r
|
||||||
|
dstPixels[i + 1] = brightness; // g
|
||||||
|
dstPixels[i + 2] = brightness; // b
|
||||||
|
dstPixels[i + 3] = srcPixels[i + 3]; // alpha
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Kinetic.Filters.Grayscale = Kinetic.Util._FilterWrapSingleBuffer(Grayscale);
|
||||||
|
|
||||||
|
})();
|
||||||
|
|||||||
@@ -1,19 +1,30 @@
|
|||||||
(function() {
|
(function () {
|
||||||
/**
|
|
||||||
* Invert Filter
|
/**
|
||||||
* @function
|
* Invert Filter. Moves all color channels toward the opposite extreme
|
||||||
* @memberof Kinetic.Filters
|
* ie 0 becomes 255, 10 becomes 245 etc... It does not alter the
|
||||||
* @param {Object} imageData
|
* alpha channel.
|
||||||
*/
|
* Performs w*h pixel reads and w*h pixel writes.
|
||||||
Kinetic.Filters.Invert = function(imageData) {
|
* @function
|
||||||
var data = imageData.data;
|
* @author ippo615
|
||||||
for(var i = 0; i < data.length; i += 4) {
|
* @memberof Kinetic.Filters
|
||||||
// red
|
* @param {ImageData} src, the source image data (what will be transformed)
|
||||||
data[i] = 255 - data[i];
|
* @param {ImageData} dst, the destination image data (where it will be saved)
|
||||||
// green
|
* @param {Object} opt, There are no options for this filter
|
||||||
data[i + 1] = 255 - data[i + 1];
|
*/
|
||||||
// blue
|
|
||||||
data[i + 2] = 255 - data[i + 2];
|
var Invert = function (src, dst, opt) {
|
||||||
}
|
var srcPixels = src.data,
|
||||||
};
|
dstPixels = dst.data,
|
||||||
})();
|
nPixels = srcPixels.length,
|
||||||
|
i;
|
||||||
|
for (i = 0; i < nPixels; i += 4) {
|
||||||
|
dstPixels[i+0] = 255 - srcPixels[i+0]; // r
|
||||||
|
dstPixels[i+1] = 255 - srcPixels[i+1]; // g
|
||||||
|
dstPixels[i+2] = 255 - srcPixels[i+2]; // b
|
||||||
|
dstPixels[i+3] = srcPixels[i+3]; // copy alpha
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Kinetic.Filters.Invert = Kinetic.Util._FilterWrapSingleBuffer(Invert);
|
||||||
|
})();
|
||||||
31
src/filters/Levels.js
Normal file
31
src/filters/Levels.js
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
(function () {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Levels Filter. Adjusts the channels so that there are no more
|
||||||
|
* than n different values for that channel. This is also applied
|
||||||
|
* to the alpha channel.
|
||||||
|
* Performs w*h pixel reads and w*h pixel writes.
|
||||||
|
* @function
|
||||||
|
* @author ippo615
|
||||||
|
* @memberof Kinetic.Filters
|
||||||
|
* @param {ImageData} src, the source image data (what will be transformed)
|
||||||
|
* @param {ImageData} dst, the destination image data (where it will be saved)
|
||||||
|
* @param {Object} opt
|
||||||
|
* @param {Number} [opt.quantizationLevels], the number of values allowed for each
|
||||||
|
* channel. Between 2 and 255. Default is 2.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var Levels = function (src, dst, opt) {
|
||||||
|
var nLevels = opt.quantizationLevels || 2;
|
||||||
|
var srcPixels = src.data,
|
||||||
|
dstPixels = dst.data,
|
||||||
|
nPixels = srcPixels.length,
|
||||||
|
scale = (255 / nLevels),
|
||||||
|
i;
|
||||||
|
for (i = 0; i < nPixels; i += 1) {
|
||||||
|
dstPixels[i] = Math.floor(srcPixels[i] / scale) * scale;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Kinetic.Filters.Levels = Kinetic.Util._FilterWrapSingleBuffer(Levels);
|
||||||
|
})();
|
||||||
79
src/filters/Mirror.js
Normal file
79
src/filters/Mirror.js
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
(function () {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MirrorX Filter. Copies and flips the left half of the image
|
||||||
|
* to the right side of the image
|
||||||
|
* Performs w*h pixel reads and w*h pixel writes.
|
||||||
|
* @function
|
||||||
|
* @author ippo615
|
||||||
|
* @memberof Kinetic.Filters
|
||||||
|
* @param {ImageData} src, the source image data (what will be transformed)
|
||||||
|
* @param {ImageData} dst, the destination image data (where it will be saved)
|
||||||
|
* @param {Object} opt, There are no options for this filter
|
||||||
|
*/
|
||||||
|
|
||||||
|
var MirrorX = function (src, dst, opt) {
|
||||||
|
var srcPixels = src.data,
|
||||||
|
dstPixels = dst.data,
|
||||||
|
xSize = src.width,
|
||||||
|
ySize = src.height,
|
||||||
|
xMid = Math.ceil(xSize / 2),
|
||||||
|
i, m, x, y;
|
||||||
|
for (x = 0; x <= xMid; x += 1) {
|
||||||
|
for (y = 0; y < ySize; y += 1) {
|
||||||
|
// copy the original
|
||||||
|
i = (y * xSize + x) * 4;
|
||||||
|
dstPixels[i + 0] = srcPixels[i + 0];
|
||||||
|
dstPixels[i + 1] = srcPixels[i + 1];
|
||||||
|
dstPixels[i + 2] = srcPixels[i + 2];
|
||||||
|
dstPixels[i + 3] = srcPixels[i + 3];
|
||||||
|
// copy the mirrored
|
||||||
|
m = (y * xSize + xSize - x) * 4;
|
||||||
|
dstPixels[m + 0] = srcPixels[i + 0];
|
||||||
|
dstPixels[m + 1] = srcPixels[i + 1];
|
||||||
|
dstPixels[m + 2] = srcPixels[i + 2];
|
||||||
|
dstPixels[m + 3] = srcPixels[i + 3];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MirrorY Filter. Copies and flips the top half of the image
|
||||||
|
* to the bottom of the image
|
||||||
|
* Performs w*h pixel reads and w*h pixel writes.
|
||||||
|
* @function
|
||||||
|
* @author ippo615
|
||||||
|
* @memberof Kinetic.Filters
|
||||||
|
* @param {ImageData} src, the source image data (what will be transformed)
|
||||||
|
* @param {ImageData} dst, the destination image data (where it will be saved)
|
||||||
|
* @param {Object} opt, There are no options for this filter
|
||||||
|
*/
|
||||||
|
var MirrorY = function (src, dst, opt) {
|
||||||
|
var srcPixels = src.data,
|
||||||
|
dstPixels = dst.data,
|
||||||
|
xSize = src.width,
|
||||||
|
ySize = src.height,
|
||||||
|
yMid = Math.ceil(ySize / 2),
|
||||||
|
i, m, x, y;
|
||||||
|
for (x = 0; x < xSize; x += 1) {
|
||||||
|
for (y = 0; y <= yMid; y += 1) {
|
||||||
|
// copy the original
|
||||||
|
i = (y * xSize + x) * 4;
|
||||||
|
dstPixels[i + 0] = srcPixels[i + 0];
|
||||||
|
dstPixels[i + 1] = srcPixels[i + 1];
|
||||||
|
dstPixels[i + 2] = srcPixels[i + 2];
|
||||||
|
dstPixels[i + 3] = srcPixels[i + 3];
|
||||||
|
// copy the mirrored
|
||||||
|
m = ( (ySize-y) * xSize + x) * 4;
|
||||||
|
dstPixels[m + 0] = srcPixels[i + 0];
|
||||||
|
dstPixels[m + 1] = srcPixels[i + 1];
|
||||||
|
dstPixels[m + 2] = srcPixels[i + 2];
|
||||||
|
dstPixels[m + 3] = srcPixels[i + 3];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Kinetic.Filters.MirrorX = Kinetic.Util._FilterWrapSingleBuffer(MirrorX);
|
||||||
|
Kinetic.Filters.MirrorY = Kinetic.Util._FilterWrapSingleBuffer(MirrorY);
|
||||||
|
|
||||||
|
})();
|
||||||
44
src/filters/Noise.js
Normal file
44
src/filters/Noise.js
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
(function () {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Noise Filter. Randomly adds or substracts to the color channels.
|
||||||
|
* Performs w*h pixel reads and w*h pixel writes.
|
||||||
|
* @function
|
||||||
|
* @author ippo615
|
||||||
|
* @memberof Kinetic.Filters
|
||||||
|
* @param {ImageData} src, the source image data (what will be transformed)
|
||||||
|
* @param {ImageData} dst, the destination image data (where it will be saved)
|
||||||
|
* @param {Object} opt
|
||||||
|
* @param {Number} [opt.noiseAmount] The amount of noise to add. Between 0 and 255.
|
||||||
|
* Each channel of each pixel will change by a random amount
|
||||||
|
* between +- amount/2. Default is 32.
|
||||||
|
* @param {Number} [opt.affectAlpha] 1 to add noise to the alpha channel.
|
||||||
|
* Default is 0.
|
||||||
|
*/
|
||||||
|
var Noise = function (src, dst, opt) {
|
||||||
|
var amount = opt.noiseAmount || 32,
|
||||||
|
affectAlpha = opt.affectAlpha || 0;
|
||||||
|
var srcPixels = src.data,
|
||||||
|
dstPixels = dst.data,
|
||||||
|
nPixels = srcPixels.length,
|
||||||
|
half = amount / 2,
|
||||||
|
i;
|
||||||
|
if (affectAlpha) {
|
||||||
|
for (i = 0; i < nPixels; i += 4) {
|
||||||
|
dstPixels[i + 0] = srcPixels[i + 0] + half - 2 * half * Math.random();
|
||||||
|
dstPixels[i + 1] = srcPixels[i + 1] + half - 2 * half * Math.random();
|
||||||
|
dstPixels[i + 2] = srcPixels[i + 2] + half - 2 * half * Math.random();
|
||||||
|
dstPixels[i + 3] = srcPixels[i + 3] + half - 2 * half * Math.random();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (i = 0; i < nPixels; i += 4) {
|
||||||
|
dstPixels[i + 0] = srcPixels[i + 0] + half - 2 * half * Math.random();
|
||||||
|
dstPixels[i + 1] = srcPixels[i + 1] + half - 2 * half * Math.random();
|
||||||
|
dstPixels[i + 2] = srcPixels[i + 2] + half - 2 * half * Math.random();
|
||||||
|
dstPixels[i + 3] = srcPixels[i + 3];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Kinetic.Filters.Noise = Kinetic.Util._FilterWrapSingleBuffer(Noise);
|
||||||
|
})();
|
||||||
90
src/filters/Pixelate.js
Normal file
90
src/filters/Pixelate.js
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
(function () {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pixelate Filter. Averages groups of pixels and redraws
|
||||||
|
* them as larger "pixels".
|
||||||
|
* Performs w*h pixel reads and w*h pixel writes.
|
||||||
|
* @function
|
||||||
|
* @author ippo615
|
||||||
|
* @memberof Kinetic.Filters
|
||||||
|
* @param {ImageData} src, the source image data (what will be transformed)
|
||||||
|
* @param {ImageData} dst, the destination image data (where it will be saved)
|
||||||
|
* @param {Object} opt
|
||||||
|
* @param {Number} [opt.pixelWidth], The width (in pixels) of the
|
||||||
|
* new larger pixels, default is 8.
|
||||||
|
* @param {Number} [opt.pixelHeight], The height (in pixels) of the
|
||||||
|
* new larger pixels, default is 8.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var Pixelate = function (src, dst, opt) {
|
||||||
|
|
||||||
|
var xBinSize = opt.pixelWidth || 8,
|
||||||
|
yBinSize = opt.pixelHeight || 8;
|
||||||
|
|
||||||
|
var xSize = src.width,
|
||||||
|
ySize = src.height,
|
||||||
|
srcPixels = src.data,
|
||||||
|
dstPixels = dst.data,
|
||||||
|
x, y, i;
|
||||||
|
var pixelsPerBin = xBinSize * yBinSize,
|
||||||
|
red, green, blue, alpha,
|
||||||
|
nBinsX = Math.ceil(xSize / xBinSize),
|
||||||
|
nBinsY = Math.ceil(ySize / yBinSize),
|
||||||
|
xBinStart, xBinEnd, yBinStart, yBinEnd,
|
||||||
|
xBin, yBin, pixelsInBin;
|
||||||
|
|
||||||
|
for (xBin = 0; xBin < nBinsX; xBin += 1) {
|
||||||
|
for (yBin = 0; yBin < nBinsY; yBin += 1) {
|
||||||
|
|
||||||
|
// Initialize the color accumlators to 0
|
||||||
|
red = 0;
|
||||||
|
green = 0;
|
||||||
|
blue = 0;
|
||||||
|
alpha = 0;
|
||||||
|
|
||||||
|
// Determine which pixels are included in this bin
|
||||||
|
xBinStart = xBin * xBinSize;
|
||||||
|
xBinEnd = xBinStart + xBinSize;
|
||||||
|
yBinStart = yBin * yBinSize;
|
||||||
|
yBinEnd = yBinStart + yBinSize;
|
||||||
|
|
||||||
|
// Add all of the pixels to this bin!
|
||||||
|
pixelsInBin = 0;
|
||||||
|
for (x = xBinStart; x < xBinEnd; x += 1) {
|
||||||
|
if( x >= xSize ){ continue; }
|
||||||
|
for (y = yBinStart; y < yBinEnd; y += 1) {
|
||||||
|
if( y >= ySize ){ continue; }
|
||||||
|
i = (xSize * y + x) * 4;
|
||||||
|
red += srcPixels[i + 0];
|
||||||
|
green += srcPixels[i + 1];
|
||||||
|
blue += srcPixels[i + 2];
|
||||||
|
alpha += srcPixels[i + 3];
|
||||||
|
pixelsInBin += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure the channels are between 0-255
|
||||||
|
red = red / pixelsInBin;
|
||||||
|
green = green / pixelsInBin;
|
||||||
|
blue = blue / pixelsInBin;
|
||||||
|
alphas = alpha / pixelsInBin;
|
||||||
|
|
||||||
|
// Draw this bin
|
||||||
|
for (x = xBinStart; x < xBinEnd; x += 1) {
|
||||||
|
if( x >= xSize ){ continue; }
|
||||||
|
for (y = yBinStart; y < yBinEnd; y += 1) {
|
||||||
|
if( y >= ySize ){ continue; }
|
||||||
|
i = (xSize * y + x) * 4;
|
||||||
|
dstPixels[i + 0] = red;
|
||||||
|
dstPixels[i + 1] = green;
|
||||||
|
dstPixels[i + 2] = blue;
|
||||||
|
dstPixels[i + 3] = alpha;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
Kinetic.Filters.Pixelate = Kinetic.Util._FilterWrapSingleBuffer(Pixelate);
|
||||||
|
})();
|
||||||
220
src/filters/Polar.js
Normal file
220
src/filters/Polar.js
Normal file
@@ -0,0 +1,220 @@
|
|||||||
|
(function () {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ToPolar Filter. Converts image data to polar coordinates. Performs
|
||||||
|
* w*h*4 pixel reads and w*h pixel writes.
|
||||||
|
* @function
|
||||||
|
* @author ippo615
|
||||||
|
* @memberof Kinetic.Filters
|
||||||
|
* @param {ImageData} src, the source image data (what will be transformed)
|
||||||
|
* @param {ImageData} dst, the destination image data (where it will be saved)
|
||||||
|
* @param {Object} opt
|
||||||
|
* @param {Number} [opt.polarCenterX] horizontal location for the center of the circle,
|
||||||
|
* default is in the middle
|
||||||
|
* @param {Number} [opt.polarCenterY] vertical location for the center of the circle,
|
||||||
|
* default is in the middle
|
||||||
|
*/
|
||||||
|
|
||||||
|
var ToPolar = function(src,dst,opt){
|
||||||
|
|
||||||
|
var srcPixels = src.data,
|
||||||
|
dstPixels = dst.data,
|
||||||
|
xSize = src.width,
|
||||||
|
ySize = src.height,
|
||||||
|
xMid = opt.polarCenterX || xSize/2,
|
||||||
|
yMid = opt.polarCenterY || ySize/2,
|
||||||
|
i, m, x, y, k, tmp, r=0,g=0,b=0,a=0;
|
||||||
|
|
||||||
|
// Find the largest radius
|
||||||
|
var rad, rMax = Math.sqrt( xMid*xMid + yMid*yMid );
|
||||||
|
x = xSize - xMid;
|
||||||
|
y = ySize - yMid;
|
||||||
|
rad = Math.sqrt( x*x + y*y );
|
||||||
|
rMax = (rad > rMax)?rad:rMax;
|
||||||
|
|
||||||
|
// We'll be uisng x as the radius, and y as the angle (theta=t)
|
||||||
|
var rSize = ySize,
|
||||||
|
tSize = xSize,
|
||||||
|
radius, theta;
|
||||||
|
|
||||||
|
// We want to cover all angles (0-360) and we need to convert to
|
||||||
|
// radians (*PI/180)
|
||||||
|
var conversion = 360/tSize*Math.PI/180, sin, cos;
|
||||||
|
|
||||||
|
var x1, x2, x1i, x2i, y1, y2, y1i, y2i, scale;
|
||||||
|
|
||||||
|
for( theta=0; theta<tSize; theta+=1 ){
|
||||||
|
sin = Math.sin(theta*conversion);
|
||||||
|
cos = Math.cos(theta*conversion);
|
||||||
|
for( radius=0; radius<rSize; radius+=1 ){
|
||||||
|
x = xMid+rMax*radius/rSize*cos;
|
||||||
|
y = yMid+rMax*radius/rSize*sin;
|
||||||
|
if( x <= 1 ){ x = 1; }
|
||||||
|
if( x >= xSize-0.5 ){ x = xSize-1; }
|
||||||
|
if( y <= 1 ){ y = 1; }
|
||||||
|
if( y >= ySize-0.5 ){ y = ySize-1; }
|
||||||
|
|
||||||
|
// Interpolate x and y by going +-0.5 around the pixel's central point
|
||||||
|
// this gives us the 4 nearest pixels to our 1x1 non-aligned pixel.
|
||||||
|
// We average the vaules of those pixels based on how much of our
|
||||||
|
// non-aligned pixel overlaps each of them.
|
||||||
|
x1 = x - 0.5;
|
||||||
|
x2 = x + 0.5;
|
||||||
|
x1i = Math.floor(x1);
|
||||||
|
x2i = Math.floor(x2);
|
||||||
|
y1 = y - 0.5;
|
||||||
|
y2 = y + 0.5;
|
||||||
|
y1i = Math.floor(y1);
|
||||||
|
y2i = Math.floor(y2);
|
||||||
|
|
||||||
|
scale = (1-(x1-x1i))*(1-(y1-y1i));
|
||||||
|
i = (y1i*xSize + x1i)*4;
|
||||||
|
r = srcPixels[i+0]*scale;
|
||||||
|
g = srcPixels[i+1]*scale;
|
||||||
|
b = srcPixels[i+2]*scale;
|
||||||
|
a = srcPixels[i+3]*scale;
|
||||||
|
|
||||||
|
scale = (1-(x1-x1i))*(y2-y2i);
|
||||||
|
i = (y2i*xSize + x1i)*4;
|
||||||
|
r += srcPixels[i+0]*scale;
|
||||||
|
g += srcPixels[i+1]*scale;
|
||||||
|
b += srcPixels[i+2]*scale;
|
||||||
|
a += srcPixels[i+3]*scale;
|
||||||
|
|
||||||
|
scale = (x2-x2i)*(y2-y2i);
|
||||||
|
i = (y2i*xSize + x2i)*4;
|
||||||
|
r += srcPixels[i+0]*scale;
|
||||||
|
g += srcPixels[i+1]*scale;
|
||||||
|
b += srcPixels[i+2]*scale;
|
||||||
|
a += srcPixels[i+3]*scale;
|
||||||
|
|
||||||
|
scale = (x2-x2i)*(1-(y1-y1i));
|
||||||
|
i = (y1i*xSize + x2i)*4;
|
||||||
|
r += srcPixels[i+0]*scale;
|
||||||
|
g += srcPixels[i+1]*scale;
|
||||||
|
b += srcPixels[i+2]*scale;
|
||||||
|
a += srcPixels[i+3]*scale;
|
||||||
|
|
||||||
|
// Store it
|
||||||
|
//i = (theta * xSize + radius) * 4;
|
||||||
|
i = (theta + radius*xSize) * 4;
|
||||||
|
dstPixels[i+0] = r;
|
||||||
|
dstPixels[i+1] = g;
|
||||||
|
dstPixels[i+2] = b;
|
||||||
|
dstPixels[i+3] = a;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FromPolar Filter. Converts image data from polar coordinates back to rectangular.
|
||||||
|
* Performs w*h*4 pixel reads and w*h pixel writes.
|
||||||
|
* @function
|
||||||
|
* @author ippo615
|
||||||
|
* @memberof Kinetic.Filters
|
||||||
|
* @param {ImageData} src, the source image data (what will be transformed)
|
||||||
|
* @param {ImageData} dst, the destination image data (where it will be saved)
|
||||||
|
* @param {Object} opt
|
||||||
|
* @param {Number} [opt.polarCenterX] horizontal location for the center of the circle,
|
||||||
|
* default is in the middle
|
||||||
|
* @param {Number} [opt.polarCenterY] vertical location for the center of the circle,
|
||||||
|
* default is in the middle
|
||||||
|
* @param {Number} [opt.polarRotation] amount to rotate the image counterclockwis,
|
||||||
|
* 0 is no rotation, 360 degrees is a full rotation
|
||||||
|
*/
|
||||||
|
|
||||||
|
var FromPolar = function(src,dst,opt){
|
||||||
|
|
||||||
|
var srcPixels = src.data,
|
||||||
|
dstPixels = dst.data,
|
||||||
|
xSize = src.width,
|
||||||
|
ySize = src.height,
|
||||||
|
xMid = opt.polarCenterX || xSize/2,
|
||||||
|
yMid = opt.polarCenterY || ySize/2,
|
||||||
|
i, m, x, y, dx, dy, k, tmp, r=0,g=0,b=0,a=0;
|
||||||
|
|
||||||
|
|
||||||
|
// Find the largest radius
|
||||||
|
var rad, rMax = Math.sqrt( xMid*xMid + yMid*yMid );
|
||||||
|
x = xSize - xMid;
|
||||||
|
y = ySize - yMid;
|
||||||
|
rad = Math.sqrt( x*x + y*y );
|
||||||
|
rMax = (rad > rMax)?rad:rMax;
|
||||||
|
|
||||||
|
// We'll be uisng x as the radius, and y as the angle (theta=t)
|
||||||
|
var rSize = ySize,
|
||||||
|
tSize = xSize,
|
||||||
|
radius, theta,
|
||||||
|
phaseShift = opt.polarRotation || 0;
|
||||||
|
|
||||||
|
// We need to convert to degrees and we need to make sure
|
||||||
|
// it's between (0-360)
|
||||||
|
// var conversion = tSize/360*180/Math.PI;
|
||||||
|
var conversion = tSize/360*180/Math.PI;
|
||||||
|
|
||||||
|
var x1, x2, x1i, x2i, y1, y2, y1i, y2i, scale;
|
||||||
|
|
||||||
|
for( x=0; x<xSize; x+=1 ){
|
||||||
|
for( y=0; y<ySize; y+=1 ){
|
||||||
|
dx = x - xMid;
|
||||||
|
dy = y - yMid;
|
||||||
|
radius = Math.sqrt(dx*dx + dy*dy)*rSize/rMax;
|
||||||
|
theta = (Math.atan2(dy,dx)*180/Math.PI + 360 + phaseShift)%360;
|
||||||
|
theta = theta*tSize/360;
|
||||||
|
|
||||||
|
// Interpolate x and y by going +-0.5 around the pixel's central point
|
||||||
|
// this gives us the 4 nearest pixels to our 1x1 non-aligned pixel.
|
||||||
|
// We average the vaules of those pixels based on how much of our
|
||||||
|
// non-aligned pixel overlaps each of them.
|
||||||
|
x1 = theta - 0.5;
|
||||||
|
x2 = theta + 0.5;
|
||||||
|
x1i = Math.floor(x1);
|
||||||
|
x2i = Math.floor(x2);
|
||||||
|
y1 = radius - 0.5;
|
||||||
|
y2 = radius + 0.5;
|
||||||
|
y1i = Math.floor(y1);
|
||||||
|
y2i = Math.floor(y2);
|
||||||
|
|
||||||
|
scale = (1-(x1-x1i))*(1-(y1-y1i));
|
||||||
|
i = (y1i*xSize + x1i)*4;
|
||||||
|
r = srcPixels[i+0]*scale;
|
||||||
|
g = srcPixels[i+1]*scale;
|
||||||
|
b = srcPixels[i+2]*scale;
|
||||||
|
a = srcPixels[i+3]*scale;
|
||||||
|
|
||||||
|
scale = (1-(x1-x1i))*(y2-y2i);
|
||||||
|
i = (y2i*xSize + x1i)*4;
|
||||||
|
r += srcPixels[i+0]*scale;
|
||||||
|
g += srcPixels[i+1]*scale;
|
||||||
|
b += srcPixels[i+2]*scale;
|
||||||
|
a += srcPixels[i+3]*scale;
|
||||||
|
|
||||||
|
scale = (x2-x2i)*(y2-y2i);
|
||||||
|
i = (y2i*xSize + x2i)*4;
|
||||||
|
r += srcPixels[i+0]*scale;
|
||||||
|
g += srcPixels[i+1]*scale;
|
||||||
|
b += srcPixels[i+2]*scale;
|
||||||
|
a += srcPixels[i+3]*scale;
|
||||||
|
|
||||||
|
scale = (x2-x2i)*(1-(y1-y1i));
|
||||||
|
i = (y1i*xSize + x2i)*4;
|
||||||
|
r += srcPixels[i+0]*scale;
|
||||||
|
g += srcPixels[i+1]*scale;
|
||||||
|
b += srcPixels[i+2]*scale;
|
||||||
|
a += srcPixels[i+3]*scale;
|
||||||
|
|
||||||
|
// Store it
|
||||||
|
i = (y*xSize + x)*4;
|
||||||
|
dstPixels[i+0] = r;
|
||||||
|
dstPixels[i+1] = g;
|
||||||
|
dstPixels[i+2] = b;
|
||||||
|
dstPixels[i+3] = a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
Kinetic.Filters.ToPolar = Kinetic.Util._FilterWrapDoubleBuffer(ToPolar);
|
||||||
|
Kinetic.Filters.FromPolar = Kinetic.Util._FilterWrapDoubleBuffer(FromPolar);
|
||||||
|
})();
|
||||||
34
src/filters/Threshold.js
Normal file
34
src/filters/Threshold.js
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
(function () {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Threshold Filter. Pushes any value above the mid point to
|
||||||
|
* the max and any value below the mid point to the min.
|
||||||
|
* This affects the alpha channel.
|
||||||
|
* Performs w*h pixel reads and w*h pixel writes.
|
||||||
|
* @function
|
||||||
|
* @author ippo615
|
||||||
|
* @memberof Kinetic.Filters
|
||||||
|
* @param {ImageData} src, the source image data (what will be transformed)
|
||||||
|
* @param {ImageData} dst, the destination image data (where it will be saved)
|
||||||
|
* @param {Object} opt
|
||||||
|
* @param {Number} [opt.thresholdLevel=128] the value which divides
|
||||||
|
* the channel value into 2 groups (between 0 and 255)
|
||||||
|
*/
|
||||||
|
|
||||||
|
var Threshold = function (src, dst, opt) {
|
||||||
|
var level = opt.thresholdLevel || 128;
|
||||||
|
var srcPixels = src.data,
|
||||||
|
dstPixels = dst.data,
|
||||||
|
nPixels = srcPixels.length,
|
||||||
|
i;
|
||||||
|
for (i = 0; i < nPixels; i += 1) {
|
||||||
|
if (srcPixels[i] < level) {
|
||||||
|
dstPixels[i] = 0;
|
||||||
|
} else {
|
||||||
|
dstPixels[i] = 255;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Kinetic.Filters.Threshold = Kinetic.Util._FilterWrapSingleBuffer(Threshold);
|
||||||
|
})();
|
||||||
Reference in New Issue
Block a user