      1 //= require "object.class"
      2
      3 (function() {
      4
      5   var global = this,
      6       extend = fabric.util.object.extend;
      7
      8   if (!global.fabric) {
      9     global.fabric = { };
     10   }
     11
     12   if (global.fabric.Image) {
     13     fabric.warn('fabric.Image is already defined.');
     14     return;
     15   };
     16
     17   if (!fabric.Object) {
     18     fabric.warn('fabric.Object is required for fabric.Image initialization');
     19     return;
     20   }
     21
     22   /**
     23    * @class Image
     24    * @extends fabric.Object
     25    */
     26   fabric.Image = fabric.util.createClass(fabric.Object, /** @scope fabric.Image.prototype */ {
     27
     28     /**
     29      * @property
     30      * @type Number
     31      */
     32     maxwidth: null,
     33
     34     /**
     35      * @property
     36      * @type Number
     37      */
     38     maxheight: null,
     39
     40     /**
     41      * @property
     42      * @type Boolean
     43      */
     44     active: false,
     45
     46     /**
     47      * @property
     48      * @type Boolean
     49      */
     50     bordervisibility: false,
     51
     52     /**
     53      * @property
     54      * @type Boolean
     55      */
     56     cornervisibility: false,
     57
     58     /**
     59      * @property
     60      * @type String
     61      */
     62     type: 'image',
     63
     64     __isGrayscaled: false,
     65
     66     /**
     67      * Constructor
     68      * @param {HTMLImageElement | String} element Image element
     69      * @param {Object} options optional
     70      */
     71     initialize: function(element, options) {
     72       this.callSuper('initialize', options);
     73       this._initElement(element);
     74       this._initConfig(options || { });
     75     },
     76
     77     /**
     78      * Returns image element which this instance if based on
     79      * @method getElement
     80      * @return {HTMLImageElement} image element
     81      */
     82     getElement: function() {
     83       return this._element;
     84     },
     85
     86     /**
     87      * Sets image element for this instance to a specified one
     88      * @method setElement
     89      * @param {HTMLImageElement} element
     90      * @return {fabric.Image} thisArg
     91      * @chainable
     92      */
     93     setElement: function(element) {
     94       this._element = element;
     95       return this;
     96     },
     97
     98     /**
     99      * Resizes an image depending on whether maxwidth and maxheight are set up;
    100      * Width and height have to mantain the same proportion in the final image as it was in the initial one.
    101      * @method getNormalizedSize
    102      * @param {Object} oImg
    103      * @param {Number} maxwidth maximum width of the image (in px)
    104      * @param {Number} maxheight maximum height of the image (in px)
    105      */
    106     getNormalizedSize: function(oImg, maxwidth, maxheight) {
    107       if (maxheight && maxwidth && (oImg.width > oImg.height && (oImg.width / oImg.height) < (maxwidth / maxheight))) {
    108         // height is the constraining dimension.
    109         normalizedWidth = ~~((oImg.width * maxheight) / oImg.height);
    110         normalizedHeight = maxheight;
    111       }
    112       else if (maxheight && ((oImg.height == oImg.width) || (oImg.height > oImg.width) || (oImg.height > maxheight))) {
    113         // height is the constraining dimension.
    114         normalizedWidth = ~~((oImg.width * maxheight) / oImg.height);
    115         normalizedHeight = maxheight;
    116       }
    117       else if (maxwidth && (maxwidth < oImg.width)) {
    118         // width is the constraining dimension.
    119         normalizedHeight = ~~((oImg.height * maxwidth) / oImg.width);
    120         normalizedWidth = maxwidth;
    121       }
    122       else {
    123         normalizedWidth = oImg.width;
    124         normalizedHeight = oImg.height;
    125       }
    126
    127       return {
    128         width: normalizedWidth,
    129         height: normalizedHeight
    130       };
    131     },
    132
    133     /**
    134      * Returns original size of an image
    135      * @method getOriginalSize
    136      * @return {Object} object with "width" and "height" properties
    137      */
    138     getOriginalSize: function() {
    139       var element = this.getElement();
    140       return {
    141         width: element.width,
    142         height: element.height
    143       };
    144     },
    145
    146     /**
    147      * Sets border visibility
    148      * @method setBorderVisibility
    149      * @param {Boolean} visible When true, border is set to be visible
    150      */
    151     setBorderVisibility: function(visible) {
    152       this._resetWidthHeight();
    153       this._adjustWidthHeightToBorders(showBorder);
    154       this.setCoords();
    155     },
    156
    157     /**
    158      * Sets corner visibility
    159      * @method setCornersVisibility
    160      * @param {Boolean} visible When true, corners are set to be visible
    161      */
    162     setCornersVisibility: function(visible) {
    163       this.cornervisibility = !!visible;
    164     },
    165
    166     /**
    167      * Renders image on a specified context
    168      * @method render
    169      * @param {CanvasRenderingContext2D} ctx Context to render on
    170      */
    171     render: function(ctx, noTransform) {
    172       ctx.save();
    173       if (!noTransform) {
    174         this.transform(ctx);
    175       }
    176       this._render(ctx);
    177       if (this.active && !noTransform) {
    178         this.drawBorders(ctx);
    179         this.hideCorners || this.drawCorners(ctx);
    180       }
    181       ctx.restore();
    182     },
    183
    184     /**
    185      * Returns object representation of an instance
    186      * @method toObject
    187      * @return {Object} Object representation of an instance
    188      */
    189     toObject: function() {
    190       return extend(this.callSuper('toObject'), {
    191         src: this.getSrc()
    192       });
    193     },
    194
    195     /**
    196      * Returns source of an image
    197      * @method getSrc
    198      * @return {String} Source of an image
    199      */
    200     getSrc: function() {
    201       return this.getElement().src;
    202     },
    203
    204     /**
    205      * Returns string representation of an instance
    206      * @method toString
    207      * @return {String} String representation of an instance
    208      */
    209     toString: function() {
    210       return '#<fabric.Image: { src: "' + this.getSrc() + '" }>';
    211     },
    212
    213     /**
    214      * Returns a clone of an instance
    215      * @mthod clone
    216      * @param {Function} callback Callback is invoked with a clone as a first argument
    217      */
    218     clone: function(callback) {
    219       this.constructor.fromObject(this.toObject(), callback);
    220     },
    221
    222     /**
    223      * Makes image grayscale
    224      * @mthod toGrayscale
    225      * @param {Function} callback
    226      */
    227     toGrayscale: function(callback) {
    228
    229       if (this.__isGrayscaled) {
    230         return;
    231       }
    232
    233       var imgEl = this.getElement(),
    234           canvasEl = document.createElement('canvas'),
    235           replacement = document.createElement('img'),
    236           _this = this;
    237
    238       canvasEl.width = imgEl.width;
    239       canvasEl.height = imgEl.height;
    240
    241       canvasEl.getContext('2d').drawImage(imgEl, 0, 0);
    242       fabric.Element.toGrayscale(canvasEl);
    243
    244       /** @ignore */
    245       replacement.onload = function() {
    246         _this.setElement(replacement);
    247         callback && callback();
    248         replacement.onload = canvasEl = imgEl = imageData = null;
    249       };
    250       replacement.width = imgEl.width;
    251       replacement.height = imgEl.height;
    252
    253       replacement.src = canvasEl.toDataURL('image/png');
    254
    255       this.__isGrayscaled = true;
    256
    257       return this;
    258     },
    259
    260     /**
    261      * @private
    262      */
    263     _render: function(ctx) {
    264       var originalImgSize = this.getOriginalSize();
    265       ctx.drawImage(
    266         this.getElement(),
    267         - originalImgSize.width / 2,
    268         - originalImgSize.height / 2,
    269         originalImgSize.width,
    270         originalImgSize.height
    271       );
    272     },
    273
    274     /**
    275      * @private
    276      */
    277     _adjustWidthHeightToBorders: function(showBorder) {
    278       if (showBorder) {
    279         this.currentBorder = this.borderwidth;
    280         this.width += (2 * this.currentBorder);
    281         this.height += (2 * this.currentBorder);
    282       }
    283       else {
    284         this.currentBorder = 0;
    285       }
    286     },
    287
    288     /**
    289      * @private
    290      */
    291     _resetWidthHeight: function() {
    292       var element = this.getElement();
    293
    294       this.set('width', element.width);
    295       this.set('height', element.height);
    296     },
    297
    298     /**
    299      * The Image class's initialization method. This method is automatically
    300      * called by the constructor.
    301      * @method _initElement
    302      * @param {HTMLImageElement|String} el The element representing the image
    303      */
    304     _initElement: function(element) {
    305       this.setElement(fabric.util.getById(element));
    306       fabric.util.addClass(this.getElement(), fabric.Image.CSS_CANVAS);
    307     },
    308
    309     /**
    310      * @method _initConfig
    311      * @param {Object} options Options object
    312      */
    313     _initConfig: function(options) {
    314       this.setOptions(options);
    315       this._setBorder();
    316       this._setWidthHeight(options);
    317     },
    318
    319     /**
    320      * @private
    321      */
    322     _setBorder: function() {
    323       if (this.bordervisibility) {
    324         this.currentBorder = this.borderwidth;
    325       }
    326       else {
    327         this.currentBorder = 0;
    328       }
    329     },
    330
    331     /**
    332      * @private
    333      */
    334     _setWidthHeight: function(options) {
    335       var sidesBorderWidth = 2 * this.currentBorder;
    336       this.width = (this.getElement().width || 0) + sidesBorderWidth;
    337       this.height = (this.getElement().height || 0) + sidesBorderWidth;
    338     },
    339
    340     /**
    341      * Returns complexity of an instance
    342      * @method complexity
    343      * @return {Number} complexity
    344      */
    345     complexity: function() {
    346       return 1;
    347     }
    348   });
    349
    350   /**
    351    * Default CSS class name for canvas
    352    * @static
    353    * @type String
    354    */
    355   fabric.Image.CSS_CANVAS = "canvas-img";
    356
    357   /**
    358    * Creates an instance of fabric.Image from its object representation
    359    * @static
    360    * @method fromObject
    361    * @param object {Object}
    362    * @param callback {Function} optional
    363    */
    364   fabric.Image.fromObject = function(object, callback) {
    365     var img = document.createElement('img'),
    366         src = object.src;
    367
    368     if (object.width) {
    369       img.width = object.width;
    370     }
    371     if (object.height) {
    372       img.height = object.height;
    373     }
    374
    375     /** @ignore */
    376     img.onload = function() {
    377       if (callback) {
    378         callback(new fabric.Image(img, object));
    379       }
    380       img = img.onload = null;
    381     };
    382     img.src = src;
    383   };
    384
    385   /**
    386    * Creates an instance of fabric.Image from an URL string
    387    * @static
    388    * @method fromURL
    389    * @param {String} url URL to create an image from
    390    * @param {Function} [callback] Callback to invoke when image is created (newly created image is passed as a first argument)
    391    * @param {Object} [imgOptions] Options object
    392    */
    393   fabric.Image.fromURL = function(url, callback, imgOptions) {
    394     var img = document.createElement('img');
    395
    396     /** @ignore */
    397     img.onload = function() {
    398       if (callback) {
    399         callback(new fabric.Image(img, imgOptions));
    400       }
    401       img = img.onload = null;
    402     };
    403     img.src = url;
    404   };
    405 })();