diff --git a/Gruntfile.js b/Gruntfile.js index 5b1796d2..52441e53 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -39,7 +39,9 @@ module.exports = function(grunt) { 'src/filters/Brighten.js', 'src/filters/Invert.js', 'src/filters/Blur.js', - 'src/filters/Mask.js' + 'src/filters/Mask.js', + 'src/filters/Colors.js', + 'src/filters/Convolution.js' ]; var unitTestFiles = [ @@ -163,4 +165,4 @@ module.exports = function(grunt) { grunt.registerTask('full', ['clean', 'concat:source', 'replace:dev', 'uglify', 'replace:prod']); grunt.registerTask('test', ['concat:test']); grunt.registerTask('hint', ['clean', 'concat:source', 'replace:dev', 'jshint']); -}; \ No newline at end of file +}; diff --git a/src/Container.js b/src/Container.js index 732fb35e..e61af59d 100644 --- a/src/Container.js +++ b/src/Container.js @@ -30,7 +30,7 @@ child; while(children.length > 0) { - var child = children[0]; + child = children[0]; if (child.hasChildren()) { child.removeChildren(); } diff --git a/src/filters/Blur.js b/src/filters/Blur.js index 1a5fcf89..a68ba61d 100644 --- a/src/filters/Blur.js +++ b/src/filters/Blur.js @@ -105,6 +105,7 @@ radiusPlus1 = radius + 1, sumFactor = radiusPlus1 * ( radiusPlus1 + 1 ) / 2, stackStart = new BlurStack(), + stackEnd = null, stack = stackStart, stackIn = null, stackOut = null, @@ -113,7 +114,7 @@ for ( i = 1; i < div; i++ ) { stack = stack.next = new BlurStack(); - if ( i == radiusPlus1 ) var stackEnd = stack; + if ( i == radiusPlus1 ) stackEnd = stack; } stack.next = stackStart; @@ -167,7 +168,7 @@ for ( x = 0; x < width; x++ ) { pixels[yi+3] = pa = (a_sum * mul_sum) >> shg_sum; - if ( pa != 0 ) + if ( pa !== 0 ) { pa = 255 / pa; pixels[yi] = ((r_sum * mul_sum) >> shg_sum) * pa; diff --git a/src/filters/Colors.js b/src/filters/Colors.js new file mode 100644 index 00000000..cec8992b --- /dev/null +++ b/src/filters/Colors.js @@ -0,0 +1,148 @@ +(function() { + + 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; + + // http://en.wikipedia.org/wiki/HSL_and_HSV + // Convert to HSL + var max = Math.max(r,g,b), + min = Math.min(r,g,b), + chroma = max - min, + luminance = chroma / 2, + saturation = chroma / (1 - Math.abs(2*luminance-1)), + hue = 0; + + if( max == r ){ hue = ((g-b)/chroma) % 6; }else + if( max == g ){ hue = (b-r)/chroma + 2; }else + if( max == b ){ hue = (r-g)/chroma + 4; } + + return [(hue*60+360) % 360, saturation, luminance]; + }; + + var pixelShiftHue = function(r,g,b,deg){ + + // Input colors are in 0-255, calculations are between 0-1 + r /= 255; g /= 255; b /= 255; + + // http://en.wikipedia.org/wiki/HSL_and_HSV + // Convert to HSL + var max = Math.max(r,g,b), + min = Math.min(r,g,b), + chroma = max - min, + luminance = chroma / 2, + saturation = chroma / (1 - Math.abs(2*luminance-1)), + hue = 0; + + if( max == r ){ hue = ((g-b)/chroma) % 6; }else + if( max == g ){ hue = (b-r)/chroma + 2; }else + if( max == b ){ hue = (r-g)/chroma + 4; } + + hue *= 60; + hue %= 360; + + // Shift hue + hue += deg; + hue %= 360; + //hue /= 360; + + // hsl to rgb: + hue /= 60; + var rR = 0, rG = 0, rB = 0, + //chroma = saturation*(1 - Math.abs(2*luminance - 1)), + tmp = chroma * (1-Math.abs(hue % 2 - 1)), + m = luminance - chroma/2; + + 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 + if( 3 <= hue && hue < 4 ){ rB = chroma; rG = tmp; }else + if( 4 <= hue && hue < 5 ){ rB = chroma; rR = tmp; }else + if( 5 <= hue && hue < 6 ){ rR = chroma; rB = tmp; } + + rR += m; rG += m; rB += m; + rR = (255*rR); + rG = (255*rG); + rB = (255*rB); + + return [rR,rG,rB]; + }; + + var shift_hue = function(imageData,deg){ + 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 + * @function + * @memberof Kinetic.Filters + * @param {Object} imageData + */ + Kinetic.Filters.ShiftHue = function(imageData) { + shift_hue(imageData,this.getFilterHueShiftDeg()); + }; + + Kinetic.Node.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 + */ + + + /** + * colorizes the image so that it is just varying shades of the specified color + * @function + * @memberof Kinetic.Filters + * @param {Object} imageData + */ + 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 + 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.Node.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 + */ + +})(); diff --git a/src/filters/Convolution.js b/src/filters/Convolution.js new file mode 100644 index 00000000..92af8566 --- /dev/null +++ b/src/filters/Convolution.js @@ -0,0 +1,393 @@ +(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 is2D = (matrix[0].length > 0) || 0, + matrixSizeX = matrix.length, + matrixSizeY = matrix.length; + + // Make sure we don't try to access pixels outside the image + var xMax = Math.floor(imageSizeX - matrixSizeX/2), + xMin = Math.floor(matrixSizeX/2), + yMax = Math.floor(imageSizeY - matrixSizeY/2), + yMin = Math.floor(matrixSizeY/2); + + // Accumlators and positions for iterating + var r,g,b,a, x, y, pos, i,j; + + // Handle the 2D matrix + if( is2D ){ + for( y=yMin; y