diff --git a/CHANGELOG.md b/CHANGELOG.md index c6a70aa781b..1f7b56518fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,16 @@ +**Version 2.3.4** + - Fix: ToSVG was ignoring excludeFromExport for backgroundImage and OverlayImage. [#5075](https://github.com/fabricjs/fabric.js/pull/5075) + - Fix: ToSVG for circle with start and end angles. [#5085](https://github.com/fabricjs/fabric.js/pull/5085) + - Fix: Added callback for setPatternFill. [#5101](https://github.com/fabricjs/fabric.js/pull/5101) + - Fix: Resize filter taking in account multiple scale sources. [#5117](https://github.com/fabricjs/fabric.js/pull/5117) + - Fix: Blend image filter clean after refilter. [#5121](https://github.com/fabricjs/fabric.js/pull/5121) + - Fix: Object.toDataURL should not be influenced by zoom. [#5139](https://github.com/fabricjs/fabric.js/pull/5139) + - Improvement: requestRenderAllBound add to Canvas instance. [#5138](https://github.com/fabricjs/fabric.js/pull/5138) + - Improvement: Make path bounding cache optional and also reacheable/cleanable [#5140](https://github.com/fabricjs/fabric.js/pull/5140) + - Improvement: Make the logic of isNeutralState filters work before filtering start. [#5129](https://github.com/fabricjs/fabric.js/pull/5129) + - Improvement: Added some code to clean up some memory when canvas is disposed in nodejs. [#5142](https://github.com/fabricjs/fabric.js/pull/5142) + - Fix: Make numeric origins work with group creation. [#5143](https://github.com/fabricjs/fabric.js/pull/5143) + **Version 2.3.3** - Fix: Fixed font generic names for text, measurement of zero width related characters and also trailing of cursor when zooming. [#5048](https://github.com/fabricjs/fabric.js/pull/5048) diff --git a/HEADER.js b/HEADER.js index 119611db7d6..36864f5872c 100644 --- a/HEADER.js +++ b/HEADER.js @@ -1,6 +1,6 @@ /*! Fabric.js Copyright 2008-2015, Printio (Juriy Zaytsev, Maxim Chernyak) */ -var fabric = fabric || { version: '2.3.3' }; +var fabric = fabric || { version: '2.3.4' }; if (typeof exports !== 'undefined') { exports.fabric = fabric; } diff --git a/dist/fabric.js b/dist/fabric.js index aa5fa978b31..85fb0553f33 100644 --- a/dist/fabric.js +++ b/dist/fabric.js @@ -1,7 +1,7 @@ /* build: `node build.js modules=ALL exclude=gestures,accessors requirejs minifier=uglifyjs` */ /*! Fabric.js Copyright 2008-2015, Printio (Juriy Zaytsev, Maxim Chernyak) */ -var fabric = fabric || { version: '2.3.3' }; +var fabric = fabric || { version: '2.3.4' }; if (typeof exports !== 'undefined') { exports.fabric = fabric; } @@ -141,6 +141,28 @@ fabric.devicePixelRatio = fabric.window.devicePixelRatio || */ fabric.browserShadowBlurConstant = 1; +/** + * This object contains the result of arc to beizer conversion for faster retrieving if the same arc needs to be converted again. + * It was an internal variable, is accessible since version 2.3.4 + */ +fabric.arcToSegmentsCache = { }; + +/** + * This object keeps the results of the boundsOfCurve calculation mapped by the joined arguments necessary to calculate it. + * It does speed up calculation, if you parse and add always the same paths, but in case of heavy usage of freedrawing + * you do not get any speed benefit and you get a big object in memory. + * The object was a private variable before, while now is appended to the lib so that you have access to it and you + * can eventually clear it. + * It was an internal variable, is accessible since version 2.3.4 + */ +fabric.boundsOfCurveCache = { }; + +/** + * If disabled boundsOfCurveCache is not used. For apps that make heavy usage of pencil drawing probably disabling it is better + * @default true + */ +fabric.cachesBoundsOfCurve = true; + fabric.initFilterBackend = function() { if (fabric.enableGLFiltering && fabric.isWebglSupported && fabric.isWebglSupported(fabric.textureSize)) { console.log('max texture size: ' + fabric.maxTextureSize); @@ -1223,6 +1245,12 @@ fabric.CommonMethods = { return fabric.util.multiplyTransformMatrices(scaleMatrix, skewMatrixX, true); }, + /** + * reset an object transform state to neutral. Top and left are not accounted for + * @static + * @memberOf fabric.util + * @param {fabric.Object} target object to transform + */ resetObjectTransform: function (target) { target.scaleX = 1; target.scaleY = 1; @@ -1233,6 +1261,27 @@ fabric.CommonMethods = { target.rotate(0); }, + /** + * Extract Object transform values + * @static + * @memberOf fabric.util + * @param {fabric.Object} target object to read from + * @return {Object} Components of transform + */ + saveObjectTransform: function (target) { + return { + scaleX: target.scaleX, + scaleY: target.scaleY, + skewX: target.skewX, + skewY: target.skewY, + angle: target.angle, + left: target.left, + flipX: target.flipX, + flipY: target.flipY, + top: target.top + }; + }, + /** * Returns string representation of function body * @param {Function} fn Function to get body of @@ -1370,10 +1419,7 @@ fabric.CommonMethods = { (function() { - var arcToSegmentsCache = { }, - segmentToBezierCache = { }, - boundsOfCurveCache = { }, - _join = Array.prototype.join; + var _join = Array.prototype.join; /* Adapted from http://dxr.mozilla.org/mozilla-central/source/content/svg/content/src/nsSVGPathDataParser.cpp * by Andrea Bogazzi code is under MPL. if you don't have a copy of the license you can take it here @@ -1381,8 +1427,8 @@ fabric.CommonMethods = { */ function arcToSegments(toX, toY, rx, ry, large, sweep, rotateX) { var argsString = _join.call(arguments); - if (arcToSegmentsCache[argsString]) { - return arcToSegmentsCache[argsString]; + if (fabric.arcToSegmentsCache[argsString]) { + return fabric.arcToSegmentsCache[argsString]; } var PI = Math.PI, th = rotateX * PI / 180, @@ -1436,16 +1482,11 @@ fabric.CommonMethods = { mTheta = th3; th3 += mDelta; } - arcToSegmentsCache[argsString] = result; + fabric.arcToSegmentsCache[argsString] = result; return result; } function segmentToBezier(th2, th3, cosTh, sinTh, rx, ry, cx1, cy1, mT, fromX, fromY) { - var argsString2 = _join.call(arguments); - if (segmentToBezierCache[argsString2]) { - return segmentToBezierCache[argsString2]; - } - var costh2 = fabric.util.cos(th2), sinth2 = fabric.util.sin(th2), costh3 = fabric.util.cos(th3), @@ -1457,12 +1498,11 @@ fabric.CommonMethods = { cp2X = toX + mT * ( cosTh * rx * sinth3 + sinTh * ry * costh3), cp2Y = toY + mT * ( sinTh * rx * sinth3 - cosTh * ry * costh3); - segmentToBezierCache[argsString2] = [ + return [ cp1X, cp1Y, cp2X, cp2Y, toX, toY ]; - return segmentToBezierCache[argsString2]; } /* @@ -1548,9 +1588,12 @@ fabric.CommonMethods = { */ // taken from http://jsbin.com/ivomiq/56/edit no credits available for that. function getBoundsOfCurve(x0, y0, x1, y1, x2, y2, x3, y3) { - var argsString = _join.call(arguments); - if (boundsOfCurveCache[argsString]) { - return boundsOfCurveCache[argsString]; + var argsString; + if (fabric.cachesBoundsOfCurve) { + argsString = _join.call(arguments); + if (fabric.boundsOfCurveCache[argsString]) { + return fabric.boundsOfCurveCache[argsString]; + } } var sqrt = Math.sqrt, @@ -1620,7 +1663,9 @@ fabric.CommonMethods = { y: max.apply(null, bounds[1]) } ]; - boundsOfCurveCache[argsString] = result; + if (fabric.cachesBoundsOfCurve) { + fabric.boundsOfCurveCache[argsString] = result; + } return result; } @@ -2625,6 +2670,21 @@ fabric.CommonMethods = { return impl._canvas || impl._image; }; + function cleanUpJsdomNode(element) { + if (!fabric.isLikelyNode) { + return; + } + var impl = fabric.jsdomImplForWrapper(element); + if (impl) { + impl._image = null; + impl._canvas = null; + // unsure if necessary + impl._currentSrc = null; + impl._attributes = null; + impl._classList = null; + } + } + fabric.util.getById = getById; fabric.util.toArray = toArray; fabric.util.makeElement = makeElement; @@ -2634,6 +2694,7 @@ fabric.CommonMethods = { fabric.util.getElementOffset = getElementOffset; fabric.util.getElementStyle = getElementStyle; fabric.util.getNodeCanvas = getNodeCanvas; + fabric.util.cleanUpJsdomNode = cleanUpJsdomNode; })(); @@ -2646,10 +2707,10 @@ fabric.CommonMethods = { var makeXHR = (function() { var factories = [ + function() { return new fabric.window.XMLHttpRequest(); }, function() { return new ActiveXObject('Microsoft.XMLHTTP'); }, function() { return new ActiveXObject('Msxml2.XMLHTTP'); }, - function() { return new ActiveXObject('Msxml2.XMLHTTP.3.0'); }, - function() { return new XMLHttpRequest(); } + function() { return new ActiveXObject('Msxml2.XMLHTTP.3.0'); } ]; for (var i = factories.length; i--; ) { try { @@ -2676,7 +2737,6 @@ fabric.CommonMethods = { * @return {XMLHttpRequest} request */ function request(url, options) { - options || (options = { }); var method = options.method ? options.method.toUpperCase() : 'GET', @@ -7794,7 +7854,7 @@ fabric.ElementsParser.prototype.checkIfDone = function() { * @private */ _setSVGBgOverlayImage: function(markup, property, reviver) { - if (this[property] && this[property].toSVG) { + if (this[property] && !this[property].excludeFromExport && this[property].toSVG) { markup.push(this[property].toSVG(reviver)); } }, @@ -8067,11 +8127,18 @@ fabric.ElementsParser.prototype.checkIfDone = function() { object.dispose && object.dispose(); }); this._objects = []; + if (this.backgroundImage && this.backgroundImage.dispose) { + this.backgroundImage.dispose(); + } this.backgroundImage = null; + if (this.overlayImage && this.overlayImage.dispose) { + this.overlayImage.dispose(); + } this.overlayImage = null; this._iTextInstances = null; - this.lowerCanvasEl = null; this.contextContainer = null; + fabric.util.cleanUpJsdomNode(this.lowerCanvasEl); + this.lowerCanvasEl = undefined; return this; }, @@ -8925,7 +8992,7 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab var dotWidth = 20, dotDistance = 5, - patternCanvas = fabric.document.createElement('canvas'), + patternCanvas = fabric.util.createCanvasElement(), patternCtx = patternCanvas.getContext('2d'); patternCanvas.width = patternCanvas.height = dotWidth + dotDistance; @@ -9038,6 +9105,7 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab initialize: function(el, options) { options || (options = { }); this.renderAndResetBound = this.renderAndReset.bind(this); + this.requestRenderAllBound = this.requestRenderAll.bind(this); this._initStatic(el, options); this._initInteractive(); this._createCacheCanvas(); @@ -9679,19 +9747,12 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab mouseXSign: 1, mouseYSign: 1, shiftKey: e.shiftKey, - altKey: e[this.centeredKey] + altKey: e[this.centeredKey], + original: fabric.util.saveObjectTransform(target), }; - this._currentTransform.original = { - left: target.left, - top: target.top, - scaleX: target.scaleX, - scaleY: target.scaleY, - skewX: target.skewX, - skewY: target.skewY, - originX: origin.x, - originY: origin.y - }; + this._currentTransform.original.originX = origin.x; + this._currentTransform.original.originY = origin.y; this._resetCurrentTransform(); this._beforeTransform(e); @@ -10072,18 +10133,6 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab this.upperCanvasEl.style.cursor = value; }, - /** - * @param {fabric.Object} target to reset transform - * @private - */ - _resetObjectTransform: function (target) { - target.scaleX = 1; - target.scaleY = 1; - target.skewX = 0; - target.skewY = 0; - target.rotate(0); - }, - /** * @private * @param {CanvasRenderingContext2D} ctx to draw the selection on @@ -10572,10 +10621,12 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab this.removeListeners(); wrapper.removeChild(this.upperCanvasEl); wrapper.removeChild(this.lowerCanvasEl); - this.upperCanvasEl = null; - this.cacheCanvasEl = null; this.contextCache = null; this.contextTop = null; + ['upperCanvasEl', 'cacheCanvasEl'].forEach((function(element) { + fabric.util.cleanUpJsdomNode(this[element]); + this[element] = undefined; + }).bind(this)); if (wrapper.parentNode) { wrapper.parentNode.replaceChild(this.lowerCanvasEl, this.wrapperEl); } @@ -12168,7 +12219,7 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati * @param {Object} [callback] Receives cloned instance as a first argument */ cloneWithoutData: function(callback) { - var el = fabric.document.createElement('canvas'); + var el = fabric.util.createCanvasElement(); el.width = this.width; el.height = this.height; @@ -12783,6 +12834,9 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati /** * List of properties to consider when checking if cache needs refresh + * Those properties are checked by statefullCache ON ( or lazy mode if we want ) or from single + * calls to Object.set(key, value). If the key is in this list, the object is marked as dirty + * and refreshed at the next render * @type Array */ cacheProperties: ( @@ -12806,7 +12860,7 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati */ _createCacheCanvas: function() { this._cacheProperties = {}; - this._cacheCanvas = fabric.document.createElement('canvas'); + this._cacheCanvas = fabric.util.createCanvasElement(); this._cacheContext = this._cacheCanvas.getContext('2d'); this._updateCacheCanvas(); // if canvas gets created, is empty, so dirty. @@ -12870,12 +12924,10 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati * @return {Object}.zoomY zoomY zoom value to unscale the canvas before drawing cache */ _getCacheCanvasDimensions: function() { - var zoom = this.canvas && this.canvas.getZoom() || 1, - objectScale = this.getObjectScaling(), - retina = this.canvas && this.canvas._isRetinaScaling() ? fabric.devicePixelRatio : 1, + var objectScale = this.getTotalObjectScaling(), dim = this._getNonTransformedDimensions(), - zoomX = objectScale.scaleX * zoom * retina, - zoomY = objectScale.scaleY * zoom * retina, + zoomX = objectScale.scaleX, + zoomY = objectScale.scaleY, width = dim.x * zoomX, height = dim.y * zoomY; return { @@ -13082,6 +13134,21 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati return { scaleX: scaleX, scaleY: scaleY }; }, + /** + * Return the object scale factor counting also the group scaling, zoom and retina + * @return {Object} object with scaleX and scaleY properties + */ + getTotalObjectScaling: function() { + var scale = this.getObjectScaling(), scaleX = scale.scaleX, scaleY = scale.scaleY; + if (this.canvas) { + var zoom = this.canvas.getZoom(); + var retina = this.canvas.getRetinaScaling(); + scaleX *= zoom * retina; + scaleY *= zoom * retina; + } + return { scaleX: scaleX, scaleY: scaleY }; + }, + /** * Return the object opacity counting also the group property * @return {Number} @@ -13627,17 +13694,24 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati * @param {Number} [options.width] Cropping width. Introduced in v1.2.14 * @param {Number} [options.height] Cropping height. Introduced in v1.2.14 * @param {Boolean} [options.enableRetinaScaling] Enable retina scaling for clone image. Introduce in 1.6.4 + * @param {Boolean} [options.withoutTransform] Remove current object transform ( no scale , no angle, no flip, no skew ). Introduced in 2.3.4 * @return {String} Returns a data: URL containing a representation of the object in the format specified by options.format */ toDataURL: function(options) { options || (options = { }); + var origParams = fabric.util.saveObjectTransform(this); + + if (options.withoutTransform) { + fabric.util.resetObjectTransform(this); + } + var el = fabric.util.createCanvasElement(), - boundingRect = this.getBoundingRect(); + // skip canvas zoom and calculate with setCoords now. + boundingRect = this.getBoundingRect(true, true); el.width = boundingRect.width; el.height = boundingRect.height; - fabric.util.wrapElement(el, 'div'); var canvas = new fabric.StaticCanvas(el, { enableRetinaScaling: options.enableRetinaScaling, renderOnAddRemove: false, @@ -13652,11 +13726,6 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati canvas.backgroundColor = '#fff'; } - var origParams = { - left: this.left, - top: this.top - }; - this.setPositionByOrigin(new fabric.Point(canvas.width / 2, canvas.height / 2), 'center', 'center'); var originalCanvas = this.canvas; @@ -13780,20 +13849,18 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati * @param {String} [options.repeat=repeat] Repeat property of a pattern (one of repeat, repeat-x, repeat-y or no-repeat) * @param {Number} [options.offsetX=0] Pattern horizontal offset from object's left/top corner * @param {Number} [options.offsetY=0] Pattern vertical offset from object's left/top corner + * @param {Function} [callback] Callback to invoke when image set as a pattern * @return {fabric.Object} thisArg * @chainable * @see {@link http://jsfiddle.net/fabricjs/QT3pa/|jsFiddle demo} * @example