Skip to content

Commit

Permalink
ClipPath to svg (fabricjs#5234)
Browse files Browse the repository at this point in the history
* svg plain examples works

* more code reuse in visual tests
  • Loading branch information
asturur authored Sep 16, 2018
1 parent 043118d commit 2571e32
Show file tree
Hide file tree
Showing 10 changed files with 243 additions and 372 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ jobs:
- stage: Unit Tests
node_js: "4"
- stage: Visual Tests
env: LAUNCHER=Node CANFAIL=TRUE
env: LAUNCHER=Node
node_js: "8"
script: npm run build:fast && npm run test:visual
- stage: Visual Tests
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@
"lint": "eslint --config .eslintrc.json src",
"lint_tests": "eslint test/unit --config .eslintrc_tests && eslint test/visual --config .eslintrc_tests",
"export_dist_to_site": "cp dist/fabric.js ../fabricjs.com/lib/fabric.js && cp package.json ../fabricjs.com/lib/package.json && cp -r src HEADER.js lib ../fabricjs.com/build/files/",
"export_tests_to_site": "cp test/unit/*.js ../fabricjs.com/test/unit && cp -r test/visual/* ../fabricjs.com/test/visual && cp -r test/fixtures/* ../fabricjs.com/test/fixtures",
"export_tests_to_site": "cp test/unit/*.js ../fabricjs.com/test/unit && cp -r test/visual/* ../fabricjs.com/test/visual && cp -r test/fixtures/* ../fabricjs.com/test/fixtures && cp -r test/lib/* ../fabricjs.com/test/lib",
"all": "npm run build && npm run test && npm run test:visual && npm run lint && npm run lint_tests && npm run export_dist_to_site && npm run export_tests_to_site",
"testem": "testem .",
"testem:visual": "testem --file testem-visual.json",
Expand Down
7 changes: 6 additions & 1 deletion src/elements_parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,12 @@ fabric.ElementsParser = function(elements, callback, options, reviver, parsingOp
this.options
);
}
clipPath = new fabric.Group(container);
if (container.length === 1) {
clipPath = container[0];
}
else {
clipPath = new fabric.Group(container);
}
gTransform = fabric.util.multiplyTransformMatrices(
objTransformInv,
clipPath.calcTransformMatrix()
Expand Down
8 changes: 2 additions & 6 deletions src/mixins/object.svg_export.js
Original file line number Diff line number Diff line change
Expand Up @@ -211,13 +211,9 @@
markup.push(this.shadow.toSVG(this));
}
if (clipPath) {
if (clipPath.clipPathId === undefined) {
clipPath.clipPathId = 'CLIPPATH_' + fabric.Object.__uid++;
}
clipPath.clipPathId = 'CLIPPATH_' + fabric.Object.__uid++;
markup.push(
'<clipPath id="' + clipPath.clipPathId + '" ',
'clipPathUnits="objectBoundingBox" ',
'transform="translate(' + (this.width / 2) + ' , ' + (this.height / 2) + ')" >\n\t',
'<clipPath id="' + clipPath.clipPathId + '" >\n\t',
this.clipPath.toSVG(),
'</clipPath>\n'
);
Expand Down
149 changes: 149 additions & 0 deletions test/lib/visualTestLoop.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
(function(exports) {

exports.getFixture = function(name, original, callback) {
getImage(getFixtureName(name), original, callback);
};

exports.getAsset = function(name, callback) {
var finalName = getAssetName(name);
if (fabric.isLikelyNode) {
return fs.readFile(finalName, { encoding: 'utf8' }, callback);
}
else {
fabric.util.request(finalName, {
onComplete: function(xhr) {
callback(null, xhr.responseText);
}
});
}
};

function getAbsolutePath(path) {
var isAbsolute = /^https?:/.test(path);
if (isAbsolute) { return path; };
var imgEl = fabric.document.createElement('img');
imgEl.src = path;
var src = imgEl.src;
imgEl = null;
return src;
}

function getAssetName(filename) {
var finalName = '/assets/' + filename + '.svg';
return fabric.isLikelyNode ? (__dirname + '/../visual' + finalName) : getAbsolutePath('/test/visual' + finalName);
}

function getGoldeName(filename) {
var finalName = '/golden/' + filename;
return fabric.isLikelyNode ? (__dirname + '/../visual' + finalName) : getAbsolutePath('/test/visual' + finalName);
}

function getFixtureName(filename) {
var finalName = '/fixtures/' + filename;
return fabric.isLikelyNode ? (__dirname + '/..' + finalName) : getAbsolutePath('/test' + finalName);
}

function getImage(filename, original, callback) {
if (fabric.isLikelyNode && original) {
try {
fs.statSync(filename);
}
catch (err) {
var dataUrl = original.toDataURL().split(',')[1];
console.log('creating original for ', filename);
fs.writeFileSync(filename, dataUrl, { encoding: 'base64' });
}
}
var img = fabric.document.createElement('img');
img.onload = function() {
img.onload = null;
callback(img);
};
img.onerror = function(err) {
img.onerror = null;
callback(img);
console.log('Image loading errored', err);
};
img.src = filename;
}

exports.visualTestLoop = function(fabricCanvas, QUnit) {
var _pixelMatch;
var visualCallback;
var imageDataToChalk;
if (fabric.isLikelyNode) {
_pixelMatch = global.pixelmatch;
visualCallback = global.visualCallback;
imageDataToChalk = global.imageDataToChalk;
}
else {
if (window) {
_pixelMatch = window.pixelmatch;
visualCallback = window.visualCallback;
}
imageDataToChalk = function() { return ''; };
}

var pixelmatchOptions = {
includeAA: false,
threshold: 0.095
};

function beforeEachHandler() {
fabricCanvas.clipPath = null;
fabricCanvas.viewportTransform = [1, 0, 0, 1, 0, 0];
fabricCanvas.clear();
fabricCanvas.renderAll();
}

return function testCallback(testObj) {
var testName = testObj.test;
var code = testObj.code;
var percentage = testObj.percentage;
var golden = testObj.golden;
var newModule = testObj.newModule;
if (newModule) {
QUnit.module(newModule, {
beforeEach: beforeEachHandler,
});
}
QUnit.test(testName, function(assert) {
var done = assert.async();
code(fabricCanvas, function(renderedCanvas) {
var width = renderedCanvas.width;
var height = renderedCanvas.height;
var totalPixels = width * height;
var imageDataCanvas = renderedCanvas.getContext('2d').getImageData(0, 0, width, height).data;
var canvas = fabric.document.createElement('canvas');
canvas.width = width;
canvas.height = height;
var ctx = canvas.getContext('2d');
var output = ctx.getImageData(0, 0, width, height);
getImage(getGoldeName(golden), renderedCanvas, function(goldenImage) {
ctx.drawImage(goldenImage, 0, 0);
visualCallback.addArguments({
enabled: true,
golden: canvas,
fabric: renderedCanvas,
diff: output
});
var imageDataGolden = ctx.getImageData(0, 0, width, height).data;
var differentPixels = _pixelMatch(imageDataCanvas, imageDataGolden, output.data, width, height, pixelmatchOptions);
var percDiff = differentPixels / totalPixels * 100;
var okDiff = totalPixels * percentage;
var isOK = differentPixels < okDiff;
assert.ok(
isOK,
testName + ' has too many different pixels ' + differentPixels + '(' + okDiff + ') representing ' + percDiff + '%'
);
if (!isOK) {
var stringa = imageDataToChalk(output);
console.log(stringa);
}
done();
});
});
});
}
}
})(typeof window === 'undefined' ? exports : this);
5 changes: 4 additions & 1 deletion test/node_test_setup.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,13 @@ global.fs = require('fs');
global.visualCallback = {
addArguments: function() {},
};
global.visualTestLoop = require('./lib/visualTestLoop').visualTestLoop;
global.getFixture = require('./lib/visualTestLoop').getFixture;
global.getAsset = require('./lib/visualTestLoop').getAsset;
global.imageDataToChalk = function(imageData) {
// actually this does not work on travis-ci, so commenting it out
return '';
var block = String.fromCharCode(9608)
var block = String.fromCharCode(9608);
var data = imageData.data;
var width = imageData.width;
var height = imageData.height;
Expand Down
117 changes: 4 additions & 113 deletions test/visual/clippath.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,82 +2,22 @@
fabric.enableGLFiltering = false;
fabric.isWebglSupported = false;
fabric.Object.prototype.objectCaching = true;
var _pixelMatch;
var visualCallback;
var fs;
var imageDataToChalk;
var visualTestLoop;
if (fabric.isLikelyNode) {
fs = global.fs;
_pixelMatch = global.pixelmatch;
visualCallback = global.visualCallback;
imageDataToChalk = global.imageDataToChalk;
visualTestLoop = global.visualTestLoop;
}
else {
_pixelMatch = pixelmatch;
if (window) {
visualCallback = window.visualCallback;
}
imageDataToChalk = function() { return ''; };
visualTestLoop = window.visualTestLoop;
}
var fabricCanvas = this.canvas = new fabric.Canvas(null, {
enableRetinaScaling: false, renderOnAddRemove: false, width: 200, height: 200,
});
var pixelmatchOptions = {
includeAA: false,
threshold: 0.095
};

function getAbsolutePath(path) {
var isAbsolute = /^https?:/.test(path);
if (isAbsolute) { return path; };
var imgEl = fabric.document.createElement('img');
imgEl.src = path;
var src = imgEl.src;
imgEl = null;
return src;
}

// function getFixtureName(filename) {
// var finalName = '/fixtures/' + filename;
// return fabric.isLikelyNode ? (__dirname + '/..' + finalName) : getAbsolutePath('/test' + finalName);
// }

function getGoldeName(filename) {
var finalName = '/golden/' + filename;
return fabric.isLikelyNode ? (__dirname + finalName) : getAbsolutePath('/test/visual' + finalName);
}

function getImage(filename, original, callback) {
if (fabric.isLikelyNode && original) {
try {
fs.statSync(filename);
}
catch (err) {
var dataUrl = original.toDataURL().split(',')[1];
console.log('creating original for ', filename);
fs.writeFileSync(filename, dataUrl, { encoding: 'base64' });
}
}
var img = fabric.document.createElement('img');
img.onload = function() {
img.onload = null;
callback(img);
};
img.onerror = function(err) {
img.onerror = null;
callback(img);
console.log('Image loading errored', err);
};
img.src = filename;
}

function beforeEachHandler() {
fabricCanvas.clipPath = null;
fabricCanvas.viewportTransform = [1, 0, 0, 1, 0, 0];
fabricCanvas.clear();
fabricCanvas.renderAll();
}

var tests = [];

function clipping0(canvas, callback) {
Expand Down Expand Up @@ -365,54 +305,5 @@
percentage: 0.06,
});


tests.forEach(function(testObj) {
var testName = testObj.test;
var code = testObj.code;
var percentage = testObj.percentage;
var golden = testObj.golden;
var newModule = testObj.newModule;
if (newModule) {
QUnit.module(newModule, {
beforeEach: beforeEachHandler,
});
}
QUnit.test(testName, function(assert) {
var done = assert.async();
code(fabricCanvas, function(renderedCanvas) {
var width = renderedCanvas.width;
var height = renderedCanvas.height;
var totalPixels = width * height;
var imageDataCanvas = renderedCanvas.getContext('2d').getImageData(0, 0, width, height).data;
var canvas = fabric.document.createElement('canvas');
canvas.width = width;
canvas.height = height;
var ctx = canvas.getContext('2d');
var output = ctx.getImageData(0, 0, width, height);
getImage(getGoldeName(golden), renderedCanvas, function(goldenImage) {
ctx.drawImage(goldenImage, 0, 0);
visualCallback.addArguments({
enabled: true,
golden: canvas,
fabric: renderedCanvas,
diff: output
});
var imageDataGolden = ctx.getImageData(0, 0, width, height).data;
var differentPixels = _pixelMatch(imageDataCanvas, imageDataGolden, output.data, width, height, pixelmatchOptions);
var percDiff = differentPixels / totalPixels * 100;
var okDiff = totalPixels * percentage;
var isOK = differentPixels < okDiff;
assert.ok(
isOK,
testName + ' has too many different pixels ' + differentPixels + '(' + okDiff + ') representing ' + percDiff + '%'
);
if (!isOK) {
var stringa = imageDataToChalk(output);
console.log(stringa);
}
done();
});
});
});
});
tests.forEach(visualTestLoop(fabricCanvas, QUnit));
})();
Loading

0 comments on commit 2571e32

Please sign in to comment.