Update fabric.Image.filters.Mask - based on https://github.com/kangax/fabric.js/pull/667 (@aleph1)

[BACK_INCOMPAT] `fabric.Image._initFilters` is now async, add parameter callback
Add `fabric.util.resolveNamespace` - used to get correct klass in `fabric.util.enlivenObjects`
Doc additions
This commit is contained in:
Stefan Kienzle 2013-08-14 18:07:56 +02:00
parent d94d88f14b
commit 901ee7f942
3 changed files with 160 additions and 20 deletions

View file

@ -0,0 +1,114 @@
(function(global) {
"use strict";
var fabric = global.fabric || (global.fabric = { }),
extend = fabric.util.object.extend;
/**
* Mask filter class
* @class fabric.Image.filters.Mask
* @memberOf fabric.Image.filters
* @extends fabric.Image.filters.BaseFilter
*/
fabric.Image.filters.Mask = fabric.util.createClass(fabric.Image.filters.BaseFilter, /** @lends fabric.Image.filters.Mask.prototype */ {
/**
* Filter type
* @param {String} type
* @default
*/
type: 'Mask',
/**
* Constructor
* @memberOf fabric.Image.filters.Mask.prototype
* @param {Object} [options] Options object
*/
initialize: function(options) {
options = options || { };
this.mask = options.mask;
this.channel = [ 0, 1, 2, 3 ].indexOf(options.channel) > -1 ? options.channel : 0;
},
/**
* Applies filter to canvas element
* @param {Object} canvasEl Canvas element to apply filter to
*/
applyTo: function(canvasEl) {
if (!this.mask) return;
var context = canvasEl.getContext('2d'),
imageData = context.getImageData(0, 0, canvasEl.width, canvasEl.height),
data = imageData.data,
maskEl = this.mask.getElement(),
maskCanvasEl = fabric.util.createCanvasElement(),
channel = this.channel,
i,
iLen = imageData.width * imageData.height * 4;
maskCanvasEl.width = maskEl.width;
maskCanvasEl.height = maskEl.height;
maskCanvasEl.getContext('2d').drawImage(maskEl, 0, 0, maskEl.width, maskEl.height);
var maskImageData = maskCanvasEl.getContext('2d').getImageData(0, 0, maskEl.width, maskEl.height),
maskData = maskImageData.data;
for (i = 0; i < iLen; i += 4) {
data[i + 3] = maskData[i + channel];
}
context.putImageData(imageData, 0, 0);
},
/**
* Returns object representation of an instance
* @return {Object} Object representation of an instance
*/
toObject: function() {
return extend(this.callSuper('toObject'), {
mask: this.mask.toObject(),
channel: this.channel
});
}
});
/**
* Returns filter instance from an object representation
* @static
* @param {Object} object Object to create an instance from
* @param {Function} [callback] Callback to invoke when a mask filter instance is created
*/
fabric.Image.filters.Mask.fromObject = function(object, callback) {
var img = fabric.document.createElement('img'),
src = object.mask.src;
/** @ignore */
img.onload = function() {
object.mask = new fabric.Image(img, object.mask);
callback && callback(new fabric.Image.filters.Mask(object));
img = img.onload = img.onerror = null;
};
/** @ignore */
img.onerror = function() {
fabric.log('Error loading ' + img.src);
callback && callback(null, true);
img = img.onload = img.onerror = null;
};
img.src = src;
};
/**
* Indicates that instances of this type are async
* @static
* @type Boolean
* @default
*/
fabric.Image.filters.Mask.async = true;
})(typeof exports !== 'undefined' ? exports : this);

View file

@ -79,7 +79,7 @@
/**
* Returns original size of an image
* @return {Object} object with "width" and "height" properties
* @return {Object} Object with "width" and "height" properties
*/
getOriginalSize: function() {
var element = this.getElement();
@ -181,7 +181,7 @@
/**
* Returns object representation of an instance
* @param {Array} propertiesToInclude
* @param {Array} propertiesToInclude Any properties that you might want to additionally include in the output
* @return {Object} Object representation of an instance
*/
toObject: function(propertiesToInclude) {
@ -252,7 +252,7 @@
/**
* Returns a clone of an instance
* @param {Function} callback Callback is invoked with a clone as a first argument
* @param {Array} propertiesToInclude
* @param {Array} propertiesToInclude Any properties that you might want to additionally include in the output
*/
clone: function(callback, propertiesToInclude) {
this.constructor.fromObject(this.toObject(propertiesToInclude), callback);
@ -313,7 +313,7 @@
/**
* @private
* @param ctx
* @param {CanvasRenderingContext2D} ctx Context to render on
*/
_render: function(ctx) {
ctx.drawImage(
@ -339,7 +339,7 @@
* The Image class's initialization method. This method is automatically
* called by the constructor.
* @private
* @param {HTMLImageElement|String} el The element representing the image
* @param {HTMLImageElement|String} element The element representing the image
*/
_initElement: function(element) {
this.setElement(fabric.util.getById(element));
@ -359,12 +359,13 @@
/**
* @private
* @param {Object} object Object with filters property
* @param {Function} callback Callback to invoke when all fabric.Image.filters instances are created
*/
_initFilters: function(object) {
_initFilters: function(object, callback) {
if (object.filters && object.filters.length) {
this.filters = object.filters.map(function(filterObj) {
return filterObj && fabric.Image.filters[filterObj.type].fromObject(filterObj);
});
fabric.util.enlivenObjects(object.filters, function(enlivenedObjects) {
callback(enlivenedObjects);
}, 'fabric.Image.filters');
}
},
@ -395,6 +396,7 @@
* Default CSS class name for canvas
* @static
* @type String
* @default
*/
fabric.Image.CSS_CANVAS = "canvas-img";
@ -416,11 +418,13 @@
/** @ignore */
img.onload = function() {
fabric.Image.prototype._initFilters.call(object, object);
fabric.Image.prototype._initFilters.call(object, object, function(filters) {
object.filters = filters;
var instance = new fabric.Image(img, object);
callback && callback(instance);
img = img.onload = img.onerror = null;
var instance = new fabric.Image(img, object);
callback && callback(instance);
img = img.onload = img.onerror = null;
});
};
/** @ignore */
@ -481,8 +485,8 @@
/**
* Indicates compression level used when generating PNG under Node (in applyFilters). Any of 0-9
* @static
* @default
* @type Number
* @default
*/
fabric.Image.pngCompression = 1;

View file

@ -105,13 +105,34 @@
}
/**
* Returns klass "Class" object of given fabric.Object type
* Returns klass "Class" object of given namespace
* @memberOf fabric.util
* @param {String} type Type of object (eg. 'circle')
* @param {String} namespace Namespace to get klass "Class" object from
* @return {Object} klass "Class"
*/
function getKlass(type) {
return fabric[fabric.util.string.camelize(fabric.util.string.capitalize(type))];
function getKlass(type, namespace) {
return resolveNamespace(namespace)[fabric.util.string.camelize(fabric.util.string.capitalize(type))];
}
/**
* Returns object of given namespace
* @memberOf fabric.util
* @param {String} namespace Namespace string e.g. 'fabric.Image.filter' or 'fabric'
* @return {Object} Object for given namespace (default fabric)
*/
function resolveNamespace(namespace) {
namespace = namespace || 'fabric';
var parts = namespace.split('.'),
len = parts.length,
obj = fabric.window;
for (var i = 0; i < len; ++i) {
obj = obj[parts[i]];
}
return obj;
}
/**
@ -143,7 +164,7 @@
* @param {Array} objects Objects to enliven
* @param {Function} callback Callback to invoke when all objects are created
*/
function enlivenObjects(objects, callback) {
function enlivenObjects(objects, callback, namespace) {
function onLoaded() {
if (++numLoadedObjects === numTotalObjects) {
@ -161,7 +182,7 @@
if (!o.type) {
return;
}
var klass = fabric.util.getKlass(o.type);
var klass = fabric.util.getKlass(o.type, namespace);
if (klass.async) {
klass.fromObject(o, function (o, error) {
if (!error) {
@ -278,7 +299,7 @@
* Creates image element (works on client and node)
* @static
* @memberOf fabric.util
* @return {Image} image element
* @return {HTMLImageElement} HTML image element
*/
function createImage() {
return fabric.isLikelyNode
@ -495,6 +516,7 @@
fabric.util.getRandomInt = getRandomInt;
fabric.util.falseFunction = falseFunction;
fabric.util.getKlass = getKlass;
fabric.util.resolveNamespace = resolveNamespace;
fabric.util.loadImage = loadImage;
fabric.util.enlivenObjects = enlivenObjects;
fabric.util.groupSVGElements = groupSVGElements;