Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add shadow option to toDataURL and cloneAsImage #5308

Merged
merged 9 commits into from
Oct 14, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 0 additions & 5 deletions src/mixins/canvas_dataurl_exporter.mixin.js
Original file line number Diff line number Diff line change
Expand Up @@ -99,11 +99,6 @@
__toDataURL: function(format, quality) {

var canvasEl = this.contextContainer.canvas;
// to avoid common confusion https://github.com/kangax/fabric.js/issues/806
if (format === 'jpg') {
format = 'jpeg';
}

var data = supportQuality
? canvasEl.toDataURL('image/' + format, quality)
: canvasEl.toDataURL('image/' + format);
Expand Down
45 changes: 32 additions & 13 deletions src/shapes/object.class.js
Original file line number Diff line number Diff line change
Expand Up @@ -1528,7 +1528,16 @@
* Creates an instance of fabric.Image out of an object
* @param {Function} callback callback, invoked with an instance as a first argument
* @param {Object} [options] for clone as image, passed to toDataURL
* @param {Boolean} [options.enableRetinaScaling] enable retina scaling for the cloned image
* @param {String} [options.format=png] The format of the output image. Either "jpeg" or "png"
* @param {Number} [options.quality=1] Quality level (0..1). Only used for jpeg.
* @param {Number} [options.multiplier=1] Multiplier to scale by
* @param {Number} [options.left] Cropping left offset. Introduced in v1.2.14
* @param {Number} [options.top] Cropping top offset. Introduced in v1.2.14
* @param {Number} [options.width] Cropping width. Introduced in v1.2.14
* @param {Number} [options.height] Cropping height. Introduced in v1.2.14
* @param {Boolean} [options.enableRetinaScaling] Enable retina scaling for clone image. Introduce in 1.6.4
* @param {Boolean} [options.withoutTransform] Remove current object transform ( no scale , no angle, no flip, no skew ). Introduced in 2.3.4
* @param {Boolean} [options.withoutShadow] Remove current object shadow. Introduced in 2.4.2
* @return {fabric.Object} thisArg
*/
cloneAsImage: function(callback, options) {
Expand All @@ -1553,42 +1562,52 @@
* @param {Number} [options.height] Cropping height. Introduced in v1.2.14
* @param {Boolean} [options.enableRetinaScaling] Enable retina scaling for clone image. Introduce in 1.6.4
* @param {Boolean} [options.withoutTransform] Remove current object transform ( no scale , no angle, no flip, no skew ). Introduced in 2.3.4
* @param {Boolean} [options.withoutShadow] Remove current object shadow. Introduced in 2.4.2
* @return {String} Returns a data: URL containing a representation of the object in the format specified by options.format
*/
toDataURL: function(options) {
options || (options = { });

var origParams = fabric.util.saveObjectTransform(this);
var utils = fabric.util, origParams = utils.saveObjectTransform(this),
originalShadow = this.shadow, abs = Math.abs;

if (options.withoutTransform) {
fabric.util.resetObjectTransform(this);
utils.resetObjectTransform(this);
}
if (options.withoutShadow) {
this.shadow = null;
}

var el = fabric.util.createCanvasElement(),
// skip canvas zoom and calculate with setCoords now.
boundingRect = this.getBoundingRect(true, true);

el.width = boundingRect.width;
el.height = boundingRect.height;
boundingRect = this.getBoundingRect(true, true),
shadow = this.shadow, scaling,
shadowOffset = { x: 0, y: 0 }, shadowBlur;

if (shadow) {
shadowBlur = shadow.blur;
scaling = this.getObjectScaling();
shadowOffset.x = 2 * Math.round((abs(shadow.offsetX) + shadowBlur) * abs(scaling.scaleX));
shadowOffset.y = 2 * Math.round((abs(shadow.offsetY) + shadowBlur) * abs(scaling.scaleY));
}
el.width = boundingRect.width + shadowOffset.x;
el.height = boundingRect.height + shadowOffset.y;
el.width += el.width % 2 ? 2 - el.width % 2 : 0;
el.height += el.height % 2 ? 2 - el.height % 2 : 0;
var canvas = new fabric.StaticCanvas(el, {
enableRetinaScaling: options.enableRetinaScaling,
renderOnAddRemove: false,
skipOffscreen: false,
});
// to avoid common confusion https://github.com/kangax/fabric.js/issues/806
if (options.format === 'jpg') {
options.format = 'jpeg';
}

if (options.format === 'jpeg') {
canvas.backgroundColor = '#fff';
}

this.setPositionByOrigin(new fabric.Point(canvas.width / 2, canvas.height / 2), 'center', 'center');

var originalCanvas = this.canvas;
canvas.add(this);
var data = canvas.toDataURL(options);
this.shadow = originalShadow;
this.set(origParams).setCoords();
this.canvas = originalCanvas;
// canvas.dispose will call image.dispose that will nullify the elements
Expand Down
4 changes: 2 additions & 2 deletions test/unit/canvas_static.js
Original file line number Diff line number Diff line change
Expand Up @@ -576,13 +576,13 @@
img.src = dataUrl;
});

QUnit.test('toDataURL jpg', function(assert) {
QUnit.test('toDataURL jpeg', function(assert) {
if (!fabric.Canvas.supports('toDataURL')) {
window.alert('toDataURL is not supported by this environment. Some of the tests can not be run.');
}
else {
try {
var dataURL = canvas.toDataURL({ format: 'jpg' });
var dataURL = canvas.toDataURL({ format: 'jpeg' });
assert.equal(dataURL.substring(0, 22), 'data:image/jpeg;base64');
}
// node-canvas does not support jpeg data urls
Expand Down
Binary file added test/visual/golden/dataurl1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added test/visual/golden/dataurl2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added test/visual/golden/dataurl3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
106 changes: 106 additions & 0 deletions test/visual/toDataURL.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
(function() {
if ((fabric.isLikelyNode && process.env.launcher === 'Firefox') || navigator.userAgent.indexOf('Firefox') !== -1) {
fabric.browserShadowBlurConstant = 0.9;
}
if ((fabric.isLikelyNode && process.env.launcher === 'Node')) {
fabric.browserShadowBlurConstant = 1;
}
if ((fabric.isLikelyNode && process.env.launcher === 'Chrome') || navigator.userAgent.indexOf('Chrome') !== -1) {
fabric.browserShadowBlurConstant = 1.5;
}
if ((fabric.isLikelyNode && process.env.launcher === 'Edge') || navigator.userAgent.indexOf('Edge') !== -1) {
fabric.browserShadowBlurConstant = 1.75;
}
fabric.enableGLFiltering = false;
fabric.isWebglSupported = false;
fabric.Object.prototype.objectCaching = true;
var visualTestLoop;
if (fabric.isLikelyNode) {
visualTestLoop = global.visualTestLoop;
}
else {
visualTestLoop = window.visualTestLoop;
}
var fabricCanvas = this.canvas = new fabric.Canvas(null, {
enableRetinaScaling: false, renderOnAddRemove: false, width: 200, height: 200,
});

var tests = [];

function toDataURL1(canvas, callback) {
var text = new fabric.Text('Hi i m an image',
{ strokeWidth: 2, stroke: 'red', fontSize: 60, objectCaching: false }
);
callback(text.toDataURL());
}

tests.push({
test: 'Text to DataURL',
code: toDataURL1,
golden: 'dataurl1.png',
newModule: 'DataURL exports',
percentage: 0.09,
});

function toDataURL2(canvas, callback) {
var text = new fabric.Text('Hi i m an image',
{ strokeWidth: 0, fontSize: 60, objectCaching: false }
);
var shadow = new fabric.Shadow({
color: 'purple',
offsetX: 0,
offsetY: 0,
blur: 6,
});
text.shadow = shadow;
callback(text.toDataURL());
}

tests.push({
test: 'Text to DataURL with shadow no offset',
code: toDataURL2,
golden: 'dataurl2.png',
percentage: 0.09,
});

function toDataURL3(canvas, callback) {
var text = new fabric.Text('Hi i m an image',
{ strokeWidth: 0, fontSize: 60, objectCaching: false }
);
var shadow = new fabric.Shadow({
color: 'purple',
offsetX: -30,
offsetY: +40,
blur: 10,
});
text.shadow = shadow;
callback(text.toDataURL());
}

tests.push({
test: 'Text to DataURL with shadow large offset',
code: toDataURL3,
golden: 'dataurl3.png',
percentage: 0.09,
});

function testWrapper(test) {
var actualTest = test.code;
test.code = function(canvas, callback) {
actualTest(canvas, function(dataURL) {
var img = fabric.document.createElement('img');
var canvas = fabric.document.createElement('canvas');
img.onload = function() {
canvas.width = img.width;
canvas.height = img.height;
canvas.getContext('2d').drawImage(img, 0, 0);
callback(canvas);
};
img.src = dataURL;
});
};
visualTestLoop(fabricCanvas, QUnit)(test);
}

tests.forEach(testWrapper);
})();