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 })();