first pass at integrating filters into new caching mechanism. added group filter test. Removed blurX and blurY filters because I don't think they'll be needed in their current state. Commented out half filter blur test because it's not a common use case. other filter tests have been disabled for now. Working on enabling them with future commits

This commit is contained in:
Eric Rowell
2013-12-31 13:04:05 -08:00
parent 1166154c1b
commit 12e7b06978
19 changed files with 331 additions and 619 deletions

View File

@@ -1,12 +1,33 @@
module.exports = function(grunt) { module.exports = function(grunt) {
var sourceFiles = [ var sourceFiles = [
// core / anim + tween + dd // core
'src/Global.js', 'src/Global.js',
'src/Util.js', 'src/Util.js',
'src/Canvas.js', 'src/Canvas.js',
'src/Context.js', 'src/Context.js',
'src/Factory.js', 'src/Factory.js',
'src/Node.js', 'src/Node.js',
// filters
'src/filters/FilterWrapper.js',
'src/filters/Grayscale.js',
'src/filters/Brighten.js',
'src/filters/Invert.js',
'src/filters/Blur.js',
'src/filters/Mask.js',
'src/filters/ColorPack.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',
'src/filters/Sepia.js',
// core
'src/Animation.js', 'src/Animation.js',
'src/Tween.js', 'src/Tween.js',
'src/DragAndDrop.js', 'src/DragAndDrop.js',
@@ -33,27 +54,7 @@ module.exports = function(grunt) {
'src/plugins/TextPath.js', 'src/plugins/TextPath.js',
'src/plugins/RegularPolygon.js', 'src/plugins/RegularPolygon.js',
'src/plugins/Star.js', 'src/plugins/Star.js',
'src/plugins/Label.js', 'src/plugins/Label.js'
// filters
'src/filters/FilterWrapper.js',
'src/filters/Grayscale.js',
'src/filters/Brighten.js',
'src/filters/Invert.js',
'src/filters/Blur.js',
'src/filters/QuickBlur.js',
'src/filters/Mask.js',
'src/filters/ColorPack.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',
'src/filters/Sepia.js'
]; ];
// Project configuration. // Project configuration.

View File

@@ -241,26 +241,41 @@
drawScene: function(can) { drawScene: function(can) {
var layer = this.getLayer(), var layer = this.getLayer(),
canvas = can || (layer && layer.getCanvas()), canvas = can || (layer && layer.getCanvas()),
context = canvas && canvas.getContext(),
cachedCanvas = this._cache.canvas, cachedCanvas = this._cache.canvas,
cachedSceneCanvas = cachedCanvas && cachedCanvas.scene; cachedSceneCanvas = cachedCanvas && cachedCanvas.scene;
if (this.isVisible()) { if (this.isVisible()) {
this._draw(canvas, cachedSceneCanvas, 'drawScene'); if (cachedSceneCanvas) {
this._drawCachedSceneCanvas(context);
}
else {
this._drawChildren(canvas, 'drawScene');
}
} }
return this; return this;
}, },
drawHit: function(can) { drawHit: function(can) {
var layer = this.getLayer(), var layer = this.getLayer(),
canvas = can || (layer && layer.hitCanvas), canvas = can || (layer && layer.hitCanvas),
context = canvas && canvas.getContext(),
cachedCanvas = this._cache.canvas, cachedCanvas = this._cache.canvas,
cachedHitCanvas = cachedCanvas && cachedCanvas.hit; cachedHitCanvas = cachedCanvas && cachedCanvas.hit;
if (this.shouldDrawHit()) { if (this.shouldDrawHit()) {
this._draw(canvas, cachedHitCanvas, 'drawHit'); if (cachedHitCanvas) {
context.save();
context._applyTransform(this);
context.drawImage(cachedHitCanvas._canvas, 0, 0);
context.restore();
}
else {
this._drawChildren(canvas, 'drawHit');
}
} }
return this; return this;
}, },
_draw: function(canvas, cachedCanvas, drawMethod) { _drawChildren: function(canvas, drawMethod) {
var context = canvas && canvas.getContext(), var context = canvas && canvas.getContext(),
clipWidth = this.getClipWidth(), clipWidth = this.getClipWidth(),
clipHeight = this.getClipHeight(), clipHeight = this.getClipHeight(),
@@ -279,17 +294,9 @@
context.reset(); context.reset();
} }
if (cachedCanvas) { this.children.each(function(child) {
context.save(); child[drawMethod](canvas);
context._applyTransform(this); });
context.drawImage(cachedCanvas._canvas, 0, 0);
context.restore();
}
else {
this.children.each(function(child) {
child[drawMethod](canvas);
});
}
if (hasClip) { if (hasClip) {
context.restore(); context.restore();

View File

@@ -111,11 +111,17 @@
this.addColorComponentSetter(constructor, attr, G); this.addColorComponentSetter(constructor, attr, G);
this.addColorComponentSetter(constructor, attr, B); this.addColorComponentSetter(constructor, attr, B);
// overloaders
this.addOverloadedGetterSetter(constructor, attr + RGB); this.addOverloadedGetterSetter(constructor, attr + RGB);
this.addOverloadedGetterSetter(constructor, attr + UPPER_R); this.addOverloadedGetterSetter(constructor, attr + UPPER_R);
this.addOverloadedGetterSetter(constructor, attr + UPPER_G); this.addOverloadedGetterSetter(constructor, attr + UPPER_G);
this.addOverloadedGetterSetter(constructor, attr + UPPER_B); this.addOverloadedGetterSetter(constructor, attr + UPPER_B);
}, },
addFilterGetterSetter: function(constructor, attr, def) {
this.addGetter(constructor, attr, def);
this.addFilterSetter(constructor, attr);
this.addOverloadedGetterSetter(constructor, attr);
},
// getter adders // getter adders
addColorRGBGetter: function(constructor, attr) { addColorRGBGetter: function(constructor, attr) {
@@ -237,6 +243,15 @@
return this; return this;
}; };
}, },
addFilterSetter: function(constructor, attr) {
var method = SET + Kinetic.Util._capitalize(attr);
constructor.prototype[method] = function(val) {
this._setAttr(attr, val);
this._filterUpToDate = false;
return this;
};
},
addPointSetter: function(constructor, attr) { addPointSetter: function(constructor, attr) {
var that = this, var that = this,
baseMethod = SET + Kinetic.Util._capitalize(attr); baseMethod = SET + Kinetic.Util._capitalize(attr);

View File

@@ -43,6 +43,7 @@
this.eventListeners = {}; this.eventListeners = {};
this.attrs = {}; this.attrs = {};
this._cache = {}; this._cache = {};
this._filterUpToDate = false;
this.setAttrs(config); this.setAttrs(config);
// event bindings for cache handling // event bindings for cache handling
@@ -112,18 +113,25 @@
* @example * @example
* node.cache(); * node.cache();
*/ */
cache: function(box) { cache: function(b) {
var x = box.x || 0, var box = b || {},
x = box.x || 0,
y = box.y || 0, y = box.y || 0,
width = box.width || this.width(),
height = box.height || this.height(),
sceneCanvasCache = new Kinetic.SceneCanvas({ sceneCanvasCache = new Kinetic.SceneCanvas({
pixelRatio: 1, pixelRatio: 1,
width: box.width, width: width,
height: box.height height: height
}),
filterCanvasCache = new Kinetic.SceneCanvas({
pixelRatio: 1,
width: width,
height: height
}), }),
hitCanvasCache = new Kinetic.HitCanvas({ hitCanvasCache = new Kinetic.HitCanvas({
pixelRatio: 1, width: width,
width: box.width, height: height
height: box.height
}), }),
origTransEnabled = this.transformsEnabled(), origTransEnabled = this.transformsEnabled(),
origX = this.x(), origX = this.x(),
@@ -142,6 +150,7 @@
this._cache.canvas = { this._cache.canvas = {
scene: sceneCanvasCache, scene: sceneCanvasCache,
filter: filterCanvasCache,
hit: hitCanvasCache, hit: hitCanvasCache,
x: x, x: x,
y: y y: y
@@ -149,6 +158,48 @@
return this; return this;
}, },
_drawCachedSceneCanvas: function(context) {
var filters = this.filters(),
cachedCanvas = this._cache.canvas,
sceneCanvas = cachedCanvas.scene,
filterCanvas = cachedCanvas.filter,
filterContext = filterCanvas.getContext(),
len, imageData, n, filter;
context.save();
context._applyTransform(this);
if (filters) {
if (!this._filterUpToDate) {
try {
len = filters.length;
filterContext.clear();
// copy cached canvas onto filter context
filterContext.drawImage(sceneCanvas._canvas, 0, 0);
imageData = filterContext.getImageData(0, 0, filterCanvas.getWidth(), filterCanvas.getHeight());
// apply filters to filter context
for (n=0; n<len; n++) {
filter = filters[n];
filter.call(this, imageData);
filterContext.putImageData(imageData, 0, 0);
}
}
catch(e) {
Kinetic.Util.warn('Unable to apply filter. ' + e.message);
}
this._filterUpToDate = true;
}
context.drawImage(filterCanvas._canvas, 0, 0);
}
else {
context.drawImage(sceneCanvas._canvas, 0, 0);
}
context.restore();
},
/* /*
* the default isDraggable method returns false. * the default isDraggable method returns false.
* if the DragAndDrop module is included, this is overwritten * if the DragAndDrop module is included, this is overwritten
@@ -1753,6 +1804,23 @@
* @returns {Boolean|String} * @returns {Boolean|String}
*/ */
Kinetic.Factory.addGetterSetter(Kinetic.Node, 'filters');
/**
* get/set filters
* @name filters
* @method
* @memberof Kinetic.Node.prototype
* @param {Array} filters array of filters
* @returns {Array}
* @example
* // set a single filter<br>
* node.filters([Kinetic.Filters.Blur]);<br><br>
*
* // get filters<br>
* var filters = node.filters();
*/
Kinetic.Factory.addGetterSetter(Kinetic.Node, 'visible', 'inherit'); Kinetic.Factory.addGetterSetter(Kinetic.Node, 'visible', 'inherit');
/** /**

View File

@@ -236,19 +236,16 @@
var canvas = can || this.getLayer().getCanvas(), var canvas = can || this.getLayer().getCanvas(),
context = canvas.getContext(), context = canvas.getContext(),
cachedCanvas = this._cache.canvas, cachedCanvas = this._cache.canvas,
cachedSceneCanvas = cachedCanvas && cachedCanvas.scene,
drawFunc = this.getDrawFunc(), drawFunc = this.getDrawFunc(),
hasShadow = this.hasShadow(), hasShadow = this.hasShadow(),
stage, bufferCanvas, bufferContext; stage, bufferCanvas, bufferContext;
if(this.isVisible()) { if(this.isVisible()) {
context.save(); if (cachedCanvas) {
this._drawCachedSceneCanvas(context);
if (cachedSceneCanvas) {
context._applyTransform(this);
context.drawImage(cachedSceneCanvas._canvas, 0, 0);
} }
else if (drawFunc) { else if (drawFunc) {
context.save();
// if buffer canvas is needed // if buffer canvas is needed
if (this._useBufferCanvas()) { if (this._useBufferCanvas()) {
stage = this.getStage(); stage = this.getStage();
@@ -286,10 +283,9 @@
context._applyOpacity(this); context._applyOpacity(this);
drawFunc.call(this, context); drawFunc.call(this, context);
} }
context.restore();
} }
context.restore();
} }
return this; return this;
@@ -302,18 +298,22 @@
cachedHitCanvas = cachedCanvas && cachedCanvas.hit; cachedHitCanvas = cachedCanvas && cachedCanvas.hit;
if(this.shouldDrawHit()) { if(this.shouldDrawHit()) {
context.save();
if (cachedHitCanvas) { if (cachedHitCanvas) {
context.save();
context._applyTransform(this); context._applyTransform(this);
context.drawImage(cachedHitCanvas._canvas, 0, 0); context.drawImage(cachedHitCanvas._canvas, 0, 0);
context.restore();
} }
else if (drawFunc) { else if (drawFunc) {
context.save();
context._applyLineJoin(this); context._applyLineJoin(this);
context._applyTransform(this); context._applyTransform(this);
drawFunc.call(this, context); drawFunc.call(this, context);
context.restore();
} }
context.restore();
} }
return this; return this;

View File

@@ -352,24 +352,19 @@
Blur(src, dst||src, opt ); Blur(src, dst||src, opt );
}else{ }else{
Blur.call(this, src, dst||src, opt || { Blur.call(this, src, dst||src, opt || {
filterRadius: this.getFilterRadius() filterRadius: this.blurRadius()
}); });
} }
}; };
Kinetic.Factory.addFilterGetterSetter(Kinetic.Image, 'filterRadius', 0); Kinetic.Factory.addFilterGetterSetter(Kinetic.Node, 'blurRadius', 0);
/** /**
* get filter radius. Returns the radius for Gaussian blur filter. * get/set blur radius
* @name getFilterRadius * @name blurRadius
* @method
* @memberof Kinetic.Image.prototype
*/
/**
* get filter radius. Set the radius for Gaussian blur filter.
* @name setFilterRadius
* @method * @method
* @memberof Kinetic.Image.prototype * @memberof Kinetic.Image.prototype
* @param {Integer} radius
* @returns {Integer}
*/ */
})(); })();

View File

@@ -18,7 +18,7 @@
} }
}; };
Kinetic.Factory.addFilterGetterSetter(Kinetic.Image, 'filterBrightness', 0); Kinetic.Factory.addFilterGetterSetter(Kinetic.Node, 'brightness', 0);
/** /**
* get filter brightness. The brightness is a number between -255 and 255.&nbsp; Positive values * get filter brightness. The brightness is a number between -255 and 255.&nbsp; Positive values
* increase the brightness and negative values decrease the brightness, making the image darker * increase the brightness and negative values decrease the brightness, making the image darker

View File

@@ -70,9 +70,9 @@
}; };
Kinetic.Factory.addFilterGetterSetter(Kinetic.Image, 'filterHue', 0); Kinetic.Factory.addFilterGetterSetter(Kinetic.Node, 'hue', 0);
Kinetic.Factory.addFilterGetterSetter(Kinetic.Image, 'filterSaturation', 1); Kinetic.Factory.addFilterGetterSetter(Kinetic.Node, 'saturation', 1);
Kinetic.Factory.addFilterGetterSetter(Kinetic.Image, 'filterValue', 1); Kinetic.Factory.addFilterGetterSetter(Kinetic.Node, 'value', 1);
Kinetic.Filters.HSV = function(src,dst,opt){ Kinetic.Filters.HSV = function(src,dst,opt){
if( this === Kinetic.Filters ){ if( this === Kinetic.Filters ){
@@ -97,7 +97,7 @@
} }
}; };
Kinetic.Factory.addFilterGetterSetter(Kinetic.Image, 'filterHueShiftDeg', 0); Kinetic.Factory.addFilterGetterSetter(Kinetic.Node, 'hueShiftDeg', 0);
/** /**
* get filter hue. Returns the hue shift for the HSV filter. * get filter hue. Returns the hue shift for the HSV filter.
@@ -184,7 +184,7 @@
} }
}; };
Kinetic.Factory.addFilterGetterSetter(Kinetic.Image, 'filterColorizeColor', [255,0,0] ); Kinetic.Factory.addFilterGetterSetter(Kinetic.Node, 'colorizeColor', [255,0,0] );
/** /**
* Gets the colorizing color. * Gets the colorizing color.
* @name getFilterColorizeColor * @name getFilterColorizeColor

View File

@@ -68,7 +68,7 @@
return kernel; return kernel;
}; };
Kinetic.Factory.addFilterGetterSetter(Kinetic.Image, 'filterAmount', 50); Kinetic.Factory.addFilterGetterSetter(Kinetic.Node, 'convoltion', 50);
/** /**
* get the current filter amount * get the current filter amount
* @name getFilterAmount * @name getFilterAmount

View File

@@ -37,7 +37,7 @@
} }
}; };
Kinetic.Factory.addFilterGetterSetter(Kinetic.Image, 'quantizationLevels', 4); Kinetic.Factory.addFilterGetterSetter(Kinetic.Node, 'quantizationLevels', 4);
/** /**
* get quantization levels. Returns the number of unique levels for each color * get quantization levels. Returns the number of unique levels for each color

View File

@@ -191,7 +191,7 @@
return idata; return idata;
}; };
Kinetic.Factory.addFilterGetterSetter(Kinetic.Image, 'filterThreshold', 0); Kinetic.Factory.addFilterGetterSetter(Kinetic.Node, 'threshold', 0);
//threshold The RGB euclidian distance threshold (default : 10) //threshold The RGB euclidian distance threshold (default : 10)

View File

@@ -40,7 +40,7 @@
} }
}; };
Kinetic.Factory.addFilterGetterSetter(Kinetic.Image, 'noiseAmount', 32); Kinetic.Factory.addFilterGetterSetter(Kinetic.Node, 'noise', 32);
Kinetic.Filters.Noise = function(src,dst,opt){ Kinetic.Filters.Noise = function(src,dst,opt){
if( this === Kinetic.Filters ){ if( this === Kinetic.Filters ){

View File

@@ -86,16 +86,15 @@
}; };
Kinetic.Factory.addFilterGetterSetter(Kinetic.Image, 'pixelWidth', 8); Kinetic.Factory.addFilterGetterSetter(Kinetic.Node, 'pixelationSize', 8);
Kinetic.Factory.addFilterGetterSetter(Kinetic.Image, 'pixelHeight', 8);
Kinetic.Filters.Pixelate = function(src,dst,opt){ Kinetic.Filters.Pixelate = function(src,dst,opt){
if( this === Kinetic.Filters ){ if( this === Kinetic.Filters ){
Pixelate(src, dst||src, opt ); Pixelate(src, dst||src, opt );
}else{ }else{
Pixelate.call(this, src, dst||src, opt || { Pixelate.call(this, src, dst||src, opt || {
pixelWidth: this.getPixelWidth(), pixelWidth: this.pixelationSize(),
pixelHeight: this.getPixelHeight() pixelHeight: this.pixelationSize()
}); });
} }
}; };

View File

@@ -1,198 +0,0 @@
(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
*/
})();

View File

@@ -33,7 +33,7 @@
} }
}; };
Kinetic.Factory.addFilterGetterSetter(Kinetic.Image, 'thresholdLevel', 128); Kinetic.Factory.addFilterGetterSetter(Kinetic.Node, 'threshold', 128);
Kinetic.Filters.Threshold = function(src,dst,opt){ Kinetic.Filters.Threshold = function(src,dst,opt){
if( this === Kinetic.Filters ){ if( this === Kinetic.Filters ){

View File

@@ -45,36 +45,18 @@
_drawFunc: function(context) { _drawFunc: function(context) {
var width = this.getWidth(), var width = this.getWidth(),
height = this.getHeight(), height = this.getHeight(),
crop, cropWidth, cropHeight, image = this.getImage(),
params, crop, cropWidth, cropHeight, params;
image;
//TODO: this logic needs to hook int othe new caching system if (image) {
crop = this.getCrop(),
// if a filter is set, and the filter needs to be updated, reapply cropWidth = crop.width;
if (this.getFilter() && this._applyFilter) { cropHeight = crop.height;
this.applyFilter(); if (cropWidth && cropHeight) {
this._applyFilter = false; params = [image, crop.x, crop.y, cropWidth, cropHeight, 0, 0, width, height];
} }
else {
// NOTE: this.filterCanvas may be set by the above code block params = [image, 0, 0, width, height];
// In that case, cropping is already applied.
if (this.filterCanvas) {
image = this.filterCanvas._canvas;
params = [image, 0, 0, width, height];
}
else {
image = this.getImage();
if (image) {
crop = this.getCrop(),
cropWidth = crop.width;
cropHeight = crop.height;
if (cropWidth && cropHeight) {
params = [image, crop.x, crop.y, cropWidth, cropHeight, 0, 0, width, height];
} else {
params = [image, 0, 0, width, height];
}
} }
} }
@@ -106,59 +88,6 @@
context.fillStrokeShape(this); context.fillStrokeShape(this);
} }
}, },
applyFilter: function() {
var image = this.getImage(),
width = this.getWidth(),
height = this.getHeight(),
filter = this.getFilter(),
crop = this.getCrop(),
filterCanvas, context, imageData;
// Determine the region we are cropping
crop.x = crop.x;
crop.y = crop.y;
crop.width = crop.width || width - crop.x;
crop.height = crop.height || height - crop.y;
// Make a filterCanvas the same size as the cropped image
if (this.filterCanvas &&
this.filterCanvas.getWidth() === crop.width &&
this.filterCanvas.getHeight() === crop.height) {
filterCanvas = this.filterCanvas;
filterCanvas.getContext().clear();
}
else {
filterCanvas = this.filterCanvas = new Kinetic.SceneCanvas({
width: crop.width,
height: crop.height,
pixelRatio: 1
});
}
context = filterCanvas.getContext();
try {
// Crop the image onto the filterCanvas then apply
// the filter to the filterCanvas
context.drawImage(image, crop.x, crop.y, crop.width, crop.height, 0,0,crop.width, crop.height);
imageData = context.getImageData(0, 0, crop.width, crop.height);
filter.call(this, imageData);
context.putImageData(imageData, 0, 0);
}
catch(e) {
this.clearFilter();
Kinetic.Util.warn('Unable to apply filter. ' + e.message);
}
},
/**
* clear filter
* @method
* @memberof Kinetic.Image.prototype
*/
clearFilter: function() {
this.filterCanvas = null;
this._applyFilter = false;
},
/** /**
* create image hit region which enables more accurate hit detection mapping of the image * create image hit region which enables more accurate hit detection mapping of the image
* by avoiding event detections for transparent pixels * by avoiding event detections for transparent pixels
@@ -227,31 +156,10 @@
getHeight: function() { getHeight: function() {
var image = this.getImage(); var image = this.getImage();
return this.attrs.height || (image ? image.height : 0); return this.attrs.height || (image ? image.height : 0);
},
destroy: function(){
Kinetic.Shape.prototype.destroy.call(this);
delete this.filterCanvas;
delete this.attrs;
return this;
} }
}; };
Kinetic.Util.extend(Kinetic.Image, Kinetic.Shape); Kinetic.Util.extend(Kinetic.Image, Kinetic.Shape);
Kinetic.Factory.addFilterGetterSetter = function(constructor, attr, def) {
this.addGetter(constructor, attr, def);
this.addFilterSetter(constructor, attr);
};
Kinetic.Factory.addFilterSetter = function(constructor, attr) {
var method = SET + Kinetic.Util._capitalize(attr);
constructor.prototype[method] = function(val) {
this._setAttr(attr, val);
this._applyFilter = true;
};
};
// add getters setters // add getters setters
Kinetic.Factory.addGetterSetter(Kinetic.Image, 'image'); Kinetic.Factory.addGetterSetter(Kinetic.Image, 'image');
@@ -363,22 +271,4 @@
* @memberof Kinetic.Image.prototype * @memberof Kinetic.Image.prototype
* @returns {Number} * @returns {Number}
*/ */
Kinetic.Factory.addFilterGetterSetter(Kinetic.Image, 'filter');
/**
* set filter
* @name setFilter
* @method
* @memberof Kinetic.Image.prototype
* @param {Function} filter
*/
/**
* get filter
* @name getFilter
* @method
* @memberof Kinetic.Image.prototype
* @returns {Function}
*/
})(); })();

View File

@@ -80,7 +80,6 @@
<!-- 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>

View File

@@ -17,11 +17,25 @@ suite('Blur', function() {
layer.add(darth); layer.add(darth);
stage.add(layer); stage.add(layer);
darth.setFilter(Kinetic.Filters.Blur); darth.cache();
darth.setFilterRadius(10); darth.filters([Kinetic.Filters.Blur]);
darth.blurRadius(10);
assert.equal(darth.blurRadius(), 10);
assert.equal(darth._filterUpToDate, false);
layer.draw(); layer.draw();
assert.equal(darth.getFilterRadius(), 10); assert.equal(darth._filterUpToDate, true);
darth.blurRadius(20);
assert.equal(darth.blurRadius(), 20);
assert.equal(darth._filterUpToDate, false);
layer.draw();
assert.equal(darth._filterUpToDate, true);
done(); done();
}; };
@@ -29,6 +43,78 @@ suite('Blur', function() {
}); });
test('blur group', function(){
var stage = addStage();
var layer = new Kinetic.Layer();
var group = new Kinetic.Group({
x: 100,
y: 100,
draggable: true
});
var top = new Kinetic.Circle({
x: 0,
y: -70,
radius: 30,
fill: 'blue',
stroke: 'black',
strokeWidth: 4
});
var right = new Kinetic.Circle({
x: 70,
y: 0,
radius: 30,
fill: 'blue',
stroke: 'black',
strokeWidth: 4
});
var bottom = new Kinetic.Circle({
x: 0,
y: 70,
radius: 30,
fill: 'blue',
stroke: 'black',
strokeWidth: 4
});
var left = new Kinetic.Circle({
x: -70,
y: 0,
radius: 30,
fill: 'blue',
stroke: 'black',
strokeWidth: 4
});
group.add(top).add(right).add(bottom).add(left);
layer.add(group);
stage.add(layer);
group.cache({
x: -150,
y: -150,
width: 300,
height: 300
});
group.offset({
x: 150,
y: 150
});
group.filters([Kinetic.Filters.Blur]);
group.blurRadius(20);
layer.draw();
//document.body.appendChild(group._cache.canvas.scene._canvas);
showHit(layer);
});
// ====================================================== // ======================================================
test('tween blur', function(done) { test('tween blur', function(done) {
var stage = addStage(); var stage = addStage();
@@ -47,14 +133,15 @@ suite('Blur', function() {
layer.add(darth); layer.add(darth);
stage.add(layer); stage.add(layer);
darth.setFilter(Kinetic.Filters.Blur); darth.cache();
darth.setFilterRadius(100); darth.filters([Kinetic.Filters.Blur]);
darth.blurRadius(100);
layer.draw(); layer.draw();
var tween = new Kinetic.Tween({ var tween = new Kinetic.Tween({
node: darth, node: darth,
duration: 2.0, duration: 2.0,
filterRadius: 0, blurRadius: 0,
easing: Kinetic.Easings.EaseInOut easing: Kinetic.Easings.EaseInOut
}); });
@@ -91,8 +178,9 @@ suite('Blur', function() {
layer.add(darth); layer.add(darth);
stage.add(layer); stage.add(layer);
darth.setFilter(Kinetic.Filters.Blur); darth.cache();
darth.setFilterRadius(10); darth.filters([Kinetic.Filters.Blur]);
darth.blurRadius(10);
layer.draw(); layer.draw();
done(); done();
@@ -120,14 +208,15 @@ suite('Blur', function() {
layer.add(darth); layer.add(darth);
stage.add(layer); stage.add(layer);
darth.setFilter(Kinetic.Filters.Blur); darth.cache();
darth.setFilterRadius(100); darth.filters([Kinetic.Filters.Blur]);
darth.blurRadius(100);
layer.draw(); layer.draw();
var tween = new Kinetic.Tween({ var tween = new Kinetic.Tween({
node: darth, node: darth,
duration: 2.0, duration: 2.0,
filterRadius: 0, blurRadius: 0,
easing: Kinetic.Easings.EaseInOut easing: Kinetic.Easings.EaseInOut
}); });
@@ -163,14 +252,15 @@ suite('Blur', function() {
layer.add(darth); layer.add(darth);
stage.add(layer); stage.add(layer);
darth.setFilter(Kinetic.Filters.Blur); darth.cache();
darth.setFilterRadius(100); darth.filters([Kinetic.Filters.Blur]);
darth.blurRadius(100);
layer.draw(); layer.draw();
var tween = new Kinetic.Tween({ var tween = new Kinetic.Tween({
node: darth, node: darth,
duration: 2.0, duration: 2.0,
filterRadius: 0, blurRadius: 0,
easing: Kinetic.Easings.EaseInOut easing: Kinetic.Easings.EaseInOut
}); });
@@ -189,64 +279,64 @@ suite('Blur', function() {
}); });
// ====================================================== // ======================================================
test('half layer gaussian 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 imageData = this.getContext().getImageData(0,0,this.getCanvas().width/2,this.getCanvas().height); // var imageData = this.getContext().getImageData(0,0,this.getCanvas().width/2,this.getCanvas().height);
var scratchData = this.getContext().createImageData(imageData); // only size copied // var scratchData = this.getContext().createImageData(imageData); // only size copied
Kinetic.Filters.Blur(imageData,scratchData,{filterRadius:24}); // Kinetic.Filters.Blur(imageData,scratchData,{filterRadius:24});
this.getContext().putImageData(scratchData,0,0); // this.getContext().putImageData(scratchData,0,0);
}); // });
var triangle = new Kinetic.RegularPolygon({ // var triangle = new Kinetic.RegularPolygon({
x: stage.getWidth() / 4, // x: stage.getWidth() / 4,
y: stage.getHeight() / 2, // y: stage.getHeight() / 2,
sides: 3, // sides: 3,
radius: 80, // radius: 80,
fillRadialGradientEndRadius: 70, // fillRadialGradientEndRadius: 70,
fillRadialGradientColorStops: [0, '#881111', 0.5, '#888811', 1, '#000088'], // fillRadialGradientColorStops: [0, '#881111', 0.5, '#888811', 1, '#000088'],
stroke: 'black', // stroke: 'black',
strokeWidth: 4, // strokeWidth: 4,
draggable: true // draggable: true
}); // });
var circle = new Kinetic.Circle({ // var circle = new Kinetic.Circle({
x: 3 * stage.getWidth() / 4, // x: 3 * stage.getWidth() / 4,
y: stage.getHeight() / 2, // y: stage.getHeight() / 2,
radius: 70, // radius: 70,
fill: '#880000', // fill: '#880000',
stroke: 'black', // stroke: 'black',
strokeWidth: 4, // strokeWidth: 4,
draggable: true, // draggable: true,
id: 'myCircle' // id: 'myCircle'
}); // });
for( var i=0; i<10; i+=1 ){ // for( var i=0; i<10; i+=1 ){
for( var j=0; j<10; j+=1 ){ // for( var j=0; j<10; j+=1 ){
var rect = new Kinetic.Rect({ // var rect = new Kinetic.Rect({
x: i/10*stage.getWidth(), // x: i/10*stage.getWidth(),
y: j/10*stage.getHeight(), // y: j/10*stage.getHeight(),
width: stage.getWidth()/10, // width: stage.getWidth()/10,
height: stage.getHeight()/10, // height: stage.getHeight()/10,
fill: (i+j)%2===0?'#FF0000':'#FFFF00', // fill: (i+j)%2===0?'#FF0000':'#FFFF00',
stroke: 'black', // stroke: 'black',
strokeWidth: 4, // strokeWidth: 4,
draggable: true // draggable: true
}); // });
shapesLayer.add(rect); // shapesLayer.add(rect);
} // }
} // }
shapesLayer.add(circle); // shapesLayer.add(circle);
shapesLayer.add(triangle); // shapesLayer.add(triangle);
stage.add(shapesLayer); // stage.add(shapesLayer);
done(); // done();
}); // });
}); });

View File

@@ -1,154 +0,0 @@
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';
});
});