fabric-rails 1.0.12 → 1.0.12.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG.md +16 -0
- data/README.md +1 -1
- data/lib/fabric/rails/version.rb +2 -2
- data/vendor/assets/javascripts/event.js +1909 -0
- data/vendor/assets/javascripts/fabric.js +64 -16464
- data/vendor/assets/javascripts/fabric/HEADER.js +31 -0
- data/vendor/assets/javascripts/fabric/canvas.class.js +1007 -0
- data/vendor/assets/javascripts/fabric/canvas_animation.mixin.js +113 -0
- data/vendor/assets/javascripts/fabric/canvas_events.mixin.js +482 -0
- data/vendor/assets/javascripts/fabric/canvas_gestures.mixin.js +79 -0
- data/vendor/assets/javascripts/fabric/canvas_serialization.mixin.js +311 -0
- data/vendor/assets/javascripts/fabric/circle.class.js +182 -0
- data/vendor/assets/javascripts/fabric/color.class.js +284 -0
- data/vendor/assets/javascripts/fabric/ellipse.class.js +169 -0
- data/vendor/assets/javascripts/fabric/freedrawing.class.js +256 -0
- data/vendor/assets/javascripts/fabric/gradient.class.js +211 -0
- data/vendor/assets/javascripts/fabric/group.class.js +556 -0
- data/vendor/assets/javascripts/fabric/image.class.js +418 -0
- data/vendor/assets/javascripts/fabric/image_filters.js +809 -0
- data/vendor/assets/javascripts/fabric/intersection.class.js +178 -0
- data/vendor/assets/javascripts/fabric/line.class.js +188 -0
- data/vendor/assets/javascripts/fabric/log.js +26 -0
- data/vendor/assets/javascripts/fabric/node.js +149 -0
- data/vendor/assets/javascripts/fabric/object.class.js +1068 -0
- data/vendor/assets/javascripts/fabric/object_geometry.mixin.js +308 -0
- data/vendor/assets/javascripts/fabric/object_interactivity.mixin.js +496 -0
- data/vendor/assets/javascripts/fabric/object_origin.mixin.js +207 -0
- data/vendor/assets/javascripts/fabric/object_straightening.mixin.js +94 -0
- data/vendor/assets/javascripts/fabric/observable.mixin.js +91 -0
- data/vendor/assets/javascripts/fabric/parser.js +750 -0
- data/vendor/assets/javascripts/fabric/path.class.js +794 -0
- data/vendor/assets/javascripts/fabric/path_group.class.js +240 -0
- data/vendor/assets/javascripts/fabric/pattern.class.js +69 -0
- data/vendor/assets/javascripts/fabric/point.class.js +327 -0
- data/vendor/assets/javascripts/fabric/polygon.class.js +184 -0
- data/vendor/assets/javascripts/fabric/polyline.class.js +157 -0
- data/vendor/assets/javascripts/fabric/rect.class.js +298 -0
- data/vendor/assets/javascripts/fabric/scout.js +45 -0
- data/vendor/assets/javascripts/fabric/shadow.class.js +70 -0
- data/vendor/assets/javascripts/fabric/stateful.js +88 -0
- data/vendor/assets/javascripts/fabric/static_canvas.class.js +1298 -0
- data/vendor/assets/javascripts/fabric/text.class.js +934 -0
- data/vendor/assets/javascripts/fabric/triangle.class.js +108 -0
- data/vendor/assets/javascripts/fabric/util/anim_ease.js +360 -0
- data/vendor/assets/javascripts/fabric/util/dom_event.js +237 -0
- data/vendor/assets/javascripts/fabric/util/dom_misc.js +245 -0
- data/vendor/assets/javascripts/fabric/util/dom_request.js +72 -0
- data/vendor/assets/javascripts/fabric/util/dom_style.js +71 -0
- data/vendor/assets/javascripts/fabric/util/lang_array.js +250 -0
- data/vendor/assets/javascripts/fabric/util/lang_class.js +97 -0
- data/vendor/assets/javascripts/fabric/util/lang_function.js +35 -0
- data/vendor/assets/javascripts/fabric/util/lang_object.js +34 -0
- data/vendor/assets/javascripts/fabric/util/lang_string.js +60 -0
- data/vendor/assets/javascripts/fabric/util/misc.js +406 -0
- data/vendor/assets/javascripts/json2.js +491 -0
- metadata +53 -2
@@ -0,0 +1,418 @@
|
|
1
|
+
(function(global) {
|
2
|
+
|
3
|
+
"use strict";
|
4
|
+
|
5
|
+
var extend = fabric.util.object.extend;
|
6
|
+
|
7
|
+
if (!global.fabric) {
|
8
|
+
global.fabric = { };
|
9
|
+
}
|
10
|
+
|
11
|
+
if (global.fabric.Image) {
|
12
|
+
fabric.warn('fabric.Image is already defined.');
|
13
|
+
return;
|
14
|
+
}
|
15
|
+
|
16
|
+
/**
|
17
|
+
* Image class
|
18
|
+
* @class Image
|
19
|
+
* @extends fabric.Object
|
20
|
+
*/
|
21
|
+
fabric.Image = fabric.util.createClass(fabric.Object, /** @scope fabric.Image.prototype */ {
|
22
|
+
|
23
|
+
/**
|
24
|
+
* Type of an object
|
25
|
+
* @property
|
26
|
+
* @type String
|
27
|
+
*/
|
28
|
+
type: 'image',
|
29
|
+
|
30
|
+
/**
|
31
|
+
* Constructor
|
32
|
+
* @param {HTMLImageElement | String} element Image element
|
33
|
+
* @param {Object} [options] Options object
|
34
|
+
* @return {fabric.Image}
|
35
|
+
*/
|
36
|
+
initialize: function(element, options) {
|
37
|
+
options || (options = { });
|
38
|
+
|
39
|
+
this.callSuper('initialize', options);
|
40
|
+
this._initElement(element);
|
41
|
+
this._originalImage = this.getElement();
|
42
|
+
this._initConfig(options);
|
43
|
+
|
44
|
+
this.filters = [ ];
|
45
|
+
|
46
|
+
if (options.filters) {
|
47
|
+
this.filters = options.filters;
|
48
|
+
this.applyFilters();
|
49
|
+
}
|
50
|
+
},
|
51
|
+
|
52
|
+
/**
|
53
|
+
* Returns image element which this instance if based on
|
54
|
+
* @method getElement
|
55
|
+
* @return {HTMLImageElement} image element
|
56
|
+
*/
|
57
|
+
getElement: function() {
|
58
|
+
return this._element;
|
59
|
+
},
|
60
|
+
|
61
|
+
/**
|
62
|
+
* Sets image element for this instance to a specified one
|
63
|
+
* @method setElement
|
64
|
+
* @param {HTMLImageElement} element
|
65
|
+
* @return {fabric.Image} thisArg
|
66
|
+
* @chainable
|
67
|
+
*/
|
68
|
+
setElement: function(element) {
|
69
|
+
this._element = element;
|
70
|
+
this._initConfig();
|
71
|
+
return this;
|
72
|
+
},
|
73
|
+
|
74
|
+
/**
|
75
|
+
* Returns original size of an image
|
76
|
+
* @method getOriginalSize
|
77
|
+
* @return {Object} object with "width" and "height" properties
|
78
|
+
*/
|
79
|
+
getOriginalSize: function() {
|
80
|
+
var element = this.getElement();
|
81
|
+
return {
|
82
|
+
width: element.width,
|
83
|
+
height: element.height
|
84
|
+
};
|
85
|
+
},
|
86
|
+
|
87
|
+
/**
|
88
|
+
* Renders image on a specified context
|
89
|
+
* @method render
|
90
|
+
* @param {CanvasRenderingContext2D} ctx Context to render on
|
91
|
+
*/
|
92
|
+
render: function(ctx, noTransform) {
|
93
|
+
ctx.save();
|
94
|
+
var m = this.transformMatrix;
|
95
|
+
// this._resetWidthHeight();
|
96
|
+
if (this.group) {
|
97
|
+
ctx.translate(-this.group.width/2 + this.width/2, -this.group.height/2 + this.height/2);
|
98
|
+
}
|
99
|
+
if (m) {
|
100
|
+
ctx.transform(m[0], m[1], m[2], m[3], m[4], m[5]);
|
101
|
+
}
|
102
|
+
if (!noTransform) {
|
103
|
+
this.transform(ctx);
|
104
|
+
}
|
105
|
+
|
106
|
+
this._setShadow(ctx);
|
107
|
+
this._render(ctx);
|
108
|
+
this._removeShadow(ctx);
|
109
|
+
|
110
|
+
if (this.active && !noTransform) {
|
111
|
+
this.drawBorders(ctx);
|
112
|
+
this.drawControls(ctx);
|
113
|
+
}
|
114
|
+
ctx.restore();
|
115
|
+
},
|
116
|
+
|
117
|
+
/**
|
118
|
+
* Returns object representation of an instance
|
119
|
+
* @method toObject
|
120
|
+
* @param {Array} propertiesToInclude
|
121
|
+
* @return {Object} object representation of an instance
|
122
|
+
*/
|
123
|
+
toObject: function(propertiesToInclude) {
|
124
|
+
return extend(this.callSuper('toObject', propertiesToInclude), {
|
125
|
+
src: this._originalImage.src || this._originalImage._src,
|
126
|
+
filters: this.filters.concat()
|
127
|
+
});
|
128
|
+
},
|
129
|
+
|
130
|
+
/**
|
131
|
+
* Returns SVG representation of an instance
|
132
|
+
* @method toSVG
|
133
|
+
* @return {String} svg representation of an instance
|
134
|
+
*/
|
135
|
+
toSVG: function() {
|
136
|
+
return '<g transform="' + this.getSvgTransform() + '">'+
|
137
|
+
'<image xlink:href="' + this.getSvgSrc() + '" '+
|
138
|
+
'style="' + this.getSvgStyles() + '" ' +
|
139
|
+
// we're essentially moving origin of transformation from top/left corner to the center of the shape
|
140
|
+
// by wrapping it in container <g> element with actual transformation, then offsetting object to the top/left
|
141
|
+
// so that object's center aligns with container's left/top
|
142
|
+
'transform="translate('+ (-this.width/2) + ' ' + (-this.height/2) + ')" ' +
|
143
|
+
'width="' + this.width + '" ' +
|
144
|
+
'height="' + this.height + '"' + '></image>' +
|
145
|
+
'</g>';
|
146
|
+
},
|
147
|
+
|
148
|
+
/**
|
149
|
+
* Returns source of an image
|
150
|
+
* @method getSrc
|
151
|
+
* @return {String} Source of an image
|
152
|
+
*/
|
153
|
+
getSrc: function() {
|
154
|
+
return this.getElement().src || this.getElement()._src;
|
155
|
+
},
|
156
|
+
|
157
|
+
/**
|
158
|
+
* Returns string representation of an instance
|
159
|
+
* @method toString
|
160
|
+
* @return {String} String representation of an instance
|
161
|
+
*/
|
162
|
+
toString: function() {
|
163
|
+
return '#<fabric.Image: { src: "' + this.getSrc() + '" }>';
|
164
|
+
},
|
165
|
+
|
166
|
+
/**
|
167
|
+
* Returns a clone of an instance
|
168
|
+
* @method clone
|
169
|
+
* @param {Array} propertiesToInclude
|
170
|
+
* @param {Function} callback Callback is invoked with a clone as a first argument
|
171
|
+
*/
|
172
|
+
clone: function(callback, propertiesToInclude) {
|
173
|
+
this.constructor.fromObject(this.toObject(propertiesToInclude), callback);
|
174
|
+
},
|
175
|
+
|
176
|
+
/**
|
177
|
+
* Applies filters assigned to this image (from "filters" array)
|
178
|
+
* @mthod applyFilters
|
179
|
+
* @param {Function} callback Callback is invoked when all filters have been applied and new image is generated
|
180
|
+
* @return {fabric.Image} thisArg
|
181
|
+
* @chainable
|
182
|
+
*/
|
183
|
+
applyFilters: function(callback) {
|
184
|
+
|
185
|
+
if (this.filters.length === 0) {
|
186
|
+
this.setElement(this._originalImage);
|
187
|
+
callback && callback();
|
188
|
+
return;
|
189
|
+
}
|
190
|
+
|
191
|
+
var isLikelyNode = typeof Buffer !== 'undefined' && typeof window === 'undefined',
|
192
|
+
imgEl = this._originalImage,
|
193
|
+
canvasEl = fabric.util.createCanvasElement(),
|
194
|
+
replacement = isLikelyNode ? new (require('canvas').Image)() : fabric.document.createElement('img'),
|
195
|
+
_this = this;
|
196
|
+
|
197
|
+
canvasEl.width = imgEl.width;
|
198
|
+
canvasEl.height = imgEl.height;
|
199
|
+
|
200
|
+
canvasEl.getContext('2d').drawImage(imgEl, 0, 0, imgEl.width, imgEl.height);
|
201
|
+
|
202
|
+
this.filters.forEach(function(filter) {
|
203
|
+
filter && filter.applyTo(canvasEl);
|
204
|
+
});
|
205
|
+
|
206
|
+
/** @ignore */
|
207
|
+
replacement.onload = function() {
|
208
|
+
_this._element = replacement;
|
209
|
+
callback && callback();
|
210
|
+
replacement.onload = canvasEl = imgEl = null;
|
211
|
+
};
|
212
|
+
replacement.width = imgEl.width;
|
213
|
+
replacement.height = imgEl.height;
|
214
|
+
|
215
|
+
if (isLikelyNode) {
|
216
|
+
// cut off data:image/png;base64, part in the beginning
|
217
|
+
var base64str = canvasEl.toDataURL('image/png').substring(22);
|
218
|
+
replacement.src = new Buffer(base64str, 'base64');
|
219
|
+
_this._element = replacement;
|
220
|
+
|
221
|
+
// onload doesn't fire in node, so we invoke callback manually
|
222
|
+
callback && callback();
|
223
|
+
}
|
224
|
+
else {
|
225
|
+
replacement.src = canvasEl.toDataURL('image/png');
|
226
|
+
}
|
227
|
+
|
228
|
+
return this;
|
229
|
+
},
|
230
|
+
|
231
|
+
/**
|
232
|
+
* @private
|
233
|
+
* @method _render
|
234
|
+
* @param ctx
|
235
|
+
*/
|
236
|
+
_render: function(ctx) {
|
237
|
+
ctx.drawImage(
|
238
|
+
this._element,
|
239
|
+
-this.width / 2,
|
240
|
+
-this.height / 2,
|
241
|
+
this.width,
|
242
|
+
this.height
|
243
|
+
);
|
244
|
+
},
|
245
|
+
|
246
|
+
/**
|
247
|
+
* @private
|
248
|
+
* @method _resetWidthHeight
|
249
|
+
*/
|
250
|
+
_resetWidthHeight: function() {
|
251
|
+
var element = this.getElement();
|
252
|
+
|
253
|
+
this.set('width', element.width);
|
254
|
+
this.set('height', element.height);
|
255
|
+
},
|
256
|
+
|
257
|
+
/**
|
258
|
+
* The Image class's initialization method. This method is automatically
|
259
|
+
* called by the constructor.
|
260
|
+
* @private
|
261
|
+
* @method _initElement
|
262
|
+
* @param {HTMLImageElement|String} el The element representing the image
|
263
|
+
*/
|
264
|
+
_initElement: function(element) {
|
265
|
+
this.setElement(fabric.util.getById(element));
|
266
|
+
fabric.util.addClass(this.getElement(), fabric.Image.CSS_CANVAS);
|
267
|
+
},
|
268
|
+
|
269
|
+
/**
|
270
|
+
* @private
|
271
|
+
* @method _initConfig
|
272
|
+
* @param {Object} [options] Options object
|
273
|
+
*/
|
274
|
+
_initConfig: function(options) {
|
275
|
+
options || (options = { });
|
276
|
+
this.setOptions(options);
|
277
|
+
this._setWidthHeight(options);
|
278
|
+
},
|
279
|
+
|
280
|
+
/**
|
281
|
+
* @private
|
282
|
+
* @method _initFilters
|
283
|
+
* @param {Object} object Object with filters property
|
284
|
+
*/
|
285
|
+
_initFilters: function(object) {
|
286
|
+
if (object.filters && object.filters.length) {
|
287
|
+
this.filters = object.filters.map(function(filterObj) {
|
288
|
+
return filterObj && fabric.Image.filters[filterObj.type].fromObject(filterObj);
|
289
|
+
});
|
290
|
+
}
|
291
|
+
},
|
292
|
+
|
293
|
+
/**
|
294
|
+
* @private
|
295
|
+
* @method _setWidthHeight
|
296
|
+
* @param {Object} [options] Object with width/height properties
|
297
|
+
*/
|
298
|
+
_setWidthHeight: function(options) {
|
299
|
+
this.width = 'width' in options
|
300
|
+
? options.width
|
301
|
+
: (this.getElement().width || 0);
|
302
|
+
|
303
|
+
this.height = 'height' in options
|
304
|
+
? options.height
|
305
|
+
: (this.getElement().height || 0);
|
306
|
+
},
|
307
|
+
|
308
|
+
/**
|
309
|
+
* Returns complexity of an instance
|
310
|
+
* @method complexity
|
311
|
+
* @return {Number} complexity
|
312
|
+
*/
|
313
|
+
complexity: function() {
|
314
|
+
return 1;
|
315
|
+
}
|
316
|
+
});
|
317
|
+
|
318
|
+
/**
|
319
|
+
* Default CSS class name for canvas
|
320
|
+
* @static
|
321
|
+
* @type String
|
322
|
+
*/
|
323
|
+
fabric.Image.CSS_CANVAS = "canvas-img";
|
324
|
+
|
325
|
+
/**
|
326
|
+
* Alias for getSrc
|
327
|
+
* @static
|
328
|
+
* @method getSvgSrc
|
329
|
+
*/
|
330
|
+
fabric.Image.prototype.getSvgSrc = fabric.Image.prototype.getSrc;
|
331
|
+
|
332
|
+
/**
|
333
|
+
* Creates an instance of fabric.Image from its object representation
|
334
|
+
* @static
|
335
|
+
* @method fromObject
|
336
|
+
* @param {Object} object
|
337
|
+
* @param {Function} [callback] Callback to invoke when an image instance is created
|
338
|
+
*/
|
339
|
+
fabric.Image.fromObject = function(object, callback) {
|
340
|
+
var img = fabric.document.createElement('img'),
|
341
|
+
src = object.src;
|
342
|
+
|
343
|
+
if (object.width) {
|
344
|
+
img.width = object.width;
|
345
|
+
}
|
346
|
+
if (object.height) {
|
347
|
+
img.height = object.height;
|
348
|
+
}
|
349
|
+
|
350
|
+
/** @ignore */
|
351
|
+
img.onload = function() {
|
352
|
+
fabric.Image.prototype._initFilters.call(object, object);
|
353
|
+
|
354
|
+
var instance = new fabric.Image(img, object);
|
355
|
+
callback && callback(instance);
|
356
|
+
img = img.onload = img.onerror = null;
|
357
|
+
};
|
358
|
+
|
359
|
+
/** @ignore */
|
360
|
+
img.onerror = function() {
|
361
|
+
fabric.log('Error loading ' + img.src);
|
362
|
+
callback && callback(null, true);
|
363
|
+
img = img.onload = img.onerror = null;
|
364
|
+
};
|
365
|
+
img.src = src;
|
366
|
+
};
|
367
|
+
|
368
|
+
/**
|
369
|
+
* Creates an instance of fabric.Image from an URL string
|
370
|
+
* @static
|
371
|
+
* @method fromURL
|
372
|
+
* @param {String} url URL to create an image from
|
373
|
+
* @param {Function} [callback] Callback to invoke when image is created (newly created image is passed as a first argument)
|
374
|
+
* @param {Object} [imgOptions] Options object
|
375
|
+
*/
|
376
|
+
fabric.Image.fromURL = function(url, callback, imgOptions) {
|
377
|
+
var img = fabric.document.createElement('img');
|
378
|
+
|
379
|
+
/** @ignore */
|
380
|
+
img.onload = function() {
|
381
|
+
if (callback) {
|
382
|
+
callback(new fabric.Image(img, imgOptions));
|
383
|
+
}
|
384
|
+
img = img.onload = null;
|
385
|
+
};
|
386
|
+
img.src = url;
|
387
|
+
};
|
388
|
+
|
389
|
+
/**
|
390
|
+
* List of attribute names to account for when parsing SVG element (used by {@link fabric.Image.fromElement})
|
391
|
+
* @static
|
392
|
+
* @see http://www.w3.org/TR/SVG/struct.html#ImageElement
|
393
|
+
*/
|
394
|
+
fabric.Image.ATTRIBUTE_NAMES = 'x y width height fill fill-opacity opacity stroke stroke-width transform xlink:href'.split(' ');
|
395
|
+
|
396
|
+
/**
|
397
|
+
* Returns {@link fabric.Image} instance from an SVG element
|
398
|
+
* @static
|
399
|
+
* @method fabric.Image.fromElement
|
400
|
+
* @param {SVGElement} element Element to parse
|
401
|
+
* @param {Function} callback Callback to execute when fabric.Image object is created
|
402
|
+
* @param {Object} [options] Options object
|
403
|
+
* @return {fabric.Image}
|
404
|
+
*/
|
405
|
+
fabric.Image.fromElement = function(element, callback, options) {
|
406
|
+
var parsedAttributes = fabric.parseAttributes(element, fabric.Image.ATTRIBUTE_NAMES);
|
407
|
+
|
408
|
+
fabric.Image.fromURL(parsedAttributes['xlink:href'], callback, extend((options ? fabric.util.object.clone(options) : { }), parsedAttributes));
|
409
|
+
};
|
410
|
+
|
411
|
+
/**
|
412
|
+
* Indicates that instances of this type are async
|
413
|
+
* @static
|
414
|
+
* @type Boolean
|
415
|
+
*/
|
416
|
+
fabric.Image.async = true;
|
417
|
+
|
418
|
+
})(typeof exports !== 'undefined' ? exports : this);
|
@@ -0,0 +1,809 @@
|
|
1
|
+
/**
|
2
|
+
* @namespace
|
3
|
+
*/
|
4
|
+
fabric.Image.filters = { };
|
5
|
+
|
6
|
+
/**
|
7
|
+
* Grayscale image filter class
|
8
|
+
* @class fabric.Image.filters.Grayscale
|
9
|
+
* @memberOf fabric.Image.filters
|
10
|
+
*/
|
11
|
+
fabric.Image.filters.Grayscale = fabric.util.createClass( /** @scope fabric.Image.filters.Grayscale.prototype */ {
|
12
|
+
|
13
|
+
/**
|
14
|
+
* Filter type
|
15
|
+
* @param {String} type
|
16
|
+
*/
|
17
|
+
type: "Grayscale",
|
18
|
+
|
19
|
+
/**
|
20
|
+
* Applies filter to canvas element
|
21
|
+
* @method applyTo
|
22
|
+
* @memberOf fabric.Image.filters.Grayscale.prototype
|
23
|
+
* @param {Object} canvasEl Canvas element to apply filter to
|
24
|
+
*/
|
25
|
+
applyTo: function(canvasEl) {
|
26
|
+
var context = canvasEl.getContext('2d'),
|
27
|
+
imageData = context.getImageData(0, 0, canvasEl.width, canvasEl.height),
|
28
|
+
data = imageData.data,
|
29
|
+
iLen = imageData.width,
|
30
|
+
jLen = imageData.height,
|
31
|
+
index, average, i, j;
|
32
|
+
|
33
|
+
for (i = 0; i < iLen; i++) {
|
34
|
+
for (j = 0; j < jLen; j++) {
|
35
|
+
|
36
|
+
index = (i * 4) * jLen + (j * 4);
|
37
|
+
average = (data[index] + data[index + 1] + data[index + 2]) / 3;
|
38
|
+
|
39
|
+
data[index] = average;
|
40
|
+
data[index + 1] = average;
|
41
|
+
data[index + 2] = average;
|
42
|
+
}
|
43
|
+
}
|
44
|
+
|
45
|
+
context.putImageData(imageData, 0, 0);
|
46
|
+
},
|
47
|
+
|
48
|
+
/**
|
49
|
+
* Returns json representation of filter
|
50
|
+
* @method toJSON
|
51
|
+
* @return {String} json representation of filter
|
52
|
+
*/
|
53
|
+
toJSON: function() {
|
54
|
+
return { type: this.type };
|
55
|
+
}
|
56
|
+
});
|
57
|
+
|
58
|
+
/**
|
59
|
+
* Returns filter instance from an object representation
|
60
|
+
* @static
|
61
|
+
* @method fabric.Image.filters.Grayscale.fromObject
|
62
|
+
* @return {fabric.Image.filters.Grayscale}
|
63
|
+
*/
|
64
|
+
fabric.Image.filters.Grayscale.fromObject = function() {
|
65
|
+
return new fabric.Image.filters.Grayscale();
|
66
|
+
};
|
67
|
+
|
68
|
+
/**
|
69
|
+
* Remove white filter class
|
70
|
+
* @class fabric.Image.filters.RemoveWhite
|
71
|
+
* @memberOf fabric.Image.filters
|
72
|
+
*/
|
73
|
+
fabric.Image.filters.RemoveWhite = fabric.util.createClass( /** @scope fabric.Image.filters.RemoveWhite.prototype */ {
|
74
|
+
|
75
|
+
/**
|
76
|
+
* Filter type
|
77
|
+
* @param {String} type
|
78
|
+
*/
|
79
|
+
type: "RemoveWhite",
|
80
|
+
|
81
|
+
/**
|
82
|
+
* Constructor
|
83
|
+
* @memberOf fabric.Image.filters.RemoveWhite.prototype
|
84
|
+
* @param {Object} [options] Options object
|
85
|
+
*/
|
86
|
+
initialize: function(options) {
|
87
|
+
options || (options = { });
|
88
|
+
this.threshold = options.threshold || 30;
|
89
|
+
this.distance = options.distance || 20;
|
90
|
+
},
|
91
|
+
|
92
|
+
/**
|
93
|
+
* Applies filter to canvas element
|
94
|
+
* @method applyTo
|
95
|
+
* @param {Object} canvasEl Canvas element to apply filter to
|
96
|
+
*/
|
97
|
+
applyTo: function(canvasEl) {
|
98
|
+
var context = canvasEl.getContext('2d'),
|
99
|
+
imageData = context.getImageData(0, 0, canvasEl.width, canvasEl.height),
|
100
|
+
data = imageData.data,
|
101
|
+
threshold = this.threshold,
|
102
|
+
distance = this.distance,
|
103
|
+
limit = 255 - threshold,
|
104
|
+
abs = Math.abs,
|
105
|
+
r, g, b;
|
106
|
+
|
107
|
+
for (var i = 0, len = data.length; i < len; i += 4) {
|
108
|
+
|
109
|
+
r = data[i];
|
110
|
+
g = data[i+1];
|
111
|
+
b = data[i+2];
|
112
|
+
|
113
|
+
if (r > limit &&
|
114
|
+
g > limit &&
|
115
|
+
b > limit &&
|
116
|
+
abs(r-g) < distance &&
|
117
|
+
abs(r-b) < distance &&
|
118
|
+
abs(g-b) < distance) {
|
119
|
+
|
120
|
+
data[i+3] = 1;
|
121
|
+
}
|
122
|
+
}
|
123
|
+
|
124
|
+
context.putImageData(imageData, 0, 0);
|
125
|
+
},
|
126
|
+
|
127
|
+
/**
|
128
|
+
* Returns json representation of filter
|
129
|
+
* @method toJSON
|
130
|
+
* @return {String} json representation of filter
|
131
|
+
*/
|
132
|
+
toJSON: function() {
|
133
|
+
return {
|
134
|
+
type: this.type,
|
135
|
+
threshold: this.threshold,
|
136
|
+
distance: this.distance
|
137
|
+
};
|
138
|
+
}
|
139
|
+
});
|
140
|
+
|
141
|
+
/**
|
142
|
+
* Returns filter instance from an object representation
|
143
|
+
* @static
|
144
|
+
* @method fabric.Image.filters.RemoveWhite.fromObject
|
145
|
+
* @return {fabric.Image.filters.RemoveWhite}
|
146
|
+
*/
|
147
|
+
fabric.Image.filters.RemoveWhite.fromObject = function(object) {
|
148
|
+
return new fabric.Image.filters.RemoveWhite(object);
|
149
|
+
};
|
150
|
+
|
151
|
+
/**
|
152
|
+
* Invert filter class
|
153
|
+
* @class fabric.Image.filters.Invert
|
154
|
+
* @memberOf fabric.Image.filters
|
155
|
+
*/
|
156
|
+
fabric.Image.filters.Invert = fabric.util.createClass( /** @scope fabric.Image.filters.Invert.prototype */ {
|
157
|
+
|
158
|
+
/**
|
159
|
+
* Filter type
|
160
|
+
* @param {String} type
|
161
|
+
*/
|
162
|
+
type: "Invert",
|
163
|
+
|
164
|
+
/**
|
165
|
+
* Applies filter to canvas element
|
166
|
+
* @method applyTo
|
167
|
+
* @memberOf fabric.Image.filters.Invert.prototype
|
168
|
+
* @param {Object} canvasEl Canvas element to apply filter to
|
169
|
+
*/
|
170
|
+
applyTo: function(canvasEl) {
|
171
|
+
var context = canvasEl.getContext('2d'),
|
172
|
+
imageData = context.getImageData(0, 0, canvasEl.width, canvasEl.height),
|
173
|
+
data = imageData.data,
|
174
|
+
iLen = data.length, i;
|
175
|
+
|
176
|
+
for (i = 0; i < iLen; i+=4) {
|
177
|
+
data[i] = 255 - data[i];
|
178
|
+
data[i + 1] = 255 - data[i + 1];
|
179
|
+
data[i + 2] = 255 - data[i + 2];
|
180
|
+
}
|
181
|
+
|
182
|
+
context.putImageData(imageData, 0, 0);
|
183
|
+
},
|
184
|
+
|
185
|
+
/**
|
186
|
+
* Returns json representation of filter
|
187
|
+
* @method toJSON
|
188
|
+
* @return {String} json representation of filter
|
189
|
+
*/
|
190
|
+
toJSON: function() {
|
191
|
+
return { type: this.type };
|
192
|
+
}
|
193
|
+
});
|
194
|
+
|
195
|
+
/**
|
196
|
+
* Returns filter instance from an object representation
|
197
|
+
* @static
|
198
|
+
* @method fabric.Image.filters.Invert.fromObject
|
199
|
+
* @return {fabric.Image.filters.Invert}
|
200
|
+
*/
|
201
|
+
fabric.Image.filters.Invert.fromObject = function() {
|
202
|
+
return new fabric.Image.filters.Invert();
|
203
|
+
};
|
204
|
+
|
205
|
+
/**
|
206
|
+
* Sepia filter class
|
207
|
+
* @class fabric.Image.filters.Sepia
|
208
|
+
* @memberOf fabric.Image.filters
|
209
|
+
*/
|
210
|
+
fabric.Image.filters.Sepia = fabric.util.createClass( /** @scope fabric.Image.filters.Sepia.prototype */ {
|
211
|
+
|
212
|
+
/**
|
213
|
+
* Filter type
|
214
|
+
* @param {String} type
|
215
|
+
*/
|
216
|
+
type: "Sepia",
|
217
|
+
|
218
|
+
/**
|
219
|
+
* Applies filter to canvas element
|
220
|
+
* @method applyTo
|
221
|
+
* @memberOf fabric.Image.filters.Sepia.prototype
|
222
|
+
* @param {Object} canvasEl Canvas element to apply filter to
|
223
|
+
*/
|
224
|
+
applyTo: function(canvasEl) {
|
225
|
+
var context = canvasEl.getContext('2d'),
|
226
|
+
imageData = context.getImageData(0, 0, canvasEl.width, canvasEl.height),
|
227
|
+
data = imageData.data,
|
228
|
+
iLen = data.length, i, avg;
|
229
|
+
|
230
|
+
for (i = 0; i < iLen; i+=4) {
|
231
|
+
avg = 0.3 * data[i] + 0.59 * data[i + 1] + 0.11 * data[i + 2];
|
232
|
+
data[i] = avg + 100;
|
233
|
+
data[i + 1] = avg + 50;
|
234
|
+
data[i + 2] = avg + 255;
|
235
|
+
}
|
236
|
+
|
237
|
+
context.putImageData(imageData, 0, 0);
|
238
|
+
},
|
239
|
+
|
240
|
+
/**
|
241
|
+
* Returns json representation of filter
|
242
|
+
* @method toJSON
|
243
|
+
* @return {String} json representation of filter
|
244
|
+
*/
|
245
|
+
toJSON: function() {
|
246
|
+
return { type: this.type };
|
247
|
+
}
|
248
|
+
});
|
249
|
+
|
250
|
+
/**
|
251
|
+
* Returns filter instance from an object representation
|
252
|
+
* @static
|
253
|
+
* @method fabric.Image.filters.Sepia.fromObject
|
254
|
+
* @return {fabric.Image.filters.Sepia}
|
255
|
+
*/
|
256
|
+
fabric.Image.filters.Sepia.fromObject = function() {
|
257
|
+
return new fabric.Image.filters.Sepia();
|
258
|
+
};
|
259
|
+
|
260
|
+
/**
|
261
|
+
* Sepia2 filter class
|
262
|
+
* @class fabric.Image.filters.Sepia2
|
263
|
+
* @memberOf fabric.Image.filters
|
264
|
+
*/
|
265
|
+
fabric.Image.filters.Sepia2 = fabric.util.createClass( /** @scope fabric.Image.filters.Sepia2.prototype */ {
|
266
|
+
|
267
|
+
/**
|
268
|
+
* Filter type
|
269
|
+
* @param {String} type
|
270
|
+
*/
|
271
|
+
type: "Sepia2",
|
272
|
+
|
273
|
+
/**
|
274
|
+
* Applies filter to canvas element
|
275
|
+
* @method applyTo
|
276
|
+
* @memberOf fabric.Image.filters.Sepia.prototype
|
277
|
+
* @param {Object} canvasEl Canvas element to apply filter to
|
278
|
+
*/
|
279
|
+
applyTo: function(canvasEl) {
|
280
|
+
var context = canvasEl.getContext('2d'),
|
281
|
+
imageData = context.getImageData(0, 0, canvasEl.width, canvasEl.height),
|
282
|
+
data = imageData.data,
|
283
|
+
iLen = data.length, i, r, g, b;
|
284
|
+
|
285
|
+
for (i = 0; i < iLen; i+=4) {
|
286
|
+
|
287
|
+
r = data[i];
|
288
|
+
g = data[i + 1];
|
289
|
+
b = data[i + 2];
|
290
|
+
|
291
|
+
data[i] = (r * 0.393 + g * 0.769 + b * 0.189 ) / 1.351;
|
292
|
+
data[i + 1] = (r * 0.349 + g * 0.686 + b * 0.168 ) / 1.203;
|
293
|
+
data[i + 2] = (r * 0.272 + g * 0.534 + b * 0.131 ) / 2.140;
|
294
|
+
}
|
295
|
+
|
296
|
+
context.putImageData(imageData, 0, 0);
|
297
|
+
},
|
298
|
+
|
299
|
+
/**
|
300
|
+
* Returns json representation of filter
|
301
|
+
* @method toJSON
|
302
|
+
* @return {String} json representation of filter
|
303
|
+
*/
|
304
|
+
toJSON: function() {
|
305
|
+
return { type: this.type };
|
306
|
+
}
|
307
|
+
});
|
308
|
+
|
309
|
+
/**
|
310
|
+
* Returns filter instance from an object representation
|
311
|
+
* @static
|
312
|
+
* @method fabric.Image.filters.Sepia2.fromObject
|
313
|
+
* @return {fabric.Image.filters.Sepia2}
|
314
|
+
*/
|
315
|
+
fabric.Image.filters.Sepia2.fromObject = function() {
|
316
|
+
return new fabric.Image.filters.Sepia2();
|
317
|
+
};
|
318
|
+
|
319
|
+
/**
|
320
|
+
* Brightness filter class
|
321
|
+
* @class fabric.Image.filters.Brightness
|
322
|
+
* @memberOf fabric.Image.filters
|
323
|
+
*/
|
324
|
+
fabric.Image.filters.Brightness = fabric.util.createClass( /** @scope fabric.Image.filters.Brightness.prototype */ {
|
325
|
+
|
326
|
+
/**
|
327
|
+
* Filter type
|
328
|
+
* @param {String} type
|
329
|
+
*/
|
330
|
+
type: "Brightness",
|
331
|
+
|
332
|
+
/**
|
333
|
+
* Constructor
|
334
|
+
* @memberOf fabric.Image.filters.Brightness.prototype
|
335
|
+
* @param {Object} [options] Options object
|
336
|
+
*/
|
337
|
+
initialize: function(options) {
|
338
|
+
options || (options = { });
|
339
|
+
this.brightness = options.brightness || 100;
|
340
|
+
},
|
341
|
+
|
342
|
+
/**
|
343
|
+
* Applies filter to canvas element
|
344
|
+
* @method applyTo
|
345
|
+
* @param {Object} canvasEl Canvas element to apply filter to
|
346
|
+
*/
|
347
|
+
applyTo: function(canvasEl) {
|
348
|
+
var context = canvasEl.getContext('2d'),
|
349
|
+
imageData = context.getImageData(0, 0, canvasEl.width, canvasEl.height),
|
350
|
+
data = imageData.data,
|
351
|
+
brightness = this.brightness;
|
352
|
+
|
353
|
+
for (var i = 0, len = data.length; i < len; i += 4) {
|
354
|
+
data[i] += brightness;
|
355
|
+
data[i + 1] += brightness;
|
356
|
+
data[i + 2] += brightness;
|
357
|
+
}
|
358
|
+
|
359
|
+
context.putImageData(imageData, 0, 0);
|
360
|
+
},
|
361
|
+
|
362
|
+
/**
|
363
|
+
* Returns json representation of filter
|
364
|
+
* @method toJSON
|
365
|
+
* @return {String} json representation of filter
|
366
|
+
*/
|
367
|
+
toJSON: function() {
|
368
|
+
return {
|
369
|
+
type: this.type,
|
370
|
+
brightness: this.brightness
|
371
|
+
};
|
372
|
+
}
|
373
|
+
});
|
374
|
+
|
375
|
+
/**
|
376
|
+
* Returns filter instance from an object representation
|
377
|
+
* @static
|
378
|
+
* @method fabric.Image.filters.Brightness.fromObject
|
379
|
+
* @return {fabric.Image.filters.Brightness}
|
380
|
+
*/
|
381
|
+
fabric.Image.filters.Brightness.fromObject = function(object) {
|
382
|
+
return new fabric.Image.filters.Brightness(object);
|
383
|
+
};
|
384
|
+
|
385
|
+
/**
|
386
|
+
* Noise filter class
|
387
|
+
* @class fabric.Image.filters.Noise
|
388
|
+
* @memberOf fabric.Image.filters
|
389
|
+
*/
|
390
|
+
fabric.Image.filters.Noise = fabric.util.createClass( /** @scope fabric.Image.filters.Noise.prototype */ {
|
391
|
+
|
392
|
+
/**
|
393
|
+
* Filter type
|
394
|
+
* @param {String} type
|
395
|
+
*/
|
396
|
+
type: "Noise",
|
397
|
+
|
398
|
+
/**
|
399
|
+
* Constructor
|
400
|
+
* @memberOf fabric.Image.filters.Noise.prototype
|
401
|
+
* @param {Object} [options] Options object
|
402
|
+
*/
|
403
|
+
initialize: function(options) {
|
404
|
+
options || (options = { });
|
405
|
+
this.noise = options.noise || 100;
|
406
|
+
},
|
407
|
+
|
408
|
+
/**
|
409
|
+
* Applies filter to canvas element
|
410
|
+
* @method applyTo
|
411
|
+
* @param {Object} canvasEl Canvas element to apply filter to
|
412
|
+
*/
|
413
|
+
applyTo: function(canvasEl) {
|
414
|
+
var context = canvasEl.getContext('2d'),
|
415
|
+
imageData = context.getImageData(0, 0, canvasEl.width, canvasEl.height),
|
416
|
+
data = imageData.data,
|
417
|
+
noise = this.noise, rand;
|
418
|
+
|
419
|
+
for (var i = 0, len = data.length; i < len; i += 4) {
|
420
|
+
|
421
|
+
rand = (0.5 - Math.random()) * noise;
|
422
|
+
|
423
|
+
data[i] += rand;
|
424
|
+
data[i + 1] += rand;
|
425
|
+
data[i + 2] += rand;
|
426
|
+
}
|
427
|
+
|
428
|
+
context.putImageData(imageData, 0, 0);
|
429
|
+
},
|
430
|
+
|
431
|
+
/**
|
432
|
+
* Returns json representation of filter
|
433
|
+
* @method toJSON
|
434
|
+
* @return {String} json representation of filter
|
435
|
+
*/
|
436
|
+
toJSON: function() {
|
437
|
+
return {
|
438
|
+
type: this.type,
|
439
|
+
noise: this.noise
|
440
|
+
};
|
441
|
+
}
|
442
|
+
});
|
443
|
+
|
444
|
+
/**
|
445
|
+
* Returns filter instance from an object representation
|
446
|
+
* @static
|
447
|
+
* @method fabric.Image.filters.Noise.fromObject
|
448
|
+
* @return {fabric.Image.filters.Noise}
|
449
|
+
*/
|
450
|
+
fabric.Image.filters.Noise.fromObject = function(object) {
|
451
|
+
return new fabric.Image.filters.Noise(object);
|
452
|
+
};
|
453
|
+
|
454
|
+
/**
|
455
|
+
* GradientTransparency filter class
|
456
|
+
* @class fabric.Image.filters.GradientTransparency
|
457
|
+
* @memberOf fabric.Image.filters
|
458
|
+
*/
|
459
|
+
fabric.Image.filters.GradientTransparency = fabric.util.createClass( /** @scope fabric.Image.filters.GradientTransparency.prototype */ {
|
460
|
+
|
461
|
+
/**
|
462
|
+
* Filter type
|
463
|
+
* @param {String} type
|
464
|
+
*/
|
465
|
+
type: "GradientTransparency",
|
466
|
+
|
467
|
+
/**
|
468
|
+
* Constructor
|
469
|
+
* @memberOf fabric.Image.filters.GradientTransparency
|
470
|
+
* @param {Object} [options] Options object
|
471
|
+
*/
|
472
|
+
initialize: function(options) {
|
473
|
+
options || (options = { });
|
474
|
+
this.threshold = options.threshold || 100;
|
475
|
+
},
|
476
|
+
|
477
|
+
/**
|
478
|
+
* Applies filter to canvas element
|
479
|
+
* @method applyTo
|
480
|
+
* @param {Object} canvasEl Canvas element to apply filter to
|
481
|
+
*/
|
482
|
+
applyTo: function(canvasEl) {
|
483
|
+
var context = canvasEl.getContext('2d'),
|
484
|
+
imageData = context.getImageData(0, 0, canvasEl.width, canvasEl.height),
|
485
|
+
data = imageData.data,
|
486
|
+
threshold = this.threshold,
|
487
|
+
total = data.length;
|
488
|
+
|
489
|
+
for (var i = 0, len = data.length; i < len; i += 4) {
|
490
|
+
data[i + 3] = threshold + 255 * (total - i) / total;
|
491
|
+
}
|
492
|
+
|
493
|
+
context.putImageData(imageData, 0, 0);
|
494
|
+
},
|
495
|
+
|
496
|
+
/**
|
497
|
+
* Returns json representation of filter
|
498
|
+
* @method toJSON
|
499
|
+
* @return {String} json representation of filter
|
500
|
+
*/
|
501
|
+
toJSON: function() {
|
502
|
+
return {
|
503
|
+
type: this.type,
|
504
|
+
threshold: this.threshold
|
505
|
+
};
|
506
|
+
}
|
507
|
+
});
|
508
|
+
|
509
|
+
/**
|
510
|
+
* Returns filter instance from an object representation
|
511
|
+
* @static
|
512
|
+
* @method fabric.Image.filters.GradientTransparency.fromObject
|
513
|
+
* @return {fabric.Image.filters.GradientTransparency}
|
514
|
+
*/
|
515
|
+
fabric.Image.filters.GradientTransparency.fromObject = function(object) {
|
516
|
+
return new fabric.Image.filters.GradientTransparency(object);
|
517
|
+
};
|
518
|
+
|
519
|
+
/**
|
520
|
+
* Tint filter class
|
521
|
+
* @class fabric.Image.filters.Tint
|
522
|
+
* @memberOf fabric.Image.filters
|
523
|
+
*/
|
524
|
+
fabric.Image.filters.Tint = fabric.util.createClass( /** @scope fabric.Image.filters.Tint.prototype */ {
|
525
|
+
|
526
|
+
/**
|
527
|
+
* Filter type
|
528
|
+
* @param {String} type
|
529
|
+
*/
|
530
|
+
type: "Tint",
|
531
|
+
|
532
|
+
/**
|
533
|
+
* Constructor
|
534
|
+
* @memberOf fabric.Image.filters.Tint.prototype
|
535
|
+
* @param {Object} [options] Options object
|
536
|
+
*/
|
537
|
+
initialize: function(options) {
|
538
|
+
options || (options = { });
|
539
|
+
this.color = options.color || 0;
|
540
|
+
},
|
541
|
+
|
542
|
+
/**
|
543
|
+
* Applies filter to canvas element
|
544
|
+
* @method applyTo
|
545
|
+
* @param {Object} canvasEl Canvas element to apply filter to
|
546
|
+
*/
|
547
|
+
applyTo: function(canvasEl) {
|
548
|
+
|
549
|
+
var context = canvasEl.getContext('2d'),
|
550
|
+
imageData = context.getImageData(0, 0, canvasEl.width, canvasEl.height),
|
551
|
+
data = imageData.data,
|
552
|
+
iLen = data.length, i, a;
|
553
|
+
|
554
|
+
var rgb = parseInt(this.color, 10).toString(16);
|
555
|
+
|
556
|
+
var cr = parseInt('0x' + rgb.substr(0, 2), 16);
|
557
|
+
var cg = parseInt('0x' + rgb.substr(2, 2), 16);
|
558
|
+
var cb = parseInt('0x' + rgb.substr(4, 2), 16);
|
559
|
+
|
560
|
+
for (i = 0; i < iLen; i+=4) {
|
561
|
+
|
562
|
+
a = data[i+3];
|
563
|
+
|
564
|
+
if (a > 0){
|
565
|
+
data[i] = cr;
|
566
|
+
data[i+1] = cg;
|
567
|
+
data[i+2] = cb;
|
568
|
+
}
|
569
|
+
}
|
570
|
+
|
571
|
+
context.putImageData(imageData, 0, 0);
|
572
|
+
},
|
573
|
+
|
574
|
+
/**
|
575
|
+
* Returns json representation of filter
|
576
|
+
* @method toJSON
|
577
|
+
* @return {String} json representation of filter
|
578
|
+
*/
|
579
|
+
toJSON: function() {
|
580
|
+
return {
|
581
|
+
type: this.type,
|
582
|
+
color: this.color
|
583
|
+
};
|
584
|
+
}
|
585
|
+
});
|
586
|
+
|
587
|
+
/**
|
588
|
+
* Returns filter instance from an object representation
|
589
|
+
* @static
|
590
|
+
* @method fabric.Image.filters.Tint.fromObject
|
591
|
+
* @return {fabric.Image.filters.Tint}
|
592
|
+
*/
|
593
|
+
fabric.Image.filters.Tint.fromObject = function(object) {
|
594
|
+
return new fabric.Image.filters.Tint(object);
|
595
|
+
};
|
596
|
+
|
597
|
+
/**
|
598
|
+
* Adapted from <a href="http://www.html5rocks.com/en/tutorials/canvas/imagefilters/">html5rocks article</a>
|
599
|
+
* @class fabric.Image.filters.Convolute
|
600
|
+
* @memberOf fabric.Image.filters
|
601
|
+
*/
|
602
|
+
fabric.Image.filters.Convolute = fabric.util.createClass(/** @scope fabric.Image.filters.Convolute.prototype */ {
|
603
|
+
|
604
|
+
/**
|
605
|
+
* Filter type
|
606
|
+
* @param {String} type
|
607
|
+
*/
|
608
|
+
type: 'Convolute',
|
609
|
+
|
610
|
+
/**
|
611
|
+
* Constructor
|
612
|
+
* @memberOf fabric.Image.filters.Convolute.prototype
|
613
|
+
* @param {Object} [options] Options object
|
614
|
+
*/
|
615
|
+
initialize: function(options) {
|
616
|
+
options || (options = { });
|
617
|
+
|
618
|
+
this.opaque = options.opaque;
|
619
|
+
this.matrix = options.matrix || [ 0, 0, 0,
|
620
|
+
0, 1, 0,
|
621
|
+
0, 0, 0 ];
|
622
|
+
|
623
|
+
var canvasEl = fabric.util.createCanvasElement();
|
624
|
+
this.tmpCtx = canvasEl.getContext('2d');
|
625
|
+
},
|
626
|
+
|
627
|
+
/**
|
628
|
+
* @private
|
629
|
+
* @method _createImageData
|
630
|
+
*/
|
631
|
+
_createImageData: function(w, h) {
|
632
|
+
return this.tmpCtx.createImageData(w, h);
|
633
|
+
},
|
634
|
+
|
635
|
+
/**
|
636
|
+
* Applies filter to canvas element
|
637
|
+
* @method applyTo
|
638
|
+
* @param {Object} canvasEl Canvas element to apply filter to
|
639
|
+
*/
|
640
|
+
applyTo: function(canvasEl) {
|
641
|
+
|
642
|
+
var weights = this.matrix;
|
643
|
+
var context = canvasEl.getContext('2d');
|
644
|
+
var pixels = context.getImageData(0, 0, canvasEl.width, canvasEl.height);
|
645
|
+
|
646
|
+
var side = Math.round(Math.sqrt(weights.length));
|
647
|
+
var halfSide = Math.floor(side/2);
|
648
|
+
var src = pixels.data;
|
649
|
+
var sw = pixels.width;
|
650
|
+
var sh = pixels.height;
|
651
|
+
|
652
|
+
// pad output by the convolution matrix
|
653
|
+
var w = sw;
|
654
|
+
var h = sh;
|
655
|
+
var output = this._createImageData(w, h);
|
656
|
+
|
657
|
+
var dst = output.data;
|
658
|
+
|
659
|
+
// go through the destination image pixels
|
660
|
+
var alphaFac = this.opaque ? 1 : 0;
|
661
|
+
for (var y=0; y<h; y++) {
|
662
|
+
for (var x=0; x<w; x++) {
|
663
|
+
var sy = y;
|
664
|
+
var sx = x;
|
665
|
+
var dstOff = (y*w+x)*4;
|
666
|
+
// calculate the weighed sum of the source image pixels that
|
667
|
+
// fall under the convolution matrix
|
668
|
+
var r=0, g=0, b=0, a=0;
|
669
|
+
for (var cy=0; cy<side; cy++) {
|
670
|
+
for (var cx=0; cx<side; cx++) {
|
671
|
+
var scy = sy + cy - halfSide;
|
672
|
+
var scx = sx + cx - halfSide;
|
673
|
+
if (scy >= 0 && scy < sh && scx >= 0 && scx < sw) {
|
674
|
+
var srcOff = (scy*sw+scx)*4;
|
675
|
+
var wt = weights[cy*side+cx];
|
676
|
+
r += src[srcOff] * wt;
|
677
|
+
g += src[srcOff+1] * wt;
|
678
|
+
b += src[srcOff+2] * wt;
|
679
|
+
a += src[srcOff+3] * wt;
|
680
|
+
}
|
681
|
+
}
|
682
|
+
}
|
683
|
+
dst[dstOff] = r;
|
684
|
+
dst[dstOff+1] = g;
|
685
|
+
dst[dstOff+2] = b;
|
686
|
+
dst[dstOff+3] = a + alphaFac*(255-a);
|
687
|
+
}
|
688
|
+
}
|
689
|
+
|
690
|
+
context.putImageData(output, 0, 0);
|
691
|
+
},
|
692
|
+
|
693
|
+
/**
|
694
|
+
* Returns json representation of filter
|
695
|
+
* @method toJSON
|
696
|
+
* @return {String} json representation of filter
|
697
|
+
*/
|
698
|
+
toJSON: function() {
|
699
|
+
return {
|
700
|
+
type: this.type,
|
701
|
+
matrix: this.matrix
|
702
|
+
};
|
703
|
+
}
|
704
|
+
});
|
705
|
+
|
706
|
+
/**
|
707
|
+
* Returns filter instance from an object representation
|
708
|
+
* @static
|
709
|
+
* @method fabric.Image.filters.Convolute.fromObject
|
710
|
+
* @return {fabric.Image.filters.Convolute}
|
711
|
+
*/
|
712
|
+
fabric.Image.filters.Convolute.fromObject = function(object) {
|
713
|
+
return new fabric.Image.filters.Convolute(object);
|
714
|
+
};
|
715
|
+
|
716
|
+
/**
|
717
|
+
* Pixelate filter class
|
718
|
+
* @class fabric.Image.filters.Pixelate
|
719
|
+
* @memberOf fabric.Image.filters
|
720
|
+
*/
|
721
|
+
fabric.Image.filters.Pixelate = fabric.util.createClass(/** @scope fabric.Image.filters.Pixelate.prototype */ {
|
722
|
+
|
723
|
+
/**
|
724
|
+
* Filter type
|
725
|
+
* @param {String} type
|
726
|
+
*/
|
727
|
+
type: 'Pixelate',
|
728
|
+
|
729
|
+
/**
|
730
|
+
* Constructor
|
731
|
+
* @memberOf fabric.Image.filters.Pixelate.prototype
|
732
|
+
* @param {Object} [options] Options object
|
733
|
+
*/
|
734
|
+
initialize: function(options) {
|
735
|
+
options || (options = { });
|
736
|
+
this.blocksize = options.blocksize || 4;
|
737
|
+
},
|
738
|
+
|
739
|
+
/**
|
740
|
+
* Applies filter to canvas element
|
741
|
+
* @method applyTo
|
742
|
+
* @param {Object} canvasEl Canvas element to apply filter to
|
743
|
+
*/
|
744
|
+
applyTo: function(canvasEl) {
|
745
|
+
|
746
|
+
var context = canvasEl.getContext('2d'),
|
747
|
+
imageData = context.getImageData(0, 0, canvasEl.width, canvasEl.height),
|
748
|
+
data = imageData.data,
|
749
|
+
iLen = imageData.width,
|
750
|
+
jLen = imageData.height,
|
751
|
+
index, i, j, r, g, b, a;
|
752
|
+
|
753
|
+
for (i = 0; i < iLen; i += this.blocksize) {
|
754
|
+
for (j = 0; j < jLen; j += this.blocksize) {
|
755
|
+
|
756
|
+
index = (i * 4) * jLen + (j * 4);
|
757
|
+
|
758
|
+
r = data[index];
|
759
|
+
g = data[index+1];
|
760
|
+
b = data[index+2];
|
761
|
+
a = data[index+3];
|
762
|
+
|
763
|
+
/*
|
764
|
+
blocksize: 4
|
765
|
+
|
766
|
+
[1,x,x,x,1]
|
767
|
+
[x,x,x,x,1]
|
768
|
+
[x,x,x,x,1]
|
769
|
+
[x,x,x,x,1]
|
770
|
+
[1,1,1,1,1]
|
771
|
+
*/
|
772
|
+
|
773
|
+
for (var _i = i, _ilen = i + this.blocksize; _i < _ilen; _i++) {
|
774
|
+
for (var _j = j, _jlen = j + this.blocksize; _j < _jlen; _j++) {
|
775
|
+
index = (_i * 4) * jLen + (_j * 4);
|
776
|
+
data[index] = r;
|
777
|
+
data[index + 1] = g;
|
778
|
+
data[index + 2] = b;
|
779
|
+
data[index + 3] = a;
|
780
|
+
}
|
781
|
+
}
|
782
|
+
}
|
783
|
+
}
|
784
|
+
|
785
|
+
context.putImageData(imageData, 0, 0);
|
786
|
+
},
|
787
|
+
|
788
|
+
/**
|
789
|
+
* Returns json representation of filter
|
790
|
+
* @method toJSON
|
791
|
+
* @return {String} json representation of filter
|
792
|
+
*/
|
793
|
+
toJSON: function() {
|
794
|
+
return {
|
795
|
+
type: this.type,
|
796
|
+
blocksize: this.blocksize
|
797
|
+
};
|
798
|
+
}
|
799
|
+
});
|
800
|
+
|
801
|
+
/**
|
802
|
+
* Returns filter instance from an object representation
|
803
|
+
* @static
|
804
|
+
* @method fabric.Image.filters.Pixelate.fromObject
|
805
|
+
* @return {fabric.Image.filters.Pixelate}
|
806
|
+
*/
|
807
|
+
fabric.Image.filters.Pixelate.fromObject = function(object) {
|
808
|
+
return new fabric.Image.filters.Pixelate(object);
|
809
|
+
};
|