bring callback toapplyfilters (#3210)

* bring callback toapplyfilters
This commit is contained in:
Andrea Bogazzi 2016-08-28 22:42:41 +02:00 committed by GitHub
parent 148b5c1434
commit bba8b3a705
5 changed files with 211 additions and 212 deletions

View file

@ -62,6 +62,9 @@
* @param {Number} scaleY
*/
applyTo: function(canvasEl, scaleX, scaleY) {
if (scaleX === 1 && scaleY === 1) {
return;
}
this.rcpScaleX = 1 / scaleX;
this.rcpScaleY = 1 / scaleY;

View file

@ -127,20 +127,10 @@
});
};
fabric.Image.fromObject = function(object, callback) {
fabric.util.loadImage(object.src, function(img) {
var oImg = new fabric.Image(img);
// fabric.util.createCanvasElement = function(_, width, height) {
// return new Canvas(width, height);
// }
oImg._initConfig(object);
oImg._initFilters(object.filters, function(filters) {
oImg.filters = filters || [ ];
oImg._initFilters(object.resizeFilters, function(resizeFilters) {
oImg.resizeFilters = resizeFilters || [ ];
callback && callback(oImg);
});
});
});
};
/**
* Only available when running fabric on node.js
* @param {Number} width Canvas width

View file

@ -88,18 +88,28 @@
*/
_lastScaleY: 1,
/**
* minimum scale factor under which any resizeFilter is triggered to resize the image
* 0 will disable the automatic resize. 1 will trigger automatically always.
* number bigger than 1 can be used in case we want to scale with some filter above
* the natural image dimensions
* @type Number
*/
minimumScaleTrigger: 0.5,
/**
* Constructor
* @param {HTMLImageElement | String} element Image element
* @param {Object} [options] Options object
* @param {function} [callback] callback function to call after eventual filters applied.
* @return {fabric.Image} thisArg
*/
initialize: function(element, options) {
initialize: function(element, options, callback) {
options || (options = { });
this.filters = [ ];
this.resizeFilters = [ ];
this.callSuper('initialize', options);
this._initElement(element, options);
this._initElement(element, options, callback);
},
/**
@ -121,15 +131,28 @@
* @chainable
*/
setElement: function(element, callback, options) {
var _callback, _this;
this._element = element;
this._originalElement = element;
this._initConfig(options);
if (this.filters.length !== 0) {
this.applyFilters(callback);
if (this.resizeFilters.length === 0) {
_callback = callback;
}
else if (callback) {
callback();
else {
_this = this;
_callback = function() {
_this.applyFilters(callback, _this.resizeFilters, _this._filteredEl || _this._originalElement, true);
};
}
if (this.filters.length !== 0) {
this.applyFilters(_callback);
}
else if (_callback) {
_callback(this);
}
return this;
@ -296,7 +319,7 @@
* @return {String} Source of an image
*/
getSrc: function() {
var element = this.getElement();
var element = this._originalElement;
if (element) {
return fabric.isLikelyNode ? element._src : element.src;
}
@ -340,10 +363,10 @@
* Applies filters assigned to this image (from "filters" array)
* @method applyFilters
* @param {Function} callback Callback is invoked when all filters have been applied and new image is generated
* @param {Array} filters to be initialized
* @param {fabric.Image} imgElement
* @param {Array} filters to be applied
* @param {fabric.Image} imgElement image to filter ( default to this._element )
* @param {Boolean} forResizing
* @return {fabric.Image} thisArg
* @return {CanvasElement} canvasEl to be drawn immediately
* @chainable
*/
applyFilters: function(callback, filters, imgElement, forResizing) {
@ -355,22 +378,38 @@
return;
}
var imgEl = imgElement,
canvasEl = fabric.util.createCanvasElement(),
replacement = fabric.util.createImage(),
_this = this;
canvasEl.width = imgEl.width;
canvasEl.height = imgEl.height;
canvasEl.getContext('2d').drawImage(imgEl, 0, 0, imgEl.width, imgEl.height);
var replacement = fabric.util.createImage(),
retinaScaling = this.canvas ? this.canvas.getRetinaScaling() : fabric.devicePixelRatio,
minimumScale = this.minimumScaleTrigger / retinaScaling,
_this = this, scaleX, scaleY;
if (filters.length === 0) {
this._element = imgElement;
callback && callback();
return canvasEl;
callback && callback(this);
return imgElement;
}
var canvasEl = fabric.util.createCanvasElement();
canvasEl.width = imgElement.width;
canvasEl.height = imgElement.height;
canvasEl.getContext('2d').drawImage(imgElement, 0, 0, imgElement.width, imgElement.height);
filters.forEach(function(filter) {
filter && filter.applyTo(canvasEl, filter.scaleX || _this.scaleX, filter.scaleY || _this.scaleY);
if (forResizing) {
scaleX = _this.scaleX < minimumScale ? _this.scaleX : 1;
scaleY = _this.scaleY < minimumScale ? _this.scaleY : 1;
if (scaleX * retinaScaling < 1) {
scaleX *= retinaScaling;
}
if (scaleY * retinaScaling < 1) {
scaleY *= retinaScaling;
}
}
else {
scaleX = filter.scaleX;
scaleY = filter.scaleY;
}
filter && filter.applyTo(canvasEl, scaleX, scaleY);
if (!forResizing && filter && filter.type === 'Resize') {
_this.width *= filter.scaleX;
_this.height *= filter.scaleY;
@ -380,20 +419,18 @@
/** @ignore */
replacement.width = canvasEl.width;
replacement.height = canvasEl.height;
if (fabric.isLikelyNode) {
replacement.src = canvasEl.toBuffer(undefined, fabric.Image.pngCompression);
// onload doesn't fire in some node versions, so we invoke callback manually
_this._element = replacement;
!forResizing && (_this._filteredEl = replacement);
callback && callback();
_this._element = replacement; // !forResizing && (_this._filteredEl = replacement);
callback && callback(_this);
}
else {
replacement.onload = function() {
_this._element = replacement;
!forResizing && (_this._filteredEl = replacement);
callback && callback();
replacement.onload = canvasEl = imgEl = null;
callback && callback(_this);
replacement.onload = canvasEl = null;
};
replacement.src = canvasEl.toDataURL('image/png');
}
@ -494,8 +531,8 @@
* @param {HTMLImageElement|String} element The element representing the image
* @param {Object} [options] Options object
*/
_initElement: function(element, options) {
this.setElement(fabric.util.getById(element), null, options);
_initElement: function(element, options, callback) {
this.setElement(fabric.util.getById(element), callback, options);
fabric.util.addClass(this.getElement(), fabric.Image.CSS_CANVAS);
},
@ -581,8 +618,7 @@
object.filters = filters || [ ];
fabric.Image.prototype._initFilters.call(object, object.resizeFilters, function(resizeFilters) {
object.resizeFilters = resizeFilters || [ ];
var instance = new fabric.Image(img, object);
callback && callback(instance);
return new fabric.Image(img, object, callback);
});
});
}, null, object.crossOrigin);

View file

@ -207,6 +207,14 @@
return (fabric.devicePixelRatio !== 1 && this.enableRetinaScaling);
},
/**
* @private
* @return {Number} retinaScaling if applied, otherwise 1;
*/
getRetinaScaling: function() {
return this._isRetinaScaling() ? fabric.devicePixelRatio : 1;
},
/**
* @private
*/

View file

@ -12,7 +12,8 @@
var IMG_SRC = fabric.isLikelyNode ? (__dirname + '/../fixtures/test_image.gif') : getAbsolutePath('../fixtures/test_image.gif'),
IMG_WIDTH = 276,
IMG_HEIGHT = 110;
IMG_HEIGHT = 110,
Canvas = require('canvas');
var REFERENCE_IMG_OBJECT = {
'type': 'image',
@ -41,7 +42,7 @@
'backgroundColor': '',
'clipTo': null,
'filters': [],
'resizeFilters': [],
'resizeFilters': [],
'fillRule': 'nonzero',
'globalCompositeOperation': 'source-over',
'skewX': 0,
@ -54,25 +55,32 @@
};
function _createImageElement() {
return fabric.isLikelyNode ? new (require('canvas').Image)() : fabric.document.createElement('img');
return fabric.isLikelyNode ? new Canvas.Image() : fabric.document.createElement('img');
}
function _createImageObject(width, height, callback) {
function _createImageObject(width, height, callback, options) {
var elImage = _createImageElement();
elImage.width = width;
elImage.height = height;
setSrc(elImage, IMG_SRC, function() {
callback(new fabric.Image(elImage));
if (width != elImage.width || height != elImage.height) {
var canvas = new Canvas(width, height);
canvas.getContext('2d').drawImage(elImage, 0, 0, width, height);
elImage._src = canvas.toDataURL();
elImage.src = elImage._src;
return new fabric.Image(elImage, options, callback);
}
else {
return new fabric.Image(elImage, options, callback);
}
});
}
function createImageObject(callback) {
return _createImageObject(IMG_WIDTH, IMG_HEIGHT, callback);
function createImageObject(callback, options) {
return _createImageObject(IMG_WIDTH, IMG_HEIGHT, callback, options);
}
// function createSmallImageObject(callback) {
// return _createImageObject(IMG_WIDTH / 2, IMG_HEIGHT / 2, callback);
// }
function createSmallImageObject(callback, options) {
return _createImageObject(IMG_WIDTH / 2, IMG_HEIGHT / 2, callback, options);
}
function setSrc(img, src, callback) {
if (fabric.isLikelyNode) {
@ -84,8 +92,10 @@
});
}
else {
img.onload = function() {
callback && callback();
};
img.src = src;
callback && callback();
}
}
@ -122,7 +132,6 @@
asyncTest('toObject with no element', function() {
createImageObject(function(image) {
image._originalElement = null;
ok(typeof image.toObject == 'function');
var toObject = image.toObject();
// workaround for node-canvas sometimes producing images with width/height and sometimes not
@ -153,63 +162,38 @@
equal(filterFromObj.scaleX, 0.3);
equal(filterFromObj.scaleY, 0.3);
equal(filterFromObj.resizeType, 'bilinear');
start();
});
start();
});
});
asyncTest('toObject with applied resize filter', function() {
createImageObject(function(image) {
ok(typeof image.toObject == 'function');
var filter = new fabric.Image.filters.Resize({resizeType: 'bilinear', scaleX: 0.5, scaleY: 0.5});
var filter = new fabric.Image.filters.Resize({resizeType: 'bilinear', scaleX: 0.2, scaleY: 0.2});
image.filters.push(filter);
var width = image.width, height = image.height;
ok(image.filters[0] instanceof fabric.Image.filters.Resize, 'should inherit from fabric.Image.filters.Resize');
image.applyFilters(function() {
equal(image.width, width / 2, 'width should be halved now');
equal(image.height, height / 2, 'height should be halved now');
equal(image.width, width / 5, 'width should be a fifth');
equal(image.height, height / 5, 'height should a fifth');
var toObject = image.toObject();
deepEqual(toObject.filters[0], filter.toObject());
equal(toObject.width, width, 'width is stored as before filters');
equal(toObject.height, height, 'height is stored as before filters');
fabric.Image.fromObject(toObject, function(imageFromObject) {
var filterFromObj = imageFromObject.filters[0];
fabric.Image.fromObject(toObject, function(_imageFromObject) {
var filterFromObj = _imageFromObject.filters[0];
ok(filterFromObj instanceof fabric.Image.filters.Resize, 'should inherit from fabric.Image.filters.Resize');
equal(filterFromObj.scaleY, 0.5);
equal(filterFromObj.scaleX, 0.5);
//equal(imageFromObject.width, width, 'on image reload width is halved again');
//equal(imageFromObject.height, height, 'on image reload width is halved again');
equal(filterFromObj.scaleY, 0.2);
equal(filterFromObj.scaleX, 0.2);
equal(_imageFromObject.width, width / 5, 'on image reload width is halved again');
equal(_imageFromObject.height, height / 5, 'on image reload width is halved again');
start();
});
});
});
});
// asyncTest('toObject without default values', function() {
// createImageObject(function(image) {
// image.includeDefaultValues = false;
// var object = image.toObject();
// // workaround for node-canvas sometimes producing images with width/height and sometimes not
// if (object.width === 0) {
// object.width = IMG_WIDTH;
// }
// if (object.height === 0) {
// object.height = IMG_HEIGHT;
// }
// deepEqual(object, {
// type: 'image',
// // why the hell deepEqual fail [] == [] check?!
// filters: [],
// crossOrigin: ''
// });
// start();
// });
// });
asyncTest('toString', function() {
createImageObject(function(image) {
ok(typeof image.toString == 'function');
@ -276,128 +260,106 @@
});
});
// asyncTest('clone', function() {
asyncTest('clone', function() {
createImageObject(function(image) {
ok(typeof image.clone == 'function');
image.clone(function(clone) {
ok(clone instanceof fabric.Image);
deepEqual(clone.toObject(), image.toObject());
start();
});
});
});
asyncTest('cloneWidthHeight', function() {
createSmallImageObject(function(image) {
image.clone(function(clone) {
equal(clone.getElement().width, IMG_WIDTH / 2,
'clone\'s element should have width identical to that of original image');
equal(clone.getElement().height, IMG_HEIGHT / 2,
'clone\'s element should have height identical to that of original image');
start();
});
});
});
asyncTest('fromObject', function() {
ok(typeof fabric.Image.fromObject == 'function');
// should not throw error when no callback is given
var obj = fabric.util.object.extend(fabric.util.object.clone(REFERENCE_IMG_OBJECT), {
src: IMG_SRC
});
fabric.Image.fromObject(obj, function(instance){
ok(instance instanceof fabric.Image);
start();
});
});
asyncTest('fromURL', function() {
ok(typeof fabric.Image.fromURL == 'function');
fabric.Image.fromURL(IMG_SRC, function(instance) {
ok(instance instanceof fabric.Image);
deepEqual(REFERENCE_IMG_OBJECT, instance.toObject());
start();
});
});
asyncTest('fromElement', function() {
function makeImageElement(attributes) {
var element = _createImageElement();
if (fabric.isLikelyNode) {
element.getAttribute = function(x) {
return element[x];
};
element.setAttribute = function(x, value) {
element[x] = value;
};
}
for (var prop in attributes) {
element.setAttribute(prop, attributes[prop]);
}
return element;
}
var IMAGE_DATA_URL = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAARCAYAAADtyJ2fAAAACXBIWXMAAAsSAAALEgHS3X78AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAVBJREFUeNqMU7tOBDEMtENuy614/QE/gZBOuvJK+Et6CiQ6JP6ExxWI7bhL1vgVExYKLPmsTTIzjieHd+MZZSBIAJwEyJU0EWaum+lNljRux3O6nl70Gx/GUwUeyYcDJWZNhMK1aEXYe95Mz4iP44kDTRUZSWSq1YEHri0/HZxXfGSFBN+qDEJTrNI+QXRBviZ7eWCQgjsg+IHiHYB30MhqUxwcmH1Arc2kFDwkBldeFGJLPqs/AbbF2dWgUym6Z2Tb6RVzYxG1wUnmaNcOonZiU0++l6C7FzoQY42g3+8jz+GZ+dWMr1rRH0OjAFhPO+VJFx/vWDqPmk8H97CGBUYUiqAGW0PVe1+aX8j2Ll0tgHtvLx6AK9Tu1ZTFTQ0ojChqGD4qkOzeAuzVfgzsaTym1ClS+IdwtQCFooQMBTumNun1H6Bfcc9/MUn4R3wJMAAZH6MmA4ht4gAAAABJRU5ErkJggg==";
ok(typeof fabric.Image.fromElement == 'function', 'fromElement should exist');
var imageEl = makeImageElement({
width: "14",
height: "17",
"xlink:href": IMAGE_DATA_URL
});
fabric.Image.fromElement(imageEl, function(imgObject) {
ok(imgObject instanceof fabric.Image);
deepEqual(imgObject.get('width'), 14, 'width of an object');
deepEqual(imgObject.get('height'), 17, 'height of an object');
deepEqual(imgObject.getSrc(), IMAGE_DATA_URL, 'src of an object');
start();
});
});
// asyncTest('minimumScale', function() {
// createImageObject(function(image) {
// ok(typeof image.clone == 'function');
// var imageClone = null;
// image.clone(function(clone) {
// imageClone = clone;
// });
// setTimeout(function() {
// ok(imageClone instanceof fabric.Image);
// deepEqual(imageClone.toObject(), image.toObject());
// ok(typeof image.toObject == 'function');
// var filter = new fabric.Image.filters.Resize({resizeType: 'sliceHack', scaleX: 0.2, scaleY: 0.2});
// image.resizeFilters.push(filter);
// var width = image.width, height = image.height;
// ok(image.resizeFilters[0] instanceof fabric.Image.filters.Resize, 'should inherit from fabric.Image.filters.Resize');
// var toObject = image.toObject();
// fabric.Image.fromObject(toObject, function(_imageFromObject) {
// var filterFromObj = _imageFromObject.resizeFilters[0];
// ok(filterFromObj instanceof fabric.Image.filters.Resize, 'should inherit from fabric.Image.filters.Resize');
// equal(filterFromObj.scaleY, 0.2);
// equal(filterFromObj.scaleX, 0.2);
// var canvasEl = _imageFromObject.applyFilters(null, _imageFromObject.resizeFilters, _imageFromObject._originalElement, true);
// start();
// }, 1000);
// });
// });
// });
// asyncTest('cloneWidthHeight', function() {
// var image = createSmallImageObject();
// var imageClone = null;
// image.clone(function(clone) {
// imageClone = clone;
// });
// setTimeout(function() {
// equal(imageClone.getElement().width, IMG_WIDTH / 2,
// 'clone\'s element should have width identical to that of original image');
// equal(imageClone.getElement().height, IMG_HEIGHT / 2,
// 'clone\'s element should have height identical to that of original image');
// start();
// }, 1000);
// });
// asyncTest('fromObject', function() {
// ok(typeof fabric.Image.fromObject == 'function');
// // should not throw error when no callback is given
// var obj = fabric.util.object.extend(fabric.util.object.clone(REFERENCE_IMG_OBJECT), {
// src: IMG_SRC
// });
// fabric.Image.fromObject(obj);
// var image;
// fabric.Image.fromObject(obj, function(instance){
// image = instance;
// });
// setTimeout(function() {
// ok(image instanceof fabric.Image);
// start();
// }, 1000);
// });
// asyncTest('fromURL', function() {
// ok(typeof fabric.Image.fromURL == 'function');
// // should not throw error when no callback is given
// // can't use `assertNothingRaised` due to asynchronous callback
// fabric.Image.fromURL(IMG_SRC);
// var image;
// fabric.Image.fromURL(IMG_SRC, function(instance) {
// image = instance;
// });
// setTimeout(function() {
// ok(image instanceof fabric.Image);
// deepEqual(REFERENCE_IMG_OBJECT, image.toObject());
// start();
// }, 1000);
// });
// test('toGrayscale', function() {
// var image = createImageObject(),
// imageEl = _createImageElement();
// imageEl.src = IMG_SRC;
// image.setElement(imageEl);
// ok(typeof image.toGrayscale == 'function');
// if (!fabric.Canvas.supports('toDataURL')) {
// alert('toDataURL is not supported. Some tests can not be run.');
// }
// else {
// equal(image.toGrayscale(), image, 'chainable');
// }
// });
// asyncTest('fromElement', function() {
// function makeImageElement(attributes) {
// var element = _createImageElement();
// for (var prop in attributes) {
// element.setAttribute(prop, attributes[prop]);
// }
// return element;
// }
// var IMAGE_DATA_URL = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAARCAYAAADtyJ2fAAAACXBIWXMAAAsSAAALEgHS3X78AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAVBJREFUeNqMU7tOBDEMtENuy614/QE/gZBOuvJK+Et6CiQ6JP6ExxWI7bhL1vgVExYKLPmsTTIzjieHd+MZZSBIAJwEyJU0EWaum+lNljRux3O6nl70Gx/GUwUeyYcDJWZNhMK1aEXYe95Mz4iP44kDTRUZSWSq1YEHri0/HZxXfGSFBN+qDEJTrNI+QXRBviZ7eWCQgjsg+IHiHYB30MhqUxwcmH1Arc2kFDwkBldeFGJLPqs/AbbF2dWgUym6Z2Tb6RVzYxG1wUnmaNcOonZiU0++l6C7FzoQY42g3+8jz+GZ+dWMr1rRH0OjAFhPO+VJFx/vWDqPmk8H97CGBUYUiqAGW0PVe1+aX8j2Ll0tgHtvLx6AK9Tu1ZTFTQ0ojChqGD4qkOzeAuzVfgzsaTym1ClS+IdwtQCFooQMBTumNun1H6Bfcc9/MUn4R3wJMAAZH6MmA4ht4gAAAABJRU5ErkJggg==";
// ok(typeof fabric.Image.fromElement == 'function', 'fromElement should exist');
// var imageEl = makeImageElement({
// width: "14",
// height: "17",
// "xlink:href": IMAGE_DATA_URL
// });
// var imgObject;
// fabric.Image.fromElement(imageEl, function(obj) {
// imgObject = obj;
// });
// setTimeout(function() {
// ok(imgObject instanceof fabric.Image);
// deepEqual(imgObject.get('width'), 14, 'width of an object');
// deepEqual(imgObject.get('height'), 17, 'height of an object');
// deepEqual(imgObject.getSrc(), IMAGE_DATA_URL, 'src of an object');
// start();
// }, 500);
// });
})();