mirror of
https://github.com/konvajs/konva.git
synced 2025-06-28 15:23:44 +08:00
Added Gaussian Blur back, put QuickBlur in separate file
This commit is contained in:
parent
21b0bb8574
commit
a680d33c40
@ -39,6 +39,7 @@ module.exports = function(grunt) {
|
|||||||
'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/QuickBlur.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',
|
||||||
|
@ -1,225 +1,375 @@
|
|||||||
(function () {
|
/*
|
||||||
|
the Gauss filter
|
||||||
/**
|
master repo: https://github.com/pavelpower/kineticjsGaussFilter/
|
||||||
* BlurX Filter. Blurs the image in the X direction (horizontally). It
|
*/
|
||||||
* performs w*h pixel reads, and w*h pixel writes.
|
(function() {
|
||||||
* @function
|
/*
|
||||||
* @author ippo615
|
|
||||||
* @memberof Kinetic.Filters
|
StackBlur - a fast almost Gaussian Blur For Canvas
|
||||||
* @param {ImageData} src, the source image data (what will be transformed)
|
|
||||||
* @param {ImageData} dst, the destination image data (where it will be saved)
|
Version: 0.5
|
||||||
* @param {Object} opt
|
Author: Mario Klingemann
|
||||||
* @param {Number} [opt.blurWidth] how many neighboring pixels to will affect the
|
Contact: mario@quasimondo.com
|
||||||
* blurred pixel, default: 5
|
Website: http://www.quasimondo.com/StackBlurForCanvas
|
||||||
*/
|
Twitter: @quasimondo
|
||||||
|
|
||||||
var BlurX = function(src,dst,opt){
|
In case you find this class useful - especially in commercial projects -
|
||||||
|
I am not totally unhappy for a small donation to my PayPal account
|
||||||
var srcPixels = src.data,
|
mario@quasimondo.de
|
||||||
dstPixels = dst.data,
|
|
||||||
xSize = src.width,
|
Or support me on flattr:
|
||||||
ySize = src.height,
|
https://flattr.com/thing/72791/StackBlur-a-fast-almost-Gaussian-Blur-Effect-for-CanvasJavascript
|
||||||
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;
|
|
||||||
// HINT: consider when (opt.blurWidth = 0)
|
Permission is hereby granted, free of charge, to any person
|
||||||
var kSize = 5;
|
obtaining a copy of this software and associated documentation
|
||||||
if( opt.hasOwnProperty('blurWidth') ){
|
files (the "Software"), to deal in the Software without
|
||||||
kSize = Math.round( Math.abs(opt.blurWidth) )+1;
|
restriction, including without limitation the rights to use,
|
||||||
}
|
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
var kMid = Math.floor(kSize/2);
|
copies of the Software, and to permit persons to whom the
|
||||||
|
Software is furnished to do so, subject to the following
|
||||||
//console.info('Blur Width: '+kSize);
|
conditions:
|
||||||
//console.info('Blur Middle: '+kMid);
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
var xEnd = xSize - kMid;
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
for (y = 0; y < ySize; y += 1) {
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
r=0;g=0;b=0;a=0;
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||||
for (x=-kMid; x<kMid; x+=1 ){
|
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
// Add the new
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||||
//if( y === 0 ){ console.info('Loading pixel at: '+((x+xSize)%xSize) ); }
|
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
i = (y * xSize + (x+xSize)%xSize ) * 4;
|
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
r += srcPixels[i+0];
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||||
g += srcPixels[i+1];
|
OTHER DEALINGS IN THE SOFTWARE.
|
||||||
b += srcPixels[i+2];
|
*/
|
||||||
a += srcPixels[i+3];
|
|
||||||
}
|
function BlurStack() {
|
||||||
|
this.r = 0;
|
||||||
for (x = 0; x < xSize; x += 1) {
|
this.g = 0;
|
||||||
//if( y === 0 ){ console.info('Added pixel at: '+(x+kMid)); }
|
this.b = 0;
|
||||||
//if( y === 0 ){ console.info('Recorded pixel at: '+x); }
|
this.a = 0;
|
||||||
//if( y === 0 ){ console.info('Removed pixel at: '+((x-kMid+xSize)%xSize) ); }
|
this.next = null;
|
||||||
// Add the new
|
}
|
||||||
i = (y * xSize + (x+kMid)%xSize ) * 4;
|
|
||||||
r += srcPixels[i+0];
|
var mul_table = [
|
||||||
g += srcPixels[i+1];
|
512,512,456,512,328,456,335,512,405,328,271,456,388,335,292,512,
|
||||||
b += srcPixels[i+2];
|
454,405,364,328,298,271,496,456,420,388,360,335,312,292,273,512,
|
||||||
a += srcPixels[i+3];
|
482,454,428,405,383,364,345,328,312,298,284,271,259,496,475,456,
|
||||||
// Store the result
|
437,420,404,388,374,360,347,335,323,312,302,292,282,273,265,512,
|
||||||
i = (y * xSize + x) * 4;
|
497,482,468,454,441,428,417,405,394,383,373,364,354,345,337,328,
|
||||||
dstPixels[i+0] = r/kSize;
|
320,312,305,298,291,284,278,271,265,259,507,496,485,475,465,456,
|
||||||
dstPixels[i+1] = g/kSize;
|
446,437,428,420,412,404,396,388,381,374,367,360,354,347,341,335,
|
||||||
dstPixels[i+2] = b/kSize;
|
329,323,318,312,307,302,297,292,287,282,278,273,269,265,261,512,
|
||||||
dstPixels[i+3] = a/kSize;
|
505,497,489,482,475,468,461,454,447,441,435,428,422,417,411,405,
|
||||||
// Subtract the old
|
399,394,389,383,378,373,368,364,359,354,350,345,341,337,332,328,
|
||||||
i = (y * xSize + (x-kMid+xSize)%xSize ) * 4;
|
324,320,316,312,309,305,301,298,294,291,287,284,281,278,274,271,
|
||||||
r -= srcPixels[i+0];
|
268,265,262,259,257,507,501,496,491,485,480,475,470,465,460,456,
|
||||||
g -= srcPixels[i+1];
|
451,446,442,437,433,428,424,420,416,412,408,404,400,396,392,388,
|
||||||
b -= srcPixels[i+2];
|
385,381,377,374,370,367,363,360,357,354,350,347,344,341,338,335,
|
||||||
a -= srcPixels[i+3];
|
332,329,326,323,320,318,315,312,310,307,304,302,299,297,294,292,
|
||||||
}
|
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,
|
||||||
* BlurY Filter. Blurs the image in the Y direction (vertically). It
|
21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
|
||||||
* performs w*h pixel reads, and w*h pixel writes.
|
21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22,
|
||||||
* @function
|
22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
|
||||||
* @author ippo615
|
22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23,
|
||||||
* @memberof Kinetic.Filters
|
23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
|
||||||
* @param {ImageData} src, the source image data (what will be transformed)
|
23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
|
||||||
* @param {ImageData} dst, the destination image data (where it will be saved)
|
23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
|
||||||
* @param {Object} opt
|
23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
|
||||||
* @param {Number} [opt.blurHeight] how many neighboring pixels to will affect the
|
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
|
||||||
* blurred pixel, default: 5
|
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,
|
||||||
|
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24 ];
|
||||||
var BlurY = function(src,dst,opt){
|
|
||||||
|
function filterGaussBlurRGBA( imageData, radius) {
|
||||||
var srcPixels = src.data,
|
|
||||||
dstPixels = dst.data,
|
var pixels = imageData.data,
|
||||||
xSize = src.width,
|
width = imageData.width,
|
||||||
ySize = src.height,
|
height = imageData.height;
|
||||||
i, m, x, y, k, tmp, r=0,g=0,b=0,a=0;
|
|
||||||
|
var x, y, i, p, yp, yi, yw, r_sum, g_sum, b_sum, a_sum,
|
||||||
var kSize = 5;
|
r_out_sum, g_out_sum, b_out_sum, a_out_sum,
|
||||||
if( opt.hasOwnProperty('blurHeight') ){
|
r_in_sum, g_in_sum, b_in_sum, a_in_sum,
|
||||||
kSize = Math.round( Math.abs(opt.blurHeight) )+1;
|
pr, pg, pb, pa, rbs;
|
||||||
}
|
|
||||||
var kMid = Math.floor(kSize/2);
|
var div = radius + radius + 1,
|
||||||
|
widthMinus1 = width - 1,
|
||||||
var yEnd = ySize - kMid;
|
heightMinus1 = height - 1,
|
||||||
|
radiusPlus1 = radius + 1,
|
||||||
for (x = 0; x < xSize; x += 1) {
|
sumFactor = radiusPlus1 * ( radiusPlus1 + 1 ) / 2,
|
||||||
r=0;g=0;b=0;a=0;
|
stackStart = new BlurStack(),
|
||||||
for (y=-kMid; y<kMid; y+=1 ){
|
stackEnd = null,
|
||||||
// Add the new
|
stack = stackStart,
|
||||||
i = ((y+ySize)%ySize * xSize + x ) * 4;
|
stackIn = null,
|
||||||
r += srcPixels[i+0];
|
stackOut = null,
|
||||||
g += srcPixels[i+1];
|
mul_sum = mul_table[radius],
|
||||||
b += srcPixels[i+2];
|
shg_sum = shg_table[radius];
|
||||||
a += srcPixels[i+3];
|
|
||||||
}
|
for ( i = 1; i < div; i++ ) {
|
||||||
|
stack = stack.next = new BlurStack();
|
||||||
for (y = 0; y < ySize; y += 1) {
|
if ( i == radiusPlus1 ) stackEnd = stack;
|
||||||
// Add the new
|
}
|
||||||
i = ((y+kMid+ySize)%ySize * xSize + x ) * 4;
|
|
||||||
r += srcPixels[i+0];
|
stack.next = stackStart;
|
||||||
g += srcPixels[i+1];
|
|
||||||
b += srcPixels[i+2];
|
yw = yi = 0;
|
||||||
a += srcPixels[i+3];
|
|
||||||
// Store the result
|
for ( y = 0; y < height; y++ )
|
||||||
i = (y * xSize + x) * 4;
|
{
|
||||||
dstPixels[i+0] = r/kSize;
|
r_in_sum = g_in_sum = b_in_sum = a_in_sum = r_sum = g_sum = b_sum = a_sum = 0;
|
||||||
dstPixels[i+1] = g/kSize;
|
|
||||||
dstPixels[i+2] = b/kSize;
|
r_out_sum = radiusPlus1 * ( pr = pixels[yi] );
|
||||||
dstPixels[i+3] = a/kSize;
|
g_out_sum = radiusPlus1 * ( pg = pixels[yi+1] );
|
||||||
// Subtract the old
|
b_out_sum = radiusPlus1 * ( pb = pixels[yi+2] );
|
||||||
i = ((y-kMid+ySize)%ySize * xSize + x ) * 4;
|
a_out_sum = radiusPlus1 * ( pa = pixels[yi+3] );
|
||||||
r -= srcPixels[i+0];
|
|
||||||
g -= srcPixels[i+1];
|
r_sum += sumFactor * pr;
|
||||||
b -= srcPixels[i+2];
|
g_sum += sumFactor * pg;
|
||||||
a -= srcPixels[i+3];
|
b_sum += sumFactor * pb;
|
||||||
}
|
a_sum += sumFactor * pa;
|
||||||
|
|
||||||
}
|
stack = stackStart;
|
||||||
|
|
||||||
};
|
for( i = 0; i < radiusPlus1; i++ )
|
||||||
|
{
|
||||||
Kinetic.Factory.addFilterGetterSetter(Kinetic.Image, 'blurWidth', 5);
|
stack.r = pr;
|
||||||
Kinetic.Factory.addFilterGetterSetter(Kinetic.Image, 'blurHeight', 5);
|
stack.g = pg;
|
||||||
|
stack.b = pb;
|
||||||
Kinetic.Filters.BlurX = function(src,dst,opt){
|
stack.a = pa;
|
||||||
if( this === Kinetic.Filters ){
|
stack = stack.next;
|
||||||
BlurX(src, dst||src, opt );
|
}
|
||||||
}else{
|
|
||||||
BlurX.call(this, src, dst||src, opt || {
|
for( i = 1; i < radiusPlus1; i++ )
|
||||||
blurWidth: this.getBlurWidth()
|
{
|
||||||
});
|
p = yi + (( widthMinus1 < i ? widthMinus1 : i ) << 2 );
|
||||||
}
|
r_sum += ( stack.r = ( pr = pixels[p])) * ( rbs = radiusPlus1 - i );
|
||||||
};
|
g_sum += ( stack.g = ( pg = pixels[p+1])) * rbs;
|
||||||
Kinetic.Filters.BlurY = function(src,dst,opt){
|
b_sum += ( stack.b = ( pb = pixels[p+2])) * rbs;
|
||||||
if( this === Kinetic.Filters ){
|
a_sum += ( stack.a = ( pa = pixels[p+3])) * rbs;
|
||||||
BlurY(src, dst||src, opt );
|
|
||||||
}else{
|
r_in_sum += pr;
|
||||||
BlurY.call(this, src, dst||src, opt || {
|
g_in_sum += pg;
|
||||||
blurHeight: this.getBlurHeight()
|
b_in_sum += pb;
|
||||||
});
|
a_in_sum += pa;
|
||||||
}
|
|
||||||
};
|
stack = stack.next;
|
||||||
|
}
|
||||||
/**
|
|
||||||
* get filter blur width. Returns the width of a blurred pixel. Must be
|
|
||||||
* an integer greater than 0.
|
stackIn = stackStart;
|
||||||
* @name getBlurWidth
|
stackOut = stackEnd;
|
||||||
* @method
|
for ( x = 0; x < width; x++ )
|
||||||
* @memberof Kinetic.Image.prototype
|
{
|
||||||
*/
|
pixels[yi+3] = pa = (a_sum * mul_sum) >> shg_sum;
|
||||||
|
if ( pa !== 0 )
|
||||||
/**
|
{
|
||||||
* set filter blur width.
|
pa = 255 / pa;
|
||||||
* @name setBlurWidth
|
pixels[yi] = ((r_sum * mul_sum) >> shg_sum) * pa;
|
||||||
* @method
|
pixels[yi+1] = ((g_sum * mul_sum) >> shg_sum) * pa;
|
||||||
* @memberof Kinetic.Image.prototype
|
pixels[yi+2] = ((b_sum * mul_sum) >> shg_sum) * pa;
|
||||||
*/
|
} else {
|
||||||
|
pixels[yi] = pixels[yi+1] = pixels[yi+2] = 0;
|
||||||
/**
|
}
|
||||||
* get filter blur height. Returns the height of a blurred pixel. Must be
|
|
||||||
* an integer greater than 0.
|
r_sum -= r_out_sum;
|
||||||
* @name getBlurHeight
|
g_sum -= g_out_sum;
|
||||||
* @method
|
b_sum -= b_out_sum;
|
||||||
* @memberof Kinetic.Image.prototype
|
a_sum -= a_out_sum;
|
||||||
*/
|
|
||||||
|
r_out_sum -= stackIn.r;
|
||||||
/**
|
g_out_sum -= stackIn.g;
|
||||||
* set filter blur height.
|
b_out_sum -= stackIn.b;
|
||||||
* @name setBlurHeight
|
a_out_sum -= stackIn.a;
|
||||||
* @method
|
|
||||||
* @memberof Kinetic.Image.prototype
|
p = ( yw + ( ( p = x + radius + 1 ) < widthMinus1 ? p : widthMinus1 ) ) << 2;
|
||||||
*/
|
|
||||||
|
r_in_sum += ( stackIn.r = pixels[p]);
|
||||||
Kinetic.Factory.addFilterGetterSetter(Kinetic.Image, 'filterRadius', 5);
|
g_in_sum += ( stackIn.g = pixels[p+1]);
|
||||||
|
b_in_sum += ( stackIn.b = pixels[p+2]);
|
||||||
Kinetic.Filters.Blur = Kinetic.Util._FilterWrapSingleBuffer(function(src,dst,opt){
|
a_in_sum += ( stackIn.a = pixels[p+3]);
|
||||||
if( this === Kinetic.Filters ){
|
|
||||||
BlurX(src, src, opt );
|
r_sum += r_in_sum;
|
||||||
BlurY(src, dst||src, opt );
|
g_sum += g_in_sum;
|
||||||
}else{
|
b_sum += b_in_sum;
|
||||||
opt = opt || {
|
a_sum += a_in_sum;
|
||||||
blurHeight: this.getFilterRadius(),
|
|
||||||
blurWidth: this.getFilterRadius()
|
stackIn = stackIn.next;
|
||||||
};
|
|
||||||
BlurX.call(this, src, src, opt);
|
r_out_sum += ( pr = stackOut.r );
|
||||||
BlurY.call(this, src, dst||src, opt);
|
g_out_sum += ( pg = stackOut.g );
|
||||||
}
|
b_out_sum += ( pb = stackOut.b );
|
||||||
});
|
a_out_sum += ( pa = stackOut.a );
|
||||||
|
|
||||||
/**
|
r_in_sum -= pr;
|
||||||
* get filter radius. Returns the radius of a blurred pixel. The blur
|
g_in_sum -= pg;
|
||||||
* is applied horzontally and vertically.
|
b_in_sum -= pb;
|
||||||
* @name getFilterRadius
|
a_in_sum -= pa;
|
||||||
* @method
|
|
||||||
* @memberof Kinetic.Image.prototype
|
stackOut = stackOut.next;
|
||||||
*/
|
|
||||||
|
yi += 4;
|
||||||
/**
|
}
|
||||||
* set filter radius.
|
yw += width;
|
||||||
* @name setFilterRadius
|
}
|
||||||
* @method
|
|
||||||
* @memberof Kinetic.Image.prototype
|
|
||||||
*/
|
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;
|
||||||
|
r_out_sum = radiusPlus1 * ( pr = pixels[yi]);
|
||||||
|
g_out_sum = radiusPlus1 * ( pg = pixels[yi+1]);
|
||||||
|
b_out_sum = radiusPlus1 * ( pb = pixels[yi+2]);
|
||||||
|
a_out_sum = radiusPlus1 * ( pa = pixels[yi+3]);
|
||||||
|
|
||||||
|
r_sum += sumFactor * pr;
|
||||||
|
g_sum += sumFactor * pg;
|
||||||
|
b_sum += sumFactor * pb;
|
||||||
|
a_sum += sumFactor * pa;
|
||||||
|
|
||||||
|
stack = stackStart;
|
||||||
|
|
||||||
|
for( i = 0; i < radiusPlus1; i++ )
|
||||||
|
{
|
||||||
|
stack.r = pr;
|
||||||
|
stack.g = pg;
|
||||||
|
stack.b = pb;
|
||||||
|
stack.a = pa;
|
||||||
|
stack = stack.next;
|
||||||
|
}
|
||||||
|
|
||||||
|
yp = width;
|
||||||
|
|
||||||
|
for( i = 1; i <= radius; i++ )
|
||||||
|
{
|
||||||
|
yi = ( yp + x ) << 2;
|
||||||
|
|
||||||
|
r_sum += ( stack.r = ( pr = pixels[yi])) * ( rbs = radiusPlus1 - i );
|
||||||
|
g_sum += ( stack.g = ( pg = pixels[yi+1])) * rbs;
|
||||||
|
b_sum += ( stack.b = ( pb = pixels[yi+2])) * rbs;
|
||||||
|
a_sum += ( stack.a = ( pa = pixels[yi+3])) * rbs;
|
||||||
|
|
||||||
|
r_in_sum += pr;
|
||||||
|
g_in_sum += pg;
|
||||||
|
b_in_sum += pb;
|
||||||
|
a_in_sum += pa;
|
||||||
|
|
||||||
|
stack = stack.next;
|
||||||
|
|
||||||
|
if( i < heightMinus1 )
|
||||||
|
{
|
||||||
|
yp += width;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
yi = x;
|
||||||
|
stackIn = stackStart;
|
||||||
|
stackOut = stackEnd;
|
||||||
|
for ( y = 0; y < height; y++ )
|
||||||
|
{
|
||||||
|
p = yi << 2;
|
||||||
|
pixels[p+3] = pa = (a_sum * mul_sum) >> shg_sum;
|
||||||
|
if ( pa > 0 )
|
||||||
|
{
|
||||||
|
pa = 255 / pa;
|
||||||
|
pixels[p] = ((r_sum * mul_sum) >> shg_sum ) * pa;
|
||||||
|
pixels[p+1] = ((g_sum * mul_sum) >> shg_sum ) * pa;
|
||||||
|
pixels[p+2] = ((b_sum * mul_sum) >> shg_sum ) * pa;
|
||||||
|
} else {
|
||||||
|
pixels[p] = pixels[p+1] = pixels[p+2] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
r_sum -= r_out_sum;
|
||||||
|
g_sum -= g_out_sum;
|
||||||
|
b_sum -= b_out_sum;
|
||||||
|
a_sum -= a_out_sum;
|
||||||
|
|
||||||
|
r_out_sum -= stackIn.r;
|
||||||
|
g_out_sum -= stackIn.g;
|
||||||
|
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]));
|
||||||
|
g_sum += ( g_in_sum += ( stackIn.g = pixels[p+1]));
|
||||||
|
b_sum += ( b_in_sum += ( stackIn.b = pixels[p+2]));
|
||||||
|
a_sum += ( a_in_sum += ( stackIn.a = pixels[p+3]));
|
||||||
|
|
||||||
|
stackIn = stackIn.next;
|
||||||
|
|
||||||
|
r_out_sum += ( pr = stackOut.r );
|
||||||
|
g_out_sum += ( pg = stackOut.g );
|
||||||
|
b_out_sum += ( pb = stackOut.b );
|
||||||
|
a_out_sum += ( pa = stackOut.a );
|
||||||
|
|
||||||
|
r_in_sum -= pr;
|
||||||
|
g_in_sum -= pg;
|
||||||
|
b_in_sum -= pb;
|
||||||
|
a_in_sum -= pa;
|
||||||
|
|
||||||
|
stackOut = stackOut.next;
|
||||||
|
|
||||||
|
yi += width;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Blur Filter
|
||||||
|
* @function
|
||||||
|
* @memberof Kinetic.Filters
|
||||||
|
* @param {Object} imageData
|
||||||
|
*/
|
||||||
|
|
||||||
|
var Blur = function(src,dst,opt){
|
||||||
|
var radius = opt.filterRadius | 0;
|
||||||
|
|
||||||
|
// If a different desntination image data is supplied
|
||||||
|
// copy the source and perform the blur on the destination
|
||||||
|
var i;
|
||||||
|
if( dst !== src ){
|
||||||
|
i = src.data.length;
|
||||||
|
while( i-- ){
|
||||||
|
dst.data[i] = src.data[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if( radius > 0 ){
|
||||||
|
filterGaussBlurRGBA(dst,radius);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Kinetic.Filters.Blur = function(src,dst,opt){
|
||||||
|
if( this === Kinetic.Filters ){
|
||||||
|
Blur(src, dst||src, opt );
|
||||||
|
}else{
|
||||||
|
Blur.call(this, src, dst||src, opt || {
|
||||||
|
filterRadius: this.getFilterRadius()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Kinetic.Factory.addFilterGetterSetter(Kinetic.Image, 'filterRadius', 0);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get filter radius. Returns the radius for Gaussian blur filter.
|
||||||
|
* @name getFilterRadius
|
||||||
|
* @method
|
||||||
|
* @memberof Kinetic.Image.prototype
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get filter radius. Set the radius for Gaussian blur filter.
|
||||||
|
* @name setFilterRadius
|
||||||
|
* @method
|
||||||
|
* @memberof Kinetic.Image.prototype
|
||||||
|
*/
|
||||||
|
})();
|
198
src/filters/QuickBlur.js
Normal file
198
src/filters/QuickBlur.js
Normal file
@ -0,0 +1,198 @@
|
|||||||
|
(function () {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* BlurX Filter. Blurs the image in the X direction (horizontally). It
|
||||||
|
* performs w*h pixel reads, and w*h pixel writes. It is faster than a
|
||||||
|
* Gaussian blur but does not look as nice. Use Kinetic.Filters.Blur for
|
||||||
|
* a Gaussian blur.
|
||||||
|
* @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.blurWidth] how many neighboring pixels to will affect the
|
||||||
|
* blurred pixel, default: 5
|
||||||
|
*/
|
||||||
|
|
||||||
|
var BlurX = function(src,dst,opt){
|
||||||
|
|
||||||
|
var srcPixels = src.data,
|
||||||
|
dstPixels = dst.data,
|
||||||
|
xSize = src.width,
|
||||||
|
ySize = src.height,
|
||||||
|
i, m, x, y, k, tmp, r=0,g=0,b=0,a=0;
|
||||||
|
|
||||||
|
// DONT USE: kSize = opt.blurWidth || 5;
|
||||||
|
// HINT: consider when (opt.blurWidth = 0)
|
||||||
|
var kSize = 5;
|
||||||
|
if( opt.hasOwnProperty('blurWidth') ){
|
||||||
|
kSize = Math.round( Math.abs(opt.blurWidth) )+1;
|
||||||
|
}
|
||||||
|
var kMid = Math.floor(kSize/2);
|
||||||
|
|
||||||
|
//console.info('Blur Width: '+kSize);
|
||||||
|
//console.info('Blur Middle: '+kMid);
|
||||||
|
|
||||||
|
var xEnd = xSize - kMid;
|
||||||
|
|
||||||
|
for (y = 0; y < ySize; y += 1) {
|
||||||
|
r=0;g=0;b=0;a=0;
|
||||||
|
for (x=-kMid; x<kMid; x+=1 ){
|
||||||
|
// Add the new
|
||||||
|
//if( y === 0 ){ console.info('Loading pixel at: '+((x+xSize)%xSize) ); }
|
||||||
|
i = (y * xSize + (x+xSize)%xSize ) * 4;
|
||||||
|
r += srcPixels[i+0];
|
||||||
|
g += srcPixels[i+1];
|
||||||
|
b += srcPixels[i+2];
|
||||||
|
a += srcPixels[i+3];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (x = 0; x < xSize; x += 1) {
|
||||||
|
//if( y === 0 ){ console.info('Added pixel at: '+(x+kMid)); }
|
||||||
|
//if( y === 0 ){ console.info('Recorded pixel at: '+x); }
|
||||||
|
//if( y === 0 ){ console.info('Removed pixel at: '+((x-kMid+xSize)%xSize) ); }
|
||||||
|
// Add the new
|
||||||
|
i = (y * xSize + (x+kMid)%xSize ) * 4;
|
||||||
|
r += srcPixels[i+0];
|
||||||
|
g += srcPixels[i+1];
|
||||||
|
b += srcPixels[i+2];
|
||||||
|
a += srcPixels[i+3];
|
||||||
|
// Store the result
|
||||||
|
i = (y * xSize + x) * 4;
|
||||||
|
dstPixels[i+0] = r/kSize;
|
||||||
|
dstPixels[i+1] = g/kSize;
|
||||||
|
dstPixels[i+2] = b/kSize;
|
||||||
|
dstPixels[i+3] = a/kSize;
|
||||||
|
// Subtract the old
|
||||||
|
i = (y * xSize + (x-kMid+xSize)%xSize ) * 4;
|
||||||
|
r -= srcPixels[i+0];
|
||||||
|
g -= srcPixels[i+1];
|
||||||
|
b -= srcPixels[i+2];
|
||||||
|
a -= srcPixels[i+3];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* BlurY Filter. Blurs the image in the Y direction (vertically). It
|
||||||
|
* performs w*h pixel reads, and w*h pixel writes. It is faster than a
|
||||||
|
* Gaussian blur but does not look as nice. Use Kinetic.Filters.Blur for
|
||||||
|
* a Gaussian blur.
|
||||||
|
* @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.blurHeight] how many neighboring pixels to will affect the
|
||||||
|
* blurred pixel, default: 5
|
||||||
|
*/
|
||||||
|
|
||||||
|
var BlurY = function(src,dst,opt){
|
||||||
|
|
||||||
|
var srcPixels = src.data,
|
||||||
|
dstPixels = dst.data,
|
||||||
|
xSize = src.width,
|
||||||
|
ySize = src.height,
|
||||||
|
i, m, x, y, k, tmp, r=0,g=0,b=0,a=0;
|
||||||
|
|
||||||
|
var kSize = 5;
|
||||||
|
if( opt.hasOwnProperty('blurHeight') ){
|
||||||
|
kSize = Math.round( Math.abs(opt.blurHeight) )+1;
|
||||||
|
}
|
||||||
|
var kMid = Math.floor(kSize/2);
|
||||||
|
|
||||||
|
var yEnd = ySize - kMid;
|
||||||
|
|
||||||
|
for (x = 0; x < xSize; x += 1) {
|
||||||
|
r=0;g=0;b=0;a=0;
|
||||||
|
for (y=-kMid; y<kMid; y+=1 ){
|
||||||
|
// Add the new
|
||||||
|
i = ((y+ySize)%ySize * xSize + x ) * 4;
|
||||||
|
r += srcPixels[i+0];
|
||||||
|
g += srcPixels[i+1];
|
||||||
|
b += srcPixels[i+2];
|
||||||
|
a += srcPixels[i+3];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (y = 0; y < ySize; y += 1) {
|
||||||
|
// Add the new
|
||||||
|
i = ((y+kMid+ySize)%ySize * xSize + x ) * 4;
|
||||||
|
r += srcPixels[i+0];
|
||||||
|
g += srcPixels[i+1];
|
||||||
|
b += srcPixels[i+2];
|
||||||
|
a += srcPixels[i+3];
|
||||||
|
// Store the result
|
||||||
|
i = (y * xSize + x) * 4;
|
||||||
|
dstPixels[i+0] = r/kSize;
|
||||||
|
dstPixels[i+1] = g/kSize;
|
||||||
|
dstPixels[i+2] = b/kSize;
|
||||||
|
dstPixels[i+3] = a/kSize;
|
||||||
|
// Subtract the old
|
||||||
|
i = ((y-kMid+ySize)%ySize * xSize + x ) * 4;
|
||||||
|
r -= srcPixels[i+0];
|
||||||
|
g -= srcPixels[i+1];
|
||||||
|
b -= srcPixels[i+2];
|
||||||
|
a -= srcPixels[i+3];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
Kinetic.Factory.addFilterGetterSetter(Kinetic.Image, 'blurWidth', 5);
|
||||||
|
Kinetic.Factory.addFilterGetterSetter(Kinetic.Image, 'blurHeight', 5);
|
||||||
|
|
||||||
|
Kinetic.Filters.BlurX = function(src,dst,opt){
|
||||||
|
if( this === Kinetic.Filters ){
|
||||||
|
BlurX(src, dst||src, opt );
|
||||||
|
}else{
|
||||||
|
BlurX.call(this, src, dst||src, opt || {
|
||||||
|
blurWidth: this.getBlurWidth()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Kinetic.Filters.BlurY = function(src,dst,opt){
|
||||||
|
if( this === Kinetic.Filters ){
|
||||||
|
BlurY(src, dst||src, opt );
|
||||||
|
}else{
|
||||||
|
BlurY.call(this, src, dst||src, opt || {
|
||||||
|
blurHeight: this.getBlurHeight()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get filter blur width. Returns the width of a blurred pixel. Must be
|
||||||
|
* an integer greater than 0.
|
||||||
|
* @name getBlurWidth
|
||||||
|
* @method
|
||||||
|
* @memberof Kinetic.Image.prototype
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* set filter blur width.
|
||||||
|
* @name setBlurWidth
|
||||||
|
* @method
|
||||||
|
* @memberof Kinetic.Image.prototype
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get filter blur height. Returns the height of a blurred pixel. Must be
|
||||||
|
* an integer greater than 0.
|
||||||
|
* @name getBlurHeight
|
||||||
|
* @method
|
||||||
|
* @memberof Kinetic.Image.prototype
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* set filter blur height.
|
||||||
|
* @name setBlurHeight
|
||||||
|
* @method
|
||||||
|
* @memberof Kinetic.Image.prototype
|
||||||
|
*/
|
||||||
|
|
||||||
|
})();
|
@ -76,6 +76,7 @@
|
|||||||
|
|
||||||
<!-- filters -->
|
<!-- filters -->
|
||||||
<script src="unit/filters/Blur-test.js"></script>
|
<script src="unit/filters/Blur-test.js"></script>
|
||||||
|
<script src="unit/filters/QuickBlur-test.js"></script>
|
||||||
<script src="unit/filters/Brighten-test.js"></script>
|
<script src="unit/filters/Brighten-test.js"></script>
|
||||||
<script src="unit/filters/ColorPack-test.js"></script>
|
<script src="unit/filters/ColorPack-test.js"></script>
|
||||||
<script src="unit/filters/Invert-test.js"></script>
|
<script src="unit/filters/Invert-test.js"></script>
|
||||||
|
@ -189,19 +189,17 @@ suite('Blur', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// ======================================================
|
// ======================================================
|
||||||
test('fast blur', function (done) {
|
test('half layer gaussian blur', function (done) {
|
||||||
var stage = addStage();
|
var stage = addStage();
|
||||||
|
|
||||||
var shapesLayer = new Kinetic.Layer();
|
var shapesLayer = new Kinetic.Layer();
|
||||||
|
|
||||||
// The important line!
|
// The important line!
|
||||||
shapesLayer.on('draw', function () {
|
shapesLayer.on('draw', function () {
|
||||||
var BLURAMOUNT = 10;
|
var imageData = this.getContext().getImageData(0,0,this.getCanvas().width/2,this.getCanvas().height);
|
||||||
var imageData = this.getContext().getImageData(0,0,this.getCanvas().width,this.getCanvas().height);
|
|
||||||
var scratchData = this.getContext().createImageData(imageData); // only size copied
|
var scratchData = this.getContext().createImageData(imageData); // only size copied
|
||||||
Kinetic.Filters.BlurX(imageData,scratchData,{blurWidth:BLURAMOUNT});
|
Kinetic.Filters.Blur(imageData,scratchData,{filterRadius:24});
|
||||||
Kinetic.Filters.BlurY(scratchData,imageData,{blurHeight:BLURAMOUNT});
|
this.getContext().putImageData(scratchData,0,0);
|
||||||
this.getContext().putImageData(imageData,0,0);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
var triangle = new Kinetic.RegularPolygon({
|
var triangle = new Kinetic.RegularPolygon({
|
||||||
|
154
test/unit/filters/QuickBlur-test.js
Normal file
154
test/unit/filters/QuickBlur-test.js
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
suite('Quick Blur', function() {
|
||||||
|
|
||||||
|
// ======================================================
|
||||||
|
test('half layer blur', function (done) {
|
||||||
|
var stage = addStage();
|
||||||
|
|
||||||
|
var shapesLayer = new Kinetic.Layer();
|
||||||
|
|
||||||
|
// The important line!
|
||||||
|
shapesLayer.on('draw', function () {
|
||||||
|
var imageData = this.getContext().getImageData(0,0,this.getCanvas().width/2,this.getCanvas().height);
|
||||||
|
var scratchData = this.getContext().createImageData(imageData); // only size copied
|
||||||
|
Kinetic.Filters.BlurX(imageData,scratchData,{blurWidth:24});
|
||||||
|
Kinetic.Filters.BlurY(scratchData,imageData,{blurHeight:24});
|
||||||
|
this.getContext().putImageData(imageData,0,0);
|
||||||
|
});
|
||||||
|
|
||||||
|
var triangle = new Kinetic.RegularPolygon({
|
||||||
|
x: stage.getWidth() / 4,
|
||||||
|
y: stage.getHeight() / 2,
|
||||||
|
sides: 3,
|
||||||
|
radius: 80,
|
||||||
|
fillRadialGradientStartPoint: 0,
|
||||||
|
fillRadialGradientStartRadius: 0,
|
||||||
|
fillRadialGradientEndPoint: 0,
|
||||||
|
fillRadialGradientEndRadius: 70,
|
||||||
|
fillRadialGradientColorStops: [0, '#881111', 0.5, '#888811', 1, '#000088'],
|
||||||
|
stroke: 'black',
|
||||||
|
strokeWidth: 4,
|
||||||
|
draggable: true
|
||||||
|
});
|
||||||
|
|
||||||
|
var circle = new Kinetic.Circle({
|
||||||
|
x: 3 * stage.getWidth() / 4,
|
||||||
|
y: stage.getHeight() / 2,
|
||||||
|
radius: 70,
|
||||||
|
fill: '#880000',
|
||||||
|
stroke: 'black',
|
||||||
|
strokeWidth: 4,
|
||||||
|
draggable: true,
|
||||||
|
id: 'myCircle'
|
||||||
|
});
|
||||||
|
|
||||||
|
for( var i=0; i<10; i+=1 ){
|
||||||
|
for( var j=0; j<10; j+=1 ){
|
||||||
|
var rect = new Kinetic.Rect({
|
||||||
|
x: i/10*stage.getWidth(),
|
||||||
|
y: j/10*stage.getHeight(),
|
||||||
|
width: stage.getWidth()/10,
|
||||||
|
height: stage.getHeight()/10,
|
||||||
|
fill: (i+j)%2===0?'#FF0000':'#FFFF00',
|
||||||
|
stroke: 'black',
|
||||||
|
strokeWidth: 4,
|
||||||
|
draggable: true
|
||||||
|
});
|
||||||
|
shapesLayer.add(rect);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
shapesLayer.add(circle);
|
||||||
|
shapesLayer.add(triangle);
|
||||||
|
|
||||||
|
stage.add(shapesLayer);
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
// ======================================================
|
||||||
|
test('tween x blur', function(done) {
|
||||||
|
var stage = addStage();
|
||||||
|
|
||||||
|
var imageObj = new Image();
|
||||||
|
imageObj.onload = function() {
|
||||||
|
|
||||||
|
var layer = new Kinetic.Layer();
|
||||||
|
darth = new Kinetic.Image({
|
||||||
|
x: 10,
|
||||||
|
y: 10,
|
||||||
|
image: imageObj,
|
||||||
|
draggable: true
|
||||||
|
});
|
||||||
|
|
||||||
|
layer.add(darth);
|
||||||
|
stage.add(layer);
|
||||||
|
|
||||||
|
darth.setFilter(Kinetic.Filters.BlurX);
|
||||||
|
darth.setBlurWidth(100);
|
||||||
|
layer.draw();
|
||||||
|
|
||||||
|
var tween = new Kinetic.Tween({
|
||||||
|
node: darth,
|
||||||
|
duration: 2.0,
|
||||||
|
blurWidth: 0,
|
||||||
|
easing: Kinetic.Easings.EaseInOut
|
||||||
|
});
|
||||||
|
|
||||||
|
darth.on('mouseover', function() {
|
||||||
|
tween.play();
|
||||||
|
});
|
||||||
|
|
||||||
|
darth.on('mouseout', function() {
|
||||||
|
tween.reverse();
|
||||||
|
});
|
||||||
|
|
||||||
|
done();
|
||||||
|
|
||||||
|
};
|
||||||
|
imageObj.src = 'assets/darth-vader.jpg';
|
||||||
|
});
|
||||||
|
|
||||||
|
// ======================================================
|
||||||
|
test('tween y blur', function(done) {
|
||||||
|
var stage = addStage();
|
||||||
|
|
||||||
|
var imageObj = new Image();
|
||||||
|
imageObj.onload = function() {
|
||||||
|
|
||||||
|
var layer = new Kinetic.Layer();
|
||||||
|
darth = new Kinetic.Image({
|
||||||
|
x: 10,
|
||||||
|
y: 10,
|
||||||
|
image: imageObj,
|
||||||
|
draggable: true
|
||||||
|
});
|
||||||
|
|
||||||
|
layer.add(darth);
|
||||||
|
stage.add(layer);
|
||||||
|
|
||||||
|
darth.setFilter(Kinetic.Filters.BlurY);
|
||||||
|
darth.setBlurHeight(100);
|
||||||
|
layer.draw();
|
||||||
|
|
||||||
|
var tween = new Kinetic.Tween({
|
||||||
|
node: darth,
|
||||||
|
duration: 2.0,
|
||||||
|
blurHeight: 0,
|
||||||
|
easing: Kinetic.Easings.EaseInOut
|
||||||
|
});
|
||||||
|
|
||||||
|
darth.on('mouseover', function() {
|
||||||
|
tween.play();
|
||||||
|
});
|
||||||
|
|
||||||
|
darth.on('mouseout', function() {
|
||||||
|
tween.reverse();
|
||||||
|
});
|
||||||
|
|
||||||
|
done();
|
||||||
|
|
||||||
|
};
|
||||||
|
imageObj.src = 'assets/darth-vader.jpg';
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
Loading…
Reference in New Issue
Block a user