mirror of
https://github.com/konvajs/konva.git
synced 2025-09-18 09:50:05 +08:00
Add the Crop filter
This commit is contained in:
2
Thorfile
2
Thorfile
@@ -5,7 +5,7 @@ class Build < Thor
|
||||
# This is the list of files to concatenate. The first file will appear at the top of the final file. All files are relative to the lib directory.
|
||||
FILES = [
|
||||
"src/Global.js", "src/util/Type.js", "src/Canvas.js", "src/util/Tween.js", "src/util/Transform.js", "src/util/Collection.js",
|
||||
"src/filters/Grayscale.js", "src/filters/Brighten.js", "src/filters/Invert.js", "src/filters/Blur.js",
|
||||
"src/filters/Grayscale.js", "src/filters/Brighten.js", "src/filters/Invert.js", "src/filters/Blur.js", "src/filters/Crop.js",
|
||||
"src/Node.js", "src/Animation.js", "src/DragAndDrop.js", "src/Transition.js", "src/Container.js", "src/Shape.js", "src/Stage.js", "src/Layer.js", "src/Group.js",
|
||||
"src/shapes/Rect.js", "src/shapes/Circle.js", "src/shapes/Wedge.js", "src/shapes/Ellipse.js", "src/shapes/Image.js", "src/shapes/Polygon.js", "src/shapes/Text.js", "src/shapes/Line.js", "src/shapes/Spline.js", "src/shapes/Blob.js", "src/shapes/Sprite.js",
|
||||
"src/plugins/Path.js", "src/plugins/TextPath.js", "src/plugins/RegularPolygon.js", "src/plugins/Star.js", "src/plugins/Label.js"
|
||||
|
200
src/filters/Crop.js
Normal file
200
src/filters/Crop.js
Normal file
@@ -0,0 +1,200 @@
|
||||
(function(Kinetic) {
|
||||
|
||||
function pixelAt(idata, x, y) {
|
||||
var idx = (y * idata.width + x) * 4;
|
||||
var d = [];
|
||||
d.push(idata.data[idx++], idata.data[idx++], idata.data[idx++], idata.data[idx++]);
|
||||
return d;
|
||||
};
|
||||
|
||||
function rgbDistance(p1, p2) {
|
||||
return Math.sqrt(Math.pow(p1[0] - p2[0], 2) + Math.pow(p1[1] - p2[1], 2) + Math.pow(p1[2] - p2[2], 2));
|
||||
};
|
||||
|
||||
function rgbMean(pTab) {
|
||||
var m = [0, 0, 0];
|
||||
|
||||
for (var i = 0; i < pTab.length; i++) {
|
||||
m[0] += pTab[i][0];
|
||||
m[1] += pTab[i][1];
|
||||
m[2] += pTab[i][2];
|
||||
}
|
||||
|
||||
m[0] /= pTab.length;
|
||||
m[1] /= pTab.length;
|
||||
m[2] /= pTab.length;
|
||||
|
||||
return m;
|
||||
};
|
||||
|
||||
function backgroundMask(idata, config) {
|
||||
var rgbv_no = pixelAt(idata, 0, 0);
|
||||
var rgbv_ne = pixelAt(idata, idata.width - 1, 0);
|
||||
var rgbv_so = pixelAt(idata, 0, idata.height - 1);
|
||||
var rgbv_se = pixelAt(idata, idata.width - 1, idata.height - 1);
|
||||
|
||||
|
||||
var thres = (config && config.threshold) ? config.threshold : 10;
|
||||
if (rgbDistance(rgbv_no, rgbv_ne) < thres && rgbDistance(rgbv_ne, rgbv_se) < thres && rgbDistance(rgbv_se, rgbv_so) < thres && rgbDistance(rgbv_so, rgbv_no) < thres) {
|
||||
|
||||
// Mean color
|
||||
var mean = rgbMean([rgbv_ne, rgbv_no, rgbv_se, rgbv_so]);
|
||||
|
||||
// Mask based on color distance
|
||||
var mask = [];
|
||||
for (var i = 0; i < idata.width * idata.height; i++) {
|
||||
var d = rgbDistance(mean, [idata.data[i * 4], idata.data[i * 4 + 1], idata.data[i * 4 + 2]]);
|
||||
mask[i] = (d < thres) ? 0 : 255;
|
||||
}
|
||||
|
||||
return mask;
|
||||
}
|
||||
};
|
||||
|
||||
function applyMask(idata, mask) {
|
||||
for (var i = 0; i < idata.width * idata.height; i++) {
|
||||
idata.data[4 * i + 3] = mask[i];
|
||||
}
|
||||
};
|
||||
|
||||
function erodeMask(mask, sw, sh) {
|
||||
|
||||
var weights = [1, 1, 1, 1, 0, 1, 1, 1, 1];
|
||||
var side = Math.round(Math.sqrt(weights.length));
|
||||
var halfSide = Math.floor(side / 2);
|
||||
|
||||
var maskResult = [];
|
||||
for (var y = 0; y < sh; y++) {
|
||||
for (var x = 0; x < sw; x++) {
|
||||
|
||||
var so = y * sw + x;
|
||||
var a = 0;
|
||||
for (var cy = 0; cy < side; cy++) {
|
||||
for (var cx = 0; cx < side; cx++) {
|
||||
var scy = y + cy - halfSide;
|
||||
var scx = x + cx - halfSide;
|
||||
|
||||
if (scy >= 0 && scy < sh && scx >= 0 && scx < sw) {
|
||||
|
||||
var srcOff = scy * sw + scx;
|
||||
var wt = weights[cy * side + cx];
|
||||
|
||||
a += mask[srcOff] * wt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
maskResult[so] = (a === 255 * 8) ? 255 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
return maskResult;
|
||||
};
|
||||
|
||||
function dilateMask(mask, sw, sh) {
|
||||
|
||||
var weights = [1, 1, 1, 1, 1, 1, 1, 1, 1];
|
||||
var side = Math.round(Math.sqrt(weights.length));
|
||||
var halfSide = Math.floor(side / 2);
|
||||
|
||||
var maskResult = [];
|
||||
for (var y = 0; y < sh; y++) {
|
||||
for (var x = 0; x < sw; x++) {
|
||||
|
||||
var so = y * sw + x;
|
||||
var a = 0;
|
||||
for (var cy = 0; cy < side; cy++) {
|
||||
for (var cx = 0; cx < side; cx++) {
|
||||
var scy = y + cy - halfSide;
|
||||
var scx = x + cx - halfSide;
|
||||
|
||||
if (scy >= 0 && scy < sh && scx >= 0 && scx < sw) {
|
||||
|
||||
var srcOff = scy * sw + scx;
|
||||
var wt = weights[cy * side + cx];
|
||||
|
||||
a += mask[srcOff] * wt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
maskResult[so] = (a >= 255 * 4) ? 255 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
return maskResult;
|
||||
};
|
||||
|
||||
function smoothEdgeMask(mask, sw, sh) {
|
||||
|
||||
var weights = [1 / 9, 1 / 9, 1 / 9, 1 / 9, 1 / 9, 1 / 9, 1 / 9, 1 / 9, 1 / 9];
|
||||
var side = Math.round(Math.sqrt(weights.length));
|
||||
var halfSide = Math.floor(side / 2);
|
||||
|
||||
var maskResult = [];
|
||||
for (var y = 0; y < sh; y++) {
|
||||
for (var x = 0; x < sw; x++) {
|
||||
|
||||
var so = y * sw + x;
|
||||
var a = 0;
|
||||
for (var cy = 0; cy < side; cy++) {
|
||||
for (var cx = 0; cx < side; cx++) {
|
||||
var scy = y + cy - halfSide;
|
||||
var scx = x + cx - halfSide;
|
||||
|
||||
if (scy >= 0 && scy < sh && scx >= 0 && scx < sw) {
|
||||
|
||||
var srcOff = scy * sw + scx;
|
||||
var wt = weights[cy * side + cx];
|
||||
|
||||
a += mask[srcOff] * wt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
maskResult[so] = a;
|
||||
}
|
||||
}
|
||||
|
||||
return maskResult;
|
||||
}
|
||||
|
||||
Kinetic = Kinetic || {};
|
||||
Kinetic.Filters = Kinetic.Filters || {};
|
||||
|
||||
/**
|
||||
* Crop Filter
|
||||
*
|
||||
* Only crop unicolor background images for instance
|
||||
*
|
||||
* @function
|
||||
* @memberOf Kinetic.Filters
|
||||
* @param {Object} imageData
|
||||
* @param {Object} config
|
||||
* @param {Integer} config.threshold The RGB euclidian distance threshold (default : 10)
|
||||
*/
|
||||
Kinetic.Filters.Crop = function(idata, config) {
|
||||
// Detect pixels close to the background color
|
||||
var mask = backgroundMask(idata, config);
|
||||
if (mask) {
|
||||
// Erode
|
||||
mask = erodeMask(mask, idata.width, idata.height);
|
||||
|
||||
// Dilate
|
||||
mask = dilateMask(mask, idata.width, idata.height);
|
||||
|
||||
// Gradient
|
||||
mask = smoothEdgeMask(mask, idata.width, idata.height);
|
||||
|
||||
// Apply mask
|
||||
applyMask(idata, mask);
|
||||
|
||||
// todo : Update hit region function according to mask
|
||||
}
|
||||
|
||||
return idata;
|
||||
};
|
||||
|
||||
window['Kinetic'] = Kinetic;
|
||||
|
||||
})(Kinetic);
|
BIN
tests/assets/bamoon.jpg
Normal file
BIN
tests/assets/bamoon.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 27 KiB |
File diff suppressed because one or more lines are too long
@@ -407,5 +407,44 @@ Test.Modules.IMAGE = {
|
||||
imageObj.src = '../assets/lion.png';
|
||||
|
||||
showHit(layer);
|
||||
},
|
||||
'crop unicolor background filter': function(containerId) {
|
||||
var imageObj = new Image();
|
||||
imageObj.onload = function() {
|
||||
var stage = new Kinetic.Stage({
|
||||
container: containerId,
|
||||
width: 600,
|
||||
height: 200
|
||||
});
|
||||
var layer = new Kinetic.Layer({
|
||||
throttle: 999
|
||||
});
|
||||
var bamoon = new Kinetic.Image({
|
||||
x: 0,
|
||||
y: 0,
|
||||
image: imageObj,
|
||||
draggable: true
|
||||
}),
|
||||
filtered = new Kinetic.Image({
|
||||
x: 300,
|
||||
y: 0,
|
||||
image: imageObj,
|
||||
draggable: true
|
||||
});
|
||||
|
||||
layer.add(bamoon);
|
||||
layer.add(filtered);
|
||||
stage.add(layer);
|
||||
|
||||
filtered.applyFilter(Kinetic.Filters.Crop, {
|
||||
threshold: 10
|
||||
}, function() {
|
||||
layer.draw();
|
||||
var dataUrl = layer.toDataURL();
|
||||
//console.log(dataUrl);
|
||||
testDataUrl(dataUrl, 'crop filter', 'problem with Crop filter.');
|
||||
});
|
||||
};
|
||||
imageObj.src = '../assets/bamoon.jpg';
|
||||
}
|
||||
};
|
||||
|
Reference in New Issue
Block a user