(function() {
// CONSTANTS
var IMAGE = 'Image',
CROP = 'crop',
SET = 'set';
/**
* Image constructor
* @constructor
* @memberof Kinetic
* @augments Kinetic.Shape
* @param {Object} config
* @param {ImageObject} config.image
* @param {Object} [config.crop]
* @@shapeParams
* @@nodeParams
* @example
* var imageObj = new Image();
* imageObj.onload = function() {
* var image = new Kinetic.Image({
* x: 200,
* y: 50,
* image: imageObj,
* width: 100,
* height: 100
* });
* };
* imageObj.src = '/path/to/image.jpg'
*/
Kinetic.Image = function(config) {
this.___init(config);
};
Kinetic.Image.prototype = {
___init: function(config) {
var that = this;
// call super constructor
Kinetic.Shape.call(this, config);
this.className = IMAGE;
},
drawFunc: function(context) {
var width = this.getWidth(),
height = this.getHeight(),
params,
that = this,
cropX = this.getCropX() || 0,
cropY = this.getCropY() || 0,
cropWidth = this.getCropWidth(),
cropHeight = this.getCropHeight(),
image;
//TODO: this logic needs to hook int othe new caching system
// if a filter is set, and the filter needs to be updated, reapply
if (this.getFilter() && this._applyFilter) {
this.applyFilter();
this._applyFilter = false;
}
// NOTE: this.filterCanvas may be set by the above code block
if (this.filterCanvas) {
image = this.filterCanvas._canvas;
}
else {
image = this.getImage();
}
context.beginPath();
context.rect(0, 0, width, height);
context.closePath();
context.fillStrokeShape(this);
if(image) {
// if cropping
if(cropWidth && cropHeight) {
params = [image, cropX, cropY, cropWidth, cropHeight, 0, 0, width, height];
}
// no cropping
else {
params = [image, 0, 0, width, height];
}
context.drawImage.apply(context, params);
}
},
drawHitFunc: function(context) {
var width = this.getWidth(),
height = this.getHeight(),
imageHitRegion = this.imageHitRegion;
if(imageHitRegion) {
context.drawImage(imageHitRegion, 0, 0, width, height);
context.beginPath();
context.rect(0, 0, width, height);
context.closePath();
context.strokeShape(this);
}
else {
context.beginPath();
context.rect(0, 0, width, height);
context.closePath();
context.fillStrokeShape(this);
}
},
applyFilter: function() {
var image = this.getImage(),
that = this,
width = this.getWidth(),
height = this.getHeight(),
filter = this.getFilter(),
filterCanvas, context, imageData;
if (this.filterCanvas){
filterCanvas = this.filterCanvas;
filterCanvas.getContext().clear();
}
else {
filterCanvas = this.filterCanvas = new Kinetic.SceneCanvas({
width: width,
height: height,
pixelRatio: 1
});
}
context = filterCanvas.getContext();
try {
context.drawImage(image, 0, 0, filterCanvas.getWidth(), filterCanvas.getHeight());
imageData = context.getImageData(0, 0, filterCanvas.getWidth(), filterCanvas.getHeight());
filter.call(this, imageData);
context.putImageData(imageData, 0, 0);
}
catch(e) {
this.clearFilter();
Kinetic.Util.warn('Unable to apply filter. ' + e.message);
}
},
/**
* clear filter
* @method
* @memberof Kinetic.Image.prototype
*/
clearFilter: function() {
this.filterCanvas = null;
this._applyFilter = false;
},
/**
* create image hit region which enables more accurate hit detection mapping of the image
* by avoiding event detections for transparent pixels
* @method
* @memberof Kinetic.Image.prototype
* @param {Function} [callback] callback function to be called once
* the image hit region has been created
* @example
* image.createImageHitRegion(function() {
* layer.drawHit();
* });
*/
createImageHitRegion: function(callback) {
var that = this,
width = this.getWidth(),
height = this.getHeight(),
canvas = new Kinetic.SceneCanvas({
width: width,
height: height
}),
_context = canvas.getContext()._context,
image = this.getImage(),
imageData, data, rgbColorKey, i, len;
_context.drawImage(image, 0, 0);
try {
imageData = _context.getImageData(0, 0, width, height);
data = imageData.data;
len = data.length;
rgbColorKey = Kinetic.Util._hexToRgb(this.colorKey);
// replace non transparent pixels with color key
for(i = 0; i < len; i += 4) {
if (data[i + 3] > 0) {
data[i] = rgbColorKey.r;
data[i + 1] = rgbColorKey.g;
data[i + 2] = rgbColorKey.b;
}
}
Kinetic.Util._getImage(imageData, function(imageObj) {
that.imageHitRegion = imageObj;
if(callback) {
callback();
}
});
}
catch(e) {
Kinetic.Util.warn('Unable to create image hit region. ' + e.message);
}
},
/**
* clear image hit region
* @method
* @memberof Kinetic.Image.prototype
*/
clearImageHitRegion: function() {
delete this.imageHitRegion;
},
getWidth: function() {
var image = this.getImage();
return this.attrs.width || (image ? image.width : 0);
},
getHeight: function() {
var image = this.getImage();
return this.attrs.height || (image ? image.height : 0);
}
};
Kinetic.Util.extend(Kinetic.Image, Kinetic.Shape);
Kinetic.Factory.addFilterGetterSetter = function(constructor, attr, def) {
this.addGetter(constructor, attr, def);
this.addFilterSetter(constructor, attr);
};
Kinetic.Factory.addFilterSetter = function(constructor, attr) {
var that = this,
method = SET + Kinetic.Util._capitalize(attr);
constructor.prototype[method] = function(val) {
this._setAttr(attr, val);
this._applyFilter = true;
};
};
// add getters setters
Kinetic.Factory.addGetterSetter(Kinetic.Image, 'image');
/**
* set image
* @name setImage
* @method
* @memberof Kinetic.Image.prototype
* @param {ImageObject} image
*/
/**
* get image
* @name getImage
* @method
* @memberof Kinetic.Image.prototype
* @returns {ImageObject}
*/
Kinetic.Factory.addBoxGetterSetter(Kinetic.Image, 'crop');
/**
* set crop
* @method
* @name setCrop
* @memberof Kinetic.Image.prototype
* @param {Object|Array}
* @example
* // set crop x, y, width and height with an array
* image.setCrop([20, 20, 100, 100]);
*
* // set crop x, y, width and height with an object
* image.setCrop({
* x: 20,
* y: 20,
* width: 20,
* height: 20
* });
*/
/**
* set cropX
* @method
* @name setCropX
* @memberof Kinetic.Image.prototype
* @param {Number} x
*/
/**
* set cropY
* @name setCropY
* @method
* @memberof Kinetic.Image.prototype
* @param {Number} y
*/
/**
* set cropWidth
* @name setCropWidth
* @method
* @memberof Kinetic.Image.prototype
* @param {Number} width
*/
/**
* set cropHeight
* @name setCropHeight
* @method
* @memberof Kinetic.Image.prototype
* @param {Number} height
*/
/**
* get crop
* @name getCrop
* @method
* @memberof Kinetic.Image.prototype
* @returns {Object}
*/
/**
* get crop x
* @name getCropX
* @method
* @memberof Kinetic.Image.prototype
* @returns {Number}
*/
/**
* get crop y
* @name getCropY
* @method
* @memberof Kinetic.Image.prototype
* @returns {Number}
*/
/**
* get crop width
* @name getCropWidth
* @method
* @memberof Kinetic.Image.prototype
* @returns {Number}
*/
/**
* get crop height
* @name getCropHeight
* @method
* @memberof Kinetic.Image.prototype
* @returns {Number}
*/
Kinetic.Factory.addFilterGetterSetter(Kinetic.Image, 'filter');
/**
* set filter
* @name setFilter
* @method
* @memberof Kinetic.Image.prototype
* @param {Function} filter
*/
/**
* get filter
* @name getFilter
* @method
* @memberof Kinetic.Image.prototype
* @returns {Function}
*/
})();