From 7a0a0fac92c53811e9a5e2c2d8637843215d8435 Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Fri, 26 Aug 2022 18:51:05 +0300 Subject: [PATCH 01/25] Create config.ts --- src/config.ts | 107 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 src/config.ts diff --git a/src/config.ts b/src/config.ts new file mode 100644 index 00000000000..6c262136e56 --- /dev/null +++ b/src/config.ts @@ -0,0 +1,107 @@ + + +class Configuration { + + /** + * Browser-specific constant to adjust CanvasRenderingContext2D.shadowBlur value, + * which is unitless and not rendered equally across browsers. + * + * Values that work quite well (as of October 2017) are: + * - Chrome: 1.5 + * - Edge: 1.75 + * - Firefox: 0.9 + * - Safari: 0.95 + * + * @since 2.0.0 + * @type Number + * @default 1 + */ + browserShadowBlurConstant = 1 + + + /** + * Pixel per Inch as a default value set to 96. Can be changed for more realistic conversion. + */ + DPI = 96 + + /** + * Pixel limit for cache canvases. 1Mpx , 4Mpx should be fine. + * @since 1.7.14 + * @type Number + * @default + */ + perfLimitSizeTotal = 2097152 + + /** + * Pixel limit for cache canvases width or height. IE fixes the maximum at 5000 + * @since 1.7.14 + * @type Number + * @default + */ + maxCacheSideLimit = 4096 + + /** + * Lowest pixel limit for cache canvases, set at 256PX + * @since 1.7.14 + * @type Number + * @default + */ + minCacheSideLimit = 256 + + /** + * if webgl is enabled and available, textureSize will determine the size + * of the canvas backend + * @since 2.0.0 + * @type Number + * @default + */ + textureSize = 2048 + + /** + * When 'true', style information is not retained when copy/pasting text, making + * pasted text use destination style. + * Defaults to 'false'. + * @type Boolean + * @default + * @deprecated + */ + disableStyleCopyPaste = false + + /** + * Enable webgl for filtering picture is available + * A filtering backend will be initialized, this will both take memory and + * time since a default 2048x2048 canvas will be created for the gl context + * @since 2.0.0 + * @type Boolean + * @default + */ + enableGLFiltering = true + + + /** + * If disabled boundsOfCurveCache is not used. For apps that make heavy usage of pencil drawing probably disabling it is better + * @default true + */ + cachesBoundsOfCurve = true + + /** + * Skip performance testing of setupGLContext and force the use of putImageData that seems to be the one that works best on + * Chrome + old hardware. if your users are experiencing empty images after filtering you may try to force this to true + * this has to be set before instantiating the filtering backend ( before filtering the first image ) + * @type Boolean + * @default false + */ + forceGLPutImageData = false + + NUM_FRACTION_DIGITS = 2 + + constructor(config: Partial> = {}) { + this.configure(config); + } + + configure(config: Partial>) { + Object.assign(this, config); + } +} + +export const config = new Configuration(); From 7e2c5ffd04b109bc390ecccd0a2ab7d0d8c6efd8 Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Fri, 26 Aug 2022 18:52:35 +0300 Subject: [PATCH 02/25] Update config.ts --- src/config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config.ts b/src/config.ts index 6c262136e56..d78b22037d8 100644 --- a/src/config.ts +++ b/src/config.ts @@ -1,6 +1,6 @@ -class Configuration { +export class Configuration { /** * Browser-specific constant to adjust CanvasRenderingContext2D.shadowBlur value, From dfc942d1c3002986eddb752fa7a6e4194d3a1789 Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Fri, 26 Aug 2022 18:53:42 +0300 Subject: [PATCH 03/25] Update config.ts --- src/config.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/config.ts b/src/config.ts index d78b22037d8..56b022bc557 100644 --- a/src/config.ts +++ b/src/config.ts @@ -95,11 +95,11 @@ export class Configuration { NUM_FRACTION_DIGITS = 2 - constructor(config: Partial> = {}) { + constructor(config?: Partial>) { this.configure(config); } - configure(config: Partial>) { + configure(config: Partial> = {}) { Object.assign(this, config); } } From b6933cbdf4f5af29ba2d6425c860ce4e30a9c473 Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Sat, 27 Aug 2022 02:44:14 +0300 Subject: [PATCH 04/25] Update config.ts --- src/config.ts | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/config.ts b/src/config.ts index 56b022bc557..17533d8752d 100644 --- a/src/config.ts +++ b/src/config.ts @@ -51,11 +51,14 @@ export class Configuration { /** * if webgl is enabled and available, textureSize will determine the size * of the canvas backend + * + * In order to support old hardware set to `2048` to avoid OOM + * * @since 2.0.0 * @type Number * @default */ - textureSize = 2048 + textureSize = 4096 /** * When 'true', style information is not retained when copy/pasting text, making @@ -93,7 +96,11 @@ export class Configuration { */ forceGLPutImageData = false - NUM_FRACTION_DIGITS = 2 + /** + * Used in exporting methods (`toObject`, `toJSON`, `toSVG`) + * Controls the percision of exported values + */ + NUM_FRACTION_DIGITS = 4 constructor(config?: Partial>) { this.configure(config); From 2c5f1d95f235cd6983812df2d60770edb0f6cb9f Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Sat, 27 Aug 2022 04:14:55 +0300 Subject: [PATCH 05/25] migrate --- HEADER.js | 113 ++++--------------------- src/config.ts | 48 +++++++---- src/filters/base_filter.class.ts | 11 ++- src/filters/webgl_backend.class.ts | 18 ++-- src/mixins/itext_key_behavior.mixin.ts | 8 +- src/shapes/object.class.ts | 17 ++-- src/static_canvas.class.ts | 7 +- src/typedefs.ts | 6 ++ src/util/misc/svgParsing.ts | 3 +- src/util/path.ts | 6 +- test/unit/canvas.js | 15 ++-- test/unit/canvas_static.js | 48 +++++------ test/unit/itext.js | 12 ++- test/unit/itext_click_behaviour.js | 14 ++- test/unit/itext_key_behaviour.js | 6 +- test/unit/object.js | 89 +++++++++++-------- test/visual/clippath.js | 8 +- test/visual/control_rendering.js | 20 +++-- test/visual/freedraw.js | 20 +++-- test/visual/generic_rendering.js | 21 ++--- test/visual/resize_filter.js | 6 +- test/visual/svg_import.js | 6 +- test/visual/text.js | 6 +- test/visual/toDataURL.js | 30 ++++--- test/visual/z_svg_export.js | 6 +- 25 files changed, 274 insertions(+), 270 deletions(-) diff --git a/HEADER.js b/HEADER.js index 28e35e4c21c..c97782f980b 100644 --- a/HEADER.js +++ b/HEADER.js @@ -1,8 +1,13 @@ /*! Fabric.js Copyright 2008-2015, Printio (Juriy Zaytsev, Maxim Chernyak) */ +import { config } from './src/config'; import { iMatrix, VERSION } from './src/constants'; -var fabric = fabric || { version: VERSION }; +var fabric = fabric || { + version: VERSION, + config, +}; + if (typeof exports !== 'undefined') { exports.fabric = fabric; } @@ -71,95 +76,27 @@ fabric.SHARED_ATTRIBUTES = [ ]; /* _FROM_SVG_END_ */ -/** - * Pixel per Inch as a default value set to 96. Can be changed for more realistic conversion. - */ -fabric.DPI = 96; + fabric.reNonWord = /[ \n\.,;!\?\-]/; fabric.fontPaths = { }; fabric.iMatrix = iMatrix; /** - * Pixel limit for cache canvases. 1Mpx , 4Mpx should be fine. - * @since 1.7.14 - * @type Number - * @default + * @todo move to config when window is exported */ -fabric.perfLimitSizeTotal = 2097152; +config.configure({ + devicePixelRatio: fabric.window.devicePixelRatio || + fabric.window.webkitDevicePixelRatio || + fabric.window.mozDevicePixelRatio || + 1 +}); -/** - * Pixel limit for cache canvases width or height. IE fixes the maximum at 5000 - * @since 1.7.14 - * @type Number - * @default - */ -fabric.maxCacheSideLimit = 4096; - -/** - * Lowest pixel limit for cache canvases, set at 256PX - * @since 1.7.14 - * @type Number - * @default - */ -fabric.minCacheSideLimit = 256; /** * Cache Object for widths of chars in text rendering. */ -fabric.charWidthsCache = { }; - -/** - * if webgl is enabled and available, textureSize will determine the size - * of the canvas backend - * @since 2.0.0 - * @type Number - * @default - */ -fabric.textureSize = 2048; - -/** - * When 'true', style information is not retained when copy/pasting text, making - * pasted text use destination style. - * Defaults to 'false'. - * @type Boolean - * @default - */ -fabric.disableStyleCopyPaste = false; - -/** - * Enable webgl for filtering picture is available - * A filtering backend will be initialized, this will both take memory and - * time since a default 2048x2048 canvas will be created for the gl context - * @since 2.0.0 - * @type Boolean - * @default - */ -fabric.enableGLFiltering = true; - -/** - * Device Pixel Ratio - * @see https://developer.apple.com/library/safari/documentation/AudioVideo/Conceptual/HTML-canvas-guide/SettingUptheCanvas/SettingUptheCanvas.html - */ -fabric.devicePixelRatio = fabric.window.devicePixelRatio || - fabric.window.webkitDevicePixelRatio || - fabric.window.mozDevicePixelRatio || - 1; -/** - * Browser-specific constant to adjust CanvasRenderingContext2D.shadowBlur value, - * which is unitless and not rendered equally across browsers. - * - * Values that work quite well (as of October 2017) are: - * - Chrome: 1.5 - * - Edge: 1.75 - * - Firefox: 0.9 - * - Safari: 0.95 - * - * @since 2.0.0 - * @type Number - * @default 1 - */ -fabric.browserShadowBlurConstant = 1; +fabric.charWidthsCache = {}; /** * This object contains the result of arc to bezier conversion for faster retrieving if the same arc needs to be converted again. @@ -177,25 +114,11 @@ fabric.arcToSegmentsCache = { }; */ 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; - -/** - * Skip performance testing of setupGLContext and force the use of putImageData that seems to be the one that works best on - * Chrome + old hardware. if your users are experiencing empty images after filtering you may try to force this to true - * this has to be set before instantiating the filtering backend ( before filtering the first image ) - * @type Boolean - * @default false - */ -fabric.forceGLPutImageData = false; fabric.initFilterBackend = function() { - if (fabric.enableGLFiltering && fabric.isWebglSupported && fabric.isWebglSupported(fabric.textureSize)) { - console.log('max texture size: ' + fabric.maxTextureSize); - return (new fabric.WebglFilterBackend({ tileSize: fabric.textureSize })); + if (config.enableGLFiltering && fabric.isWebglSupported && fabric.isWebglSupported(config.textureSize)) { + console.log(`fabric: max texture size: ${config.maxTextureSize}`); + return (new fabric.WebglFilterBackend({ tileSize: config.textureSize })); } else if (fabric.Canvas2dFilterBackend) { return (new fabric.Canvas2dFilterBackend()); diff --git a/src/config.ts b/src/config.ts index 17533d8752d..b1844f707ef 100644 --- a/src/config.ts +++ b/src/config.ts @@ -1,3 +1,4 @@ +import { WebGLPrecision } from "./typedefs" export class Configuration { @@ -24,6 +25,12 @@ export class Configuration { */ DPI = 96 + /** + * Device Pixel Ratio + * @see https://developer.apple.com/library/safari/documentation/AudioVideo/Conceptual/HTML-canvas-guide/SettingUptheCanvas/SettingUptheCanvas.html + */ + devicePixelRatio = 1 + /** * Pixel limit for cache canvases. 1Mpx , 4Mpx should be fine. * @since 1.7.14 @@ -48,18 +55,6 @@ export class Configuration { */ minCacheSideLimit = 256 - /** - * if webgl is enabled and available, textureSize will determine the size - * of the canvas backend - * - * In order to support old hardware set to `2048` to avoid OOM - * - * @since 2.0.0 - * @type Number - * @default - */ - textureSize = 4096 - /** * When 'true', style information is not retained when copy/pasting text, making * pasted text use destination style. @@ -80,12 +75,17 @@ export class Configuration { */ enableGLFiltering = true - /** - * If disabled boundsOfCurveCache is not used. For apps that make heavy usage of pencil drawing probably disabling it is better - * @default true + * if webgl is enabled and available, textureSize will determine the size + * of the canvas backend + * + * In order to support old hardware set to `2048` to avoid OOM + * + * @since 2.0.0 + * @type Number + * @default */ - cachesBoundsOfCurve = true + textureSize = 4096 /** * Skip performance testing of setupGLContext and force the use of putImageData that seems to be the one that works best on @@ -96,6 +96,22 @@ export class Configuration { */ forceGLPutImageData = false + /** + * WebGL + */ + maxTextureSize?: number + + /** + * WebGL + */ + webGLPrecision?: WebGLPrecision + + /** + * If disabled boundsOfCurveCache is not used. For apps that make heavy usage of pencil drawing probably disabling it is better + * @default true + */ + cachesBoundsOfCurve = true + /** * Used in exporting methods (`toObject`, `toJSON`, `toSVG`) * Controls the percision of exported values diff --git a/src/filters/base_filter.class.ts b/src/filters/base_filter.class.ts index 4f7fe600839..2ac9ce9c4ab 100644 --- a/src/filters/base_filter.class.ts +++ b/src/filters/base_filter.class.ts @@ -1,4 +1,9 @@ //@ts-nocheck + +import { config } from "../config"; +import { WebGLPrecision } from "../typedefs"; + + (function(global) { var fabric = global.fabric; /** @@ -72,10 +77,10 @@ createProgram: function(gl, fragmentSource, vertexSource) { fragmentSource = fragmentSource || this.fragmentSource; vertexSource = vertexSource || this.vertexSource; - if (fabric.webGlPrecision !== 'highp'){ + if (config.webGLPrecision !== WebGLPrecision.high) { fragmentSource = fragmentSource.replace( - /precision highp float/g, - 'precision ' + fabric.webGlPrecision + ' float' + new RegExp(`precision ${WebGLPrecision.high} float`,'g'), + `precision ${config.webGLPrecision} float` ); } var vertexShader = gl.createShader(gl.VERTEX_SHADER); diff --git a/src/filters/webgl_backend.class.ts b/src/filters/webgl_backend.class.ts index ba2060b3e92..3560abb094a 100644 --- a/src/filters/webgl_backend.class.ts +++ b/src/filters/webgl_backend.class.ts @@ -1,4 +1,9 @@ //@ts-nocheck + +import { config } from "../config"; +import { WebGLPrecision } from "../typedefs"; + + (function(global) { var fabric = global.fabric; /** @@ -33,12 +38,13 @@ var isSupported = false; // eslint-disable-next-line if (gl) { - fabric.maxTextureSize = gl.getParameter(gl.MAX_TEXTURE_SIZE); + config.configure({ maxTextureSize: gl.getParameter(gl.MAX_TEXTURE_SIZE) }); isSupported = fabric.maxTextureSize >= tileSize; - var precisions = ['highp', 'mediump', 'lowp']; - for (var i = 0; i < 3; i++){ - if (testPrecision(gl, precisions[i])){ - fabric.webGlPrecision = precisions[i]; + for (const key in WebGLPrecision) { + if (testPrecision(gl, key)) { + config.configure({ + webGLPrecision: key + }); break; }; } @@ -111,7 +117,7 @@ var targetCanvas = fabric.util.createCanvasElement(); // eslint-disable-next-line no-undef var imageBuffer = new ArrayBuffer(width * height * 4); - if (fabric.forceGLPutImageData) { + if (config.forceGLPutImageData) { this.imageBuffer = imageBuffer; this.copyGLTo2D = copyGLTo2DPutImageData; return; diff --git a/src/mixins/itext_key_behavior.mixin.ts b/src/mixins/itext_key_behavior.mixin.ts index 9982cdf3340..8a6c4dab0be 100644 --- a/src/mixins/itext_key_behavior.mixin.ts +++ b/src/mixins/itext_key_behavior.mixin.ts @@ -1,4 +1,8 @@ //@ts-nocheck + +import { config } from "../config"; + + (function(global) { var fabric = global.fabric; fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.prototype */ { @@ -243,7 +247,7 @@ this.removeStyleFromTo(removeFrom, removeTo); } if (insertedText.length) { - if (fromPaste && insertedText.join('') === fabric.copiedText && !fabric.disableStyleCopyPaste) { + if (fromPaste && insertedText.join('') === fabric.copiedText && !config.disableStyleCopyPaste) { copiedStyle = fabric.copiedTextStyle; } this.insertNewStyleBlock(insertedText, selectionStart, copiedStyle); @@ -289,7 +293,7 @@ } fabric.copiedText = this.getSelectedText(); - if (!fabric.disableStyleCopyPaste) { + if (!config.disableStyleCopyPaste) { fabric.copiedTextStyle = this.getSelectionStyles(this.selectionStart, this.selectionEnd, true); } else { diff --git a/src/shapes/object.class.ts b/src/shapes/object.class.ts index e791ec7bf6e..80327f919de 100644 --- a/src/shapes/object.class.ts +++ b/src/shapes/object.class.ts @@ -1,4 +1,5 @@ //@ts-nocheck +import { config } from '../config'; import { VERSION } from '../constants'; import { Point } from '../point.class'; import { capValue } from '../util/misc/capValue'; @@ -666,7 +667,7 @@ import { capValue } from '../util/misc/capValue'; }, /** - * Limit the cache dimensions so that X * Y do not cross fabric.perfLimitSizeTotal + * Limit the cache dimensions so that X * Y do not cross config.perfLimitSizeTotal * and each side do not cross fabric.cacheSideLimit * those numbers are configurable so that you can get as much detail as you want * making bargain with performances. @@ -681,9 +682,9 @@ import { capValue } from '../util/misc/capValue'; * @return {Object}.zoomY zoomY zoom value to unscale the canvas before drawing cache */ _limitCacheSize: function(dims) { - var perfLimitSizeTotal = fabric.perfLimitSizeTotal, + var perfLimitSizeTotal = config.perfLimitSizeTotal, width = dims.width, height = dims.height, - max = fabric.maxCacheSideLimit, min = fabric.minCacheSideLimit; + max = config.maxCacheSideLimit, min = config.minCacheSideLimit; if (width <= max && height <= max && width * height <= perfLimitSizeTotal) { if (width < min) { dims.width = min; @@ -756,7 +757,7 @@ import { capValue } from '../util/misc/capValue'; } var canvas = this._cacheCanvas, dims = this._limitCacheSize(this._getCacheCanvasDimensions()), - minCacheSize = fabric.minCacheSideLimit, + minCacheSize = config.minCacheSideLimit, width = dims.width, height = dims.height, drawingWidth, drawingHeight, zoomX = dims.zoomX, zoomY = dims.zoomY, dimensionsChanged = width !== this.cacheWidth || height !== this.cacheHeight, @@ -1417,11 +1418,11 @@ import { capValue } from '../util/misc/capValue'; multY = (canvas && canvas.viewportTransform[3]) || 1, scaling = shadow.nonScaling ? new Point(1, 1) : this.getObjectScaling(); if (canvas && canvas._isRetinaScaling()) { - multX *= fabric.devicePixelRatio; - multY *= fabric.devicePixelRatio; + multX *= config.devicePixelRatio; + multY *= config.devicePixelRatio; } ctx.shadowColor = shadow.color; - ctx.shadowBlur = shadow.blur * fabric.browserShadowBlurConstant * + ctx.shadowBlur = shadow.blur * config.browserShadowBlurConstant * (multX + multY) * (scaling.x + scaling.y) / 4; ctx.shadowOffsetX = shadow.offsetX * multX * scaling.x; ctx.shadowOffsetY = shadow.offsetY * multY * scaling.y; @@ -1682,7 +1683,7 @@ import { capValue } from '../util/misc/capValue'; var utils = fabric.util, origParams = utils.saveObjectTransform(this), originalGroup = this.group, originalShadow = this.shadow, abs = Math.abs, - retinaScaling = options.enableRetinaScaling ? Math.max(fabric.devicePixelRatio, 1) : 1, + retinaScaling = options.enableRetinaScaling ? Math.max(config.devicePixelRatio, 1) : 1, multiplier = (options.multiplier || 1) * retinaScaling; delete this.group; if (options.withoutTransform) { diff --git a/src/static_canvas.class.ts b/src/static_canvas.class.ts index 287cb57fb13..9f6e3a45d0d 100644 --- a/src/static_canvas.class.ts +++ b/src/static_canvas.class.ts @@ -1,4 +1,5 @@ //@ts-nocheck +import { config } from './config'; import { VERSION } from './constants'; import { Point } from './point.class'; import { removeFromArray } from './util/internals'; @@ -213,7 +214,7 @@ import { removeFromArray } from './util/internals'; * @private */ _isRetinaScaling: function() { - return (fabric.devicePixelRatio > 1 && this.enableRetinaScaling); + return (config.devicePixelRatio > 1 && this.enableRetinaScaling); }, /** @@ -221,7 +222,7 @@ import { removeFromArray } from './util/internals'; * @return {Number} retinaScaling if applied, otherwise 1; */ getRetinaScaling: function() { - return this._isRetinaScaling() ? Math.max(1, fabric.devicePixelRatio) : 1; + return this._isRetinaScaling() ? Math.max(1, config.devicePixelRatio) : 1; }, /** @@ -231,7 +232,7 @@ import { removeFromArray } from './util/internals'; if (!this._isRetinaScaling()) { return; } - var scaleRatio = fabric.devicePixelRatio; + var scaleRatio = config.devicePixelRatio; this.__initRetinaScaling(scaleRatio, this.lowerCanvasEl, this.contextContainer); if (this.upperCanvasEl) { this.__initRetinaScaling(scaleRatio, this.upperCanvasEl, this.contextTop); diff --git a/src/typedefs.ts b/src/typedefs.ts index a9712952819..8de7d423f9f 100644 --- a/src/typedefs.ts +++ b/src/typedefs.ts @@ -55,4 +55,10 @@ export type TransformEvent = TEvent & T & { transform: { target: any } +} + +export const enum WebGLPrecision { + low = 'lowp', + mediump = 'mediump', + high = 'highp' } \ No newline at end of file diff --git a/src/util/misc/svgParsing.ts b/src/util/misc/svgParsing.ts index 2c8ea233e1f..c0fc1d39024 100644 --- a/src/util/misc/svgParsing.ts +++ b/src/util/misc/svgParsing.ts @@ -2,6 +2,7 @@ import { fabric } from '../../../HEADER'; import { SVGElementName, SupportedSVGUnit, TMat2D } from '../../typedefs'; import { DEFAULT_SVG_FONT_SIZE } from '../../constants'; import { toFixed } from './toFixed'; +import { config } from '../../config'; /** * Returns array of attributes for given svg that fabric parses * @memberOf fabric.util @@ -38,7 +39,7 @@ export const parseUnit = (value: string, fontSize: number) => { if (!fontSize) { fontSize = DEFAULT_SVG_FONT_SIZE; } - const dpi = fabric.DPI; + const dpi = config.DPI; switch (unit?.[0]) { case SupportedSVGUnit.mm: return number * dpi / 25.4; diff --git a/src/util/path.ts b/src/util/path.ts index d1625663e19..62e4875288e 100644 --- a/src/util/path.ts +++ b/src/util/path.ts @@ -1,4 +1,6 @@ //@ts-nocheck + +import { config } from "../config"; import { commaWsp, rePathCommand } from "../parser/constants"; import { Point } from '../point.class'; @@ -127,7 +129,7 @@ import { Point } from '../point.class'; // TODO: can we normalize this with the starting points set at 0 and then translated the bbox? function getBoundsOfCurve(x0, y0, x1, y1, x2, y2, x3, y3) { var argsString; - if (fabric.cachesBoundsOfCurve) { + if (config.cachesBoundsOfCurve) { argsString = _join.call(arguments); if (fabric.boundsOfCurveCache[argsString]) { return fabric.boundsOfCurveCache[argsString]; @@ -201,7 +203,7 @@ import { Point } from '../point.class'; y: max.apply(null, bounds[1]) } ]; - if (fabric.cachesBoundsOfCurve) { + if (config.cachesBoundsOfCurve) { fabric.boundsOfCurveCache[argsString] = result; } return result; diff --git a/test/unit/canvas.js b/test/unit/canvas.js index aae4ad1aa07..8bffab19550 100644 --- a/test/unit/canvas.js +++ b/test/unit/canvas.js @@ -115,13 +115,17 @@ }) } + let ORIGINAL_DPR; + QUnit.module('fabric.Canvas', { beforeEach: function() { upperCanvasEl.style.display = ''; canvas.controlsAboveOverlay = fabric.Canvas.prototype.controlsAboveOverlay; canvas.preserveObjectStacking = fabric.Canvas.prototype.preserveObjectStacking; + ORIGINAL_DPR = fabric.config.devicePixelRatio; }, - afterEach: function() { + afterEach: function () { + fabric.config.configure({ devicePixelRatio: ORIGINAL_DPR }); canvas.viewportTransform = [1, 0, 0, 1, 0, 0]; canvas.clear(); canvas.cancelRequestedRender(); @@ -2088,8 +2092,7 @@ parentEl.className = 'rootNode'; parentEl.appendChild(el); - var originalDevicePixelRatio = fabric.devicePixelRatio; - fabric.devicePixelRatio = 1.25; + fabric.config.configure({ devicePixelRatio: 1.25 }); assert.equal(parentEl.firstChild, el, 'canvas should be appended at partentEl'); assert.equal(parentEl.childNodes.length, 1, 'parentEl has 1 child only'); @@ -2142,7 +2145,6 @@ assert.equal(el.width, 200, 'restored width'); assert.equal(el.height, 200, 'restored height'); - fabric.devicePixelRatio = originalDevicePixelRatio; }); QUnit.test('dispose + set dimensions', function (assert) { @@ -2153,8 +2155,7 @@ parentEl.className = 'rootNode'; parentEl.appendChild(el); - var originalDevicePixelRatio = fabric.devicePixelRatio; - fabric.devicePixelRatio = 1.25; + fabric.config.configure({ devicePixelRatio: 1.25 }); assert.equal(parentEl.firstChild, el, 'canvas should be appended at partentEl'); assert.equal(parentEl.childNodes.length, 1, 'parentEl has 1 child only'); @@ -2175,8 +2176,6 @@ assert.equal(el.width, 500, 'restored width'); assert.equal(el.height, 500, 'restored height'); - fabric.devicePixelRatio = originalDevicePixelRatio; - }); // QUnit.test('dispose', function(assert) { diff --git a/test/unit/canvas_static.js b/test/unit/canvas_static.js index caf0f204d6c..1840a4f6d36 100644 --- a/test/unit/canvas_static.js +++ b/test/unit/canvas_static.js @@ -197,6 +197,7 @@ afterEach: function() { canvas.cancelRequestedRender(); canvas2.cancelRequestedRender(); + fabric.config.configure({ devicePixelRatio: 1 }); } }); @@ -590,15 +591,14 @@ QUnit.test('toDataURL with enableRetinaScaling: true and no multiplier', function(assert) { var done = assert.async(); - fabric.devicePixelRatio = 2; + fabric.config.configure({ devicePixelRatio: 2 }); var c = new fabric.StaticCanvas(null, { enableRetinaScaling: true, width: 10, height: 10 }); var dataUrl = c.toDataURL({ enableRetinaScaling: true }); c.cancelRequestedRender(); var img = fabric.document.createElement('img'); img.onload = function() { - assert.equal(img.width, c.width * fabric.devicePixelRatio, 'output width is bigger'); - assert.equal(img.height, c.height * fabric.devicePixelRatio, 'output height is bigger'); - fabric.devicePixelRatio = 1; + assert.equal(img.width, c.width * fabric.config.devicePixelRatio, 'output width is bigger'); + assert.equal(img.height, c.height * fabric.config.devicePixelRatio, 'output height is bigger'); done(); }; img.src = dataUrl; @@ -606,15 +606,14 @@ QUnit.test('toDataURL with enableRetinaScaling: true and multiplier = 1', function(assert) { var done = assert.async(); - fabric.devicePixelRatio = 2; + fabric.config.configure({ devicePixelRatio: 2 }); var c = new fabric.StaticCanvas(null, { enableRetinaScaling: true, width: 10, height: 10 }); var dataUrl = c.toDataURL({ enableRetinaScaling: true, multiplier: 1 }); c.cancelRequestedRender(); var img = fabric.document.createElement('img'); img.onload = function() { - assert.equal(img.width, c.width * fabric.devicePixelRatio, 'output width is bigger'); - assert.equal(img.height, c.height * fabric.devicePixelRatio, 'output height is bigger'); - fabric.devicePixelRatio = 1; + assert.equal(img.width, c.width * fabric.config.devicePixelRatio, 'output width is bigger'); + assert.equal(img.height, c.height * fabric.config.devicePixelRatio, 'output height is bigger'); done(); }; img.src = dataUrl; @@ -622,15 +621,14 @@ QUnit.test('toDataURL with enableRetinaScaling: true and multiplier = 3', function(assert) { var done = assert.async(); - fabric.devicePixelRatio = 2; + fabric.config.configure({ devicePixelRatio: 2 }); var c = new fabric.StaticCanvas(null, { enableRetinaScaling: true, width: 10, height: 10 }); var dataUrl = c.toDataURL({ enableRetinaScaling: true, multiplier: 3 }); c.cancelRequestedRender(); var img = fabric.document.createElement('img'); img.onload = function() { - assert.equal(img.width, c.width * fabric.devicePixelRatio * 3, 'output width is bigger by 6'); - assert.equal(img.height, c.height * fabric.devicePixelRatio * 3, 'output height is bigger by 6'); - fabric.devicePixelRatio = 1; + assert.equal(img.width, c.width * fabric.config.devicePixelRatio * 3, 'output width is bigger by 6'); + assert.equal(img.height, c.height * fabric.config.devicePixelRatio * 3, 'output height is bigger by 6'); done(); }; img.src = dataUrl; @@ -638,7 +636,7 @@ QUnit.test('toDataURL with enableRetinaScaling: false and no multiplier', function(assert) { var done = assert.async(); - fabric.devicePixelRatio = 2; + fabric.config.configure({ devicePixelRatio: 2 }); var c = new fabric.StaticCanvas(null, { enableRetinaScaling: true, width: 10, height: 10 }); var dataUrl = c.toDataURL({ enableRetinaScaling: false }); c.cancelRequestedRender(); @@ -646,7 +644,6 @@ img.onload = function() { assert.equal(img.width, c.width, 'output width is not bigger'); assert.equal(img.height, c.height, 'output height is not bigger'); - fabric.devicePixelRatio = 1; done(); }; img.src = dataUrl; @@ -654,7 +651,7 @@ QUnit.test('toDataURL with enableRetinaScaling: false and multiplier = 1', function(assert) { var done = assert.async(); - fabric.devicePixelRatio = 2; + fabric.config.configure({ devicePixelRatio: 2 }); var c = new fabric.StaticCanvas(null, { enableRetinaScaling: true, width: 10, height: 10 }); var dataUrl = c.toDataURL({ enableRetinaScaling: false, multiplier: 1 }); c.cancelRequestedRender(); @@ -662,7 +659,6 @@ img.onload = function() { assert.equal(img.width, c.width, 'output width is not bigger'); assert.equal(img.height, c.height, 'output height is not bigger'); - fabric.devicePixelRatio = 1; done(); }; img.src = dataUrl; @@ -670,7 +666,7 @@ QUnit.test('toDataURL with enableRetinaScaling: false and multiplier = 3', function(assert) { var done = assert.async(); - fabric.devicePixelRatio = 2; + fabric.config.configure({ devicePixelRatio: 2 }); var c = new fabric.StaticCanvas(null, { enableRetinaScaling: true, width: 10, height: 10 }); var dataUrl = c.toDataURL({ enableRetinaScaling: false, multiplier: 3 }); c.cancelRequestedRender(); @@ -678,7 +674,6 @@ img.onload = function() { assert.equal(img.width, c.width * 3, 'output width is bigger by 3'); assert.equal(img.height, c.height * 3, 'output height is bigger by 3'); - fabric.devicePixelRatio = 1; done(); }; img.src = dataUrl; @@ -686,7 +681,7 @@ QUnit.test('toDataURL with enableRetinaScaling: false', function(assert) { var done = assert.async(); - fabric.devicePixelRatio = 2; + fabric.config.configure({ devicePixelRatio: 2 }); var c = new fabric.StaticCanvas(null, { enableRetinaScaling: true, width: 10, height: 10 }); var dataUrl = c.toDataURL({ enableRetinaScaling: false }); c.cancelRequestedRender(); @@ -694,7 +689,6 @@ img.onload = function() { assert.equal(img.width, c.width, 'output width is bigger'); assert.equal(img.height, c.height, 'output height is bigger'); - fabric.devicePixelRatio = 1; done(); }; img.src = dataUrl; @@ -1876,37 +1870,37 @@ QUnit.test('_isRetinaScaling', function(assert) { canvas.enableRetinaScaling = true; - fabric.devicePixelRatio = 2; + fabric.config.configure({ devicePixelRatio: 2 }); var isScaling = canvas._isRetinaScaling(); assert.equal(isScaling, true, 'retina > 1 and enabled'); canvas.enableRetinaScaling = false; - fabric.devicePixelRatio = 2; + fabric.config.configure({ devicePixelRatio: 2 }); var isScaling = canvas._isRetinaScaling(); assert.equal(isScaling, false, 'retina > 1 and disabled'); canvas.enableRetinaScaling = false; - fabric.devicePixelRatio = 1; + fabric.config.configure({ devicePixelRatio: 1 }); var isScaling = canvas._isRetinaScaling(); assert.equal(isScaling, false, 'retina = 1 and disabled'); canvas.enableRetinaScaling = true; - fabric.devicePixelRatio = 1; + fabric.config.configure({ devicePixelRatio: 1 }); var isScaling = canvas._isRetinaScaling(); assert.equal(isScaling, false, 'retina = 1 and enabled'); }); QUnit.test('getRetinaScaling', function(assert) { canvas.enableRetinaScaling = true; - fabric.devicePixelRatio = 1; + fabric.config.configure({ devicePixelRatio: 1 }); var scaling = canvas.getRetinaScaling(); assert.equal(scaling, 1, 'retina is devicePixelRatio'); - fabric.devicePixelRatio = 2; + fabric.config.configure({ devicePixelRatio: 2 }); var scaling = canvas.getRetinaScaling(); assert.equal(scaling, 2, 'retina is devicePixelRatio'); - fabric.devicePixelRatio = 2; + fabric.config.configure({ devicePixelRatio: 2 }); canvas.enableRetinaScaling = false; var scaling = canvas.getRetinaScaling(); assert.equal(scaling, 1, 'retina is disabled, 1'); diff --git a/test/unit/itext.js b/test/unit/itext.js index f2413038479..817374088a9 100644 --- a/test/unit/itext.js +++ b/test/unit/itext.js @@ -812,9 +812,16 @@ }); }); - QUnit.module('fabric.IText with canvas.enableRetinaScaling = true', function() { + QUnit.module('fabric.IText with canvas.enableRetinaScaling = true', function (hooks) { + let DPR; + hooks.before(function () { + DPR = fabric.config.devicePixelRatio; + fabric.config.configure({ devicePixelRatio: 2 }); + }); + hooks.after(function () { + fabric.config.configure({ devicePixelRatio: DPR }); + }); QUnit.test('hiddenTextarea does not move DOM', function(assert) { - fabric.devicePixelRatio = 2; var iText = new fabric.IText('a', { fill: '#ffffff', fontSize: 50 }); var canvas2 = new fabric.Canvas(null, { width: 800, height: 800, renderOnAddRemove: false, enableRetinaScaling: true }); canvas2.setDimensions({ width: 100, height: 100 }, { cssOnly: true }); @@ -848,7 +855,6 @@ assert.equal(Math.round(parseInt(iText.hiddenTextarea.style.left)), 100, 'left is scaled with CSS'); iText.exitEditing(); canvas2.cancelRequestedRender(); - fabric.devicePixelRatio = 1; }); }); }); diff --git a/test/unit/itext_click_behaviour.js b/test/unit/itext_click_behaviour.js index fb5af39f3dd..bcf67de887f 100644 --- a/test/unit/itext_click_behaviour.js +++ b/test/unit/itext_click_behaviour.js @@ -264,9 +264,17 @@ }); }); - QUnit.module('iText click interaction with canvas.enableRetinaScaling = true', function(hooks) { - hooks.beforeEach(function() { - fabric.devicePixelRatio = 2; + QUnit.module('iText click interaction with canvas.enableRetinaScaling = true', function (hooks) { + let DPR; + hooks.beforeEach(function () { + DPR = fabric.config.devicePixelRatio; + fabric.config.configure({ devicePixelRatio: 2 }); + canvas = new fabric.Canvas(null, { + enableRetinaScaling: true, + }); + }); + hooks.afterEach(function () { + fabric.config.configure({ devicePixelRatio: DPR }); canvas = new fabric.Canvas(null, { enableRetinaScaling: true, }); diff --git a/test/unit/itext_key_behaviour.js b/test/unit/itext_key_behaviour.js index 3671b7b4a16..5924ff00e09 100644 --- a/test/unit/itext_key_behaviour.js +++ b/test/unit/itext_key_behaviour.js @@ -276,16 +276,16 @@ assert.equal(fabric.copiedTextStyle[1].fontSize, 25, 'style took fontSize from text element'); }); - QUnit.test('copy with fabric.disableStyleCopyPaste', function(assert) { + QUnit.test('copy with fabric.config.disableStyleCopyPaste', function(assert) { var event = { stopPropagation: function(){}, preventDefault: function(){} }; var iText = new fabric.IText('test', { fontSize: 25, styles: { 0: { 0: { fill: 'red' }, 1: { fill: 'blue' }}}}); iText.selectionStart = 0; iText.selectionEnd = 2; - fabric.disableStyleCopyPaste = true; + fabric.config.configure({ disableStyleCopyPaste: true }); iText.copy(event); assert.equal(fabric.copiedText, 'te', 'it copied first 2 characters'); assert.equal(fabric.copiedTextStyle, null, 'style is not cloned'); - fabric.disableStyleCopyPaste = false; + fabric.config.configure({ disableStyleCopyPaste: false }); }); QUnit.test('removeChars', function(assert) { diff --git a/test/unit/object.js b/test/unit/object.js index 1f7fb30f871..50645e2e8c1 100644 --- a/test/unit/object.js +++ b/test/unit/object.js @@ -3,11 +3,13 @@ var canvas = this.canvas = new fabric.StaticCanvas(null, {enableRetinaScaling: false}); QUnit.module('fabric.Object', { - afterEach: function() { - fabric.perfLimitSizeTotal = 2097152; - fabric.maxCacheSideLimit = 4096; - fabric.minCacheSideLimit = 256; - fabric.devicePixelRatio = 1; + afterEach: function () { + fabric.config.configure({ + perfLimitSizeTotal: 2097152, + maxCacheSideLimit: 4096, + minCacheSideLimit: 256, + devicePixelRatio: 1, + }); canvas.enableRetinaScaling = false; canvas.setZoom(1); canvas.clear(); @@ -387,12 +389,11 @@ QUnit.test('cloneAsImage with retina scaling enabled', function(assert) { var cObj = new fabric.Rect({ width: 100, height: 100, fill: 'red', strokeWidth: 0 }); - fabric.devicePixelRatio = 2; + fabric.config.configure({ devicePixelRatio: 2 }); var image = cObj.cloneAsImage({ enableRetinaScaling: true }); assert.ok(image); assert.ok(image instanceof fabric.Image); assert.equal(image.width, 200, 'the image has been scaled by retina'); - fabric.devicePixelRatio = 1; }); QUnit.test('toCanvasElement', function(assert) { @@ -1094,7 +1095,7 @@ QUnit.test('getTotalObjectScaling with retina', function(assert) { var object = new fabric.Object({ scaleX: 3, scaleY: 2}); canvas.enableRetinaScaling = true; - fabric.devicePixelRatio = 4; + fabric.config.configure({ devicePixelRatio: 4 }); canvas.add(object); var objectScale = object.getTotalObjectScaling(); assert.ok(objectScale instanceof fabric.Point); @@ -1205,9 +1206,11 @@ }); QUnit.test('_updateCacheCanvas check if cache canvas should be updated', function(assert) { - fabric.perfLimitSizeTotal = 10000; - fabric.maxCacheSideLimit = 4096; - fabric.minCacheSideLimit = 1; + fabric.config.configure({ + perfLimitSizeTotal: 10000, + maxCacheSideLimit: 4096, + minCacheSideLimit: 1 + }); var object = new fabric.Object({ width: 10, height: 10, strokeWidth: 0 }); object._createCacheCanvas(); assert.equal(object.cacheWidth, 12, 'current cache dimensions are saved'); @@ -1225,9 +1228,11 @@ }); QUnit.test('_limitCacheSize limit min to 256', function(assert) { - fabric.perfLimitSizeTotal = 50000; - fabric.maxCacheSideLimit = 4096; - fabric.minCacheSideLimit = 256; + fabric.config.configure({ + perfLimitSizeTotal: 50000, + maxCacheSideLimit: 4096, + minCacheSideLimit: 256 + }); var object = new fabric.Object({ width: 200, height: 200, strokeWidth: 0 }); var dims = object._getCacheCanvasDimensions(); var zoomX = dims.zoomX; @@ -1241,9 +1246,11 @@ }); QUnit.test('_limitCacheSize does not limit if not necessary', function(assert) { - fabric.perfLimitSizeTotal = 1000000; - fabric.maxCacheSideLimit = 4096; - fabric.minCacheSideLimit = 256; + fabric.config.configure({ + perfLimitSizeTotal: 1000000, + maxCacheSideLimit: 4096, + minCacheSideLimit: 256 + }); var object = new fabric.Object({ width: 400, height: 400, strokeWidth: 0 }); var dims = object._getCacheCanvasDimensions(); var zoomX = dims.zoomX; @@ -1257,9 +1264,11 @@ }); QUnit.test('_limitCacheSize does cap up minCacheSideLimit', function(assert) { - fabric.perfLimitSizeTotal = 10000; - fabric.maxCacheSideLimit = 4096; - fabric.minCacheSideLimit = 256; + fabric.config.configure({ + perfLimitSizeTotal: 10000, + maxCacheSideLimit: 4096, + minCacheSideLimit: 256 + }); var object = new fabric.Object({ width: 400, height: 400, strokeWidth: 0 }); var dims = object._getCacheCanvasDimensions(); var width = dims.width; @@ -1275,9 +1284,11 @@ }); QUnit.test('_limitCacheSize does cap up if necessary', function(assert) { - fabric.perfLimitSizeTotal = 1000000; - fabric.maxCacheSideLimit = 4096; - fabric.minCacheSideLimit = 256; + fabric.config.configure({ + perfLimitSizeTotal: 1000000, + maxCacheSideLimit: 4096, + minCacheSideLimit: 256 + }); var object = new fabric.Object({ width: 2046, height: 2046, strokeWidth: 0 }); var dims = object._getCacheCanvasDimensions(); var width = dims.width; @@ -1293,25 +1304,29 @@ }); QUnit.test('_limitCacheSize does cap up if necessary to maxCacheSideLimit', function(assert) { - fabric.perfLimitSizeTotal = 100000000; - fabric.maxCacheSideLimit = 4096; - fabric.minCacheSideLimit = 256; + fabric.config.configure({ + perfLimitSizeTotal: 100000000, + maxCacheSideLimit: 4096, + minCacheSideLimit: 256 + }); var object = new fabric.Object({ width: 8192, height: 8192, strokeWidth: 0 }); var dims = object._getCacheCanvasDimensions(); var zoomX = dims.zoomX; var zoomY = dims.zoomY; var limitedDims = object._limitCacheSize(dims); assert.equal(dims, limitedDims, 'object is mutated'); - assert.equal(dims.width, fabric.maxCacheSideLimit, 'width is capped to max allowed by fabric'); - assert.equal(dims.height, fabric.maxCacheSideLimit, 'height is capped to max allowed by fabric'); + assert.equal(dims.width, fabric.config.maxCacheSideLimit, 'width is capped to max allowed by fabric'); + assert.equal(dims.height, fabric.config.maxCacheSideLimit, 'height is capped to max allowed by fabric'); assert.equal(dims.zoomX, zoomX * 4096 / 8194, 'zoom factor X gets updated to represent the shrink'); assert.equal(dims.zoomY, zoomY * 4096 / 8194, 'zoom factor Y gets updated to represent the shrink'); }); QUnit.test('_limitCacheSize does cap up if necessary to maxCacheSideLimit, different AR', function(assert) { - fabric.perfLimitSizeTotal = 100000000; - fabric.maxCacheSideLimit = 4096; - fabric.minCacheSideLimit = 256; + fabric.config.configure({ + perfLimitSizeTotal: 100000000, + maxCacheSideLimit: 4096, + minCacheSideLimit: 256 + }); var object = new fabric.Object({ width: 16384, height: 8192, strokeWidth: 0 }); var dims = object._getCacheCanvasDimensions(); var width = dims.width; @@ -1320,10 +1335,10 @@ var zoomY = dims.zoomY; var limitedDims = object._limitCacheSize(dims); assert.equal(dims, limitedDims, 'object is mutated'); - assert.equal(dims.width, fabric.maxCacheSideLimit, 'width is capped to max allowed by fabric'); - assert.equal(dims.height, fabric.maxCacheSideLimit, 'height is capped to max allowed by fabric'); - assert.equal(dims.zoomX, zoomX * fabric.maxCacheSideLimit / width, 'zoom factor X gets updated to represent the shrink'); - assert.equal(dims.zoomY, zoomY * fabric.maxCacheSideLimit / height, 'zoom factor Y gets updated to represent the shrink'); + assert.equal(dims.width, fabric.config.maxCacheSideLimit, 'width is capped to max allowed by fabric'); + assert.equal(dims.height, fabric.config.maxCacheSideLimit, 'height is capped to max allowed by fabric'); + assert.equal(dims.zoomX, zoomX * fabric.config.maxCacheSideLimit / width, 'zoom factor X gets updated to represent the shrink'); + assert.equal(dims.zoomY, zoomY * fabric.config.maxCacheSideLimit / height, 'zoom factor Y gets updated to represent the shrink'); }); QUnit.test('_setShadow', function(assert) { @@ -1343,12 +1358,12 @@ assert.equal(context.shadowOffsetX, object.shadow.offsetX, 'shadow offsetX is set'); assert.equal(context.shadowOffsetY, object.shadow.offsetY, 'shadow offsetY is set'); assert.equal(context.shadowBlur, object.shadow.blur, 'shadow blur is set'); - fabric.browserShadowBlurConstant = 1.5; + fabric.config.configure({ browserShadowBlurConstant: 1.5 }); object._setShadow(context); assert.equal(context.shadowOffsetX, object.shadow.offsetX, 'shadow offsetX is unchanged with browserConstant'); assert.equal(context.shadowOffsetY, object.shadow.offsetY, 'shadow offsetY is unchanged with browserConstant'); assert.equal(context.shadowBlur, object.shadow.blur * 1.5, 'shadow blur is affected with browserConstant'); - fabric.browserShadowBlurConstant = 1; + fabric.config.configure({ browserShadowBlurConstant: 1 }); object.scaleX = 2; object.scaleY = 3; object._setShadow(context); diff --git a/test/visual/clippath.js b/test/visual/clippath.js index 2de265d2912..88099dd7bc7 100644 --- a/test/visual/clippath.js +++ b/test/visual/clippath.js @@ -1,6 +1,8 @@ -(function() { - fabric.enableGLFiltering = false; - fabric.isWebglSupported = false; +(function () { + fabric.config.configure({ + enableGLFiltering: false, + isWebglSupported: false + }); fabric.Object.prototype.objectCaching = true; var visualTestLoop; if (fabric.isLikelyNode) { diff --git a/test/visual/control_rendering.js b/test/visual/control_rendering.js index dad9909f5b2..bce5c6f7146 100644 --- a/test/visual/control_rendering.js +++ b/test/visual/control_rendering.js @@ -1,31 +1,33 @@ (function() { if (fabric.isLikelyNode) { if (process.env.launcher === 'Firefox') { - fabric.browserShadowBlurConstant = 0.9; + fabric.config.configure({ browserShadowBlurConstant: 0.9 }); } if (process.env.launcher === 'Node') { - fabric.browserShadowBlurConstant = 1; + fabric.config.configure({ browserShadowBlurConstant: 1 }); } if (process.env.launcher === 'Chrome') { - fabric.browserShadowBlurConstant = 1.5; + fabric.config.configure({ browserShadowBlurConstant: 1.5 }); } if (process.env.launcher === 'Edge') { - fabric.browserShadowBlurConstant = 1.75; + fabric.config.configure({ browserShadowBlurConstant: 1.75 }); } } else { if (navigator.userAgent.indexOf('Firefox') !== -1) { - fabric.browserShadowBlurConstant = 0.9; + fabric.config.configure({ browserShadowBlurConstant: 0.9 }); } if (navigator.userAgent.indexOf('Chrome') !== -1) { - fabric.browserShadowBlurConstant = 1.5; + fabric.config.configure({ browserShadowBlurConstant: 1.5 }); } if (navigator.userAgent.indexOf('Edge') !== -1) { - fabric.browserShadowBlurConstant = 1.75; + fabric.config.configure({ browserShadowBlurConstant: 1.75 }); } } - fabric.enableGLFiltering = false; - fabric.isWebglSupported = false; + fabric.config.configure({ + enableGLFiltering: false, + isWebglSupported: false + }); fabric.Object.prototype.objectCaching = true; var visualTestLoop; if (fabric.isLikelyNode) { diff --git a/test/visual/freedraw.js b/test/visual/freedraw.js index 9314f401d40..6151b42fd99 100644 --- a/test/visual/freedraw.js +++ b/test/visual/freedraw.js @@ -2,31 +2,33 @@ (function() { if (fabric.isLikelyNode) { if (process.env.launcher === 'Firefox') { - fabric.browserShadowBlurConstant = 0.9; + fabric.config.configure({ browserShadowBlurConstant: 0.9 }); } if (process.env.launcher === 'Node') { - fabric.browserShadowBlurConstant = 1; + fabric.config.configure({ browserShadowBlurConstant: 1 }); } if (process.env.launcher === 'Chrome') { - fabric.browserShadowBlurConstant = 1.5; + fabric.config.configure({ browserShadowBlurConstant: 1.5 }); } if (process.env.launcher === 'Edge') { - fabric.browserShadowBlurConstant = 1.75; + fabric.config.configure({ browserShadowBlurConstant: 1.75 }); } } else { if (navigator.userAgent.indexOf('Firefox') !== -1) { - fabric.browserShadowBlurConstant = 0.9; + fabric.config.configure({ browserShadowBlurConstant: 0.9 }); } if (navigator.userAgent.indexOf('Chrome') !== -1) { - fabric.browserShadowBlurConstant = 1.5; + fabric.config.configure({ browserShadowBlurConstant: 1.5 }); } if (navigator.userAgent.indexOf('Edge') !== -1) { - fabric.browserShadowBlurConstant = 1.75; + fabric.config.configure({ browserShadowBlurConstant: 1.75 }); } } - fabric.enableGLFiltering = false; - fabric.isWebglSupported = false; + fabric.config.configure({ + enableGLFiltering: false, + isWebglSupported: false + }); fabric.Object.prototype.objectCaching = true; // eslint-disable-next-line var visualTestLoop, compareGoldensTest; diff --git a/test/visual/generic_rendering.js b/test/visual/generic_rendering.js index 9d17b981fde..44d1aa43b52 100644 --- a/test/visual/generic_rendering.js +++ b/test/visual/generic_rendering.js @@ -2,33 +2,34 @@ var getFixture; if (fabric.isLikelyNode) { if (process.env.launcher === 'Firefox') { - fabric.browserShadowBlurConstant = 0.9; + fabric.config.configure({ browserShadowBlurConstant: 0.9 }); } if (process.env.launcher === 'Node') { - fabric.browserShadowBlurConstant = 1; + fabric.config.configure({ browserShadowBlurConstant: 1 }); } if (process.env.launcher === 'Chrome') { - fabric.browserShadowBlurConstant = 1.5; + fabric.config.configure({ browserShadowBlurConstant: 1.5 }); } if (process.env.launcher === 'Edge') { - fabric.browserShadowBlurConstant = 1.75; + fabric.config.configure({ browserShadowBlurConstant: 1.75 }); } - getFixture = global.getFixture; } else { if (navigator.userAgent.indexOf('Firefox') !== -1) { - fabric.browserShadowBlurConstant = 0.9; + fabric.config.configure({ browserShadowBlurConstant: 0.9 }); } if (navigator.userAgent.indexOf('Chrome') !== -1) { - fabric.browserShadowBlurConstant = 1.5; + fabric.config.configure({ browserShadowBlurConstant: 1.5 }); } if (navigator.userAgent.indexOf('Edge') !== -1) { - fabric.browserShadowBlurConstant = 1.75; + fabric.config.configure({ browserShadowBlurConstant: 1.75 }); } getFixture = window.getFixture; } - fabric.enableGLFiltering = false; - fabric.isWebglSupported = false; + fabric.config.configure({ + enableGLFiltering: false, + isWebglSupported: false + }); fabric.Object.prototype.objectCaching = true; var visualTestLoop; if (fabric.isLikelyNode) { diff --git a/test/visual/resize_filter.js b/test/visual/resize_filter.js index f038fda8150..ae68fe9d2ce 100644 --- a/test/visual/resize_filter.js +++ b/test/visual/resize_filter.js @@ -1,6 +1,8 @@ (function() { - fabric.enableGLFiltering = false; - fabric.isWebglSupported = false; + fabric.config.configure({ + enableGLFiltering: false, + isWebglSupported: false + }); var visualTestLoop; var getFixture; var isFirefox = false; diff --git a/test/visual/svg_import.js b/test/visual/svg_import.js index 7230d11e593..3afa6a23a2a 100644 --- a/test/visual/svg_import.js +++ b/test/visual/svg_import.js @@ -1,6 +1,8 @@ (function() { - fabric.enableGLFiltering = false; - fabric.isWebglSupported = false; + fabric.config.configure({ + enableGLFiltering: false, + isWebglSupported: false + }); fabric.Object.prototype.objectCaching = true; var visualTestLoop; var getAsset; diff --git a/test/visual/text.js b/test/visual/text.js index f7a1e417b7f..6b977046923 100644 --- a/test/visual/text.js +++ b/test/visual/text.js @@ -1,6 +1,8 @@ (function() { - fabric.enableGLFiltering = false; - fabric.isWebglSupported = false; + fabric.config.configure({ + enableGLFiltering: false, + isWebglSupported: false + }); var visualTestLoop; if (fabric.isLikelyNode) { fabric.nodeCanvas.registerFont(__dirname + '/../fixtures/Ubuntu-Regular.ttf', { diff --git a/test/visual/toDataURL.js b/test/visual/toDataURL.js index c94b04402be..14c8343ce34 100644 --- a/test/visual/toDataURL.js +++ b/test/visual/toDataURL.js @@ -1,31 +1,33 @@ (function() { if (fabric.isLikelyNode) { if (process.env.launcher === 'Firefox') { - fabric.browserShadowBlurConstant = 0.9; + fabric.config.configure({ browserShadowBlurConstant: 0.9 }); } if (process.env.launcher === 'Node') { - fabric.browserShadowBlurConstant = 1; + fabric.config.configure({ browserShadowBlurConstant: 1 }); } if (process.env.launcher === 'Chrome') { - fabric.browserShadowBlurConstant = 1.5; + fabric.config.configure({ browserShadowBlurConstant: 1.5 }); } if (process.env.launcher === 'Edge') { - fabric.browserShadowBlurConstant = 1.75; + fabric.config.configure({ browserShadowBlurConstant: 1.75 }); } } else { if (navigator.userAgent.indexOf('Firefox') !== -1) { - fabric.browserShadowBlurConstant = 0.9; + fabric.config.configure({ browserShadowBlurConstant: 0.9 }); } if (navigator.userAgent.indexOf('Chrome') !== -1) { - fabric.browserShadowBlurConstant = 1.5; + fabric.config.configure({ browserShadowBlurConstant: 1.5 }); } if (navigator.userAgent.indexOf('Edge') !== -1) { - fabric.browserShadowBlurConstant = 1.75; + fabric.config.configure({ browserShadowBlurConstant: 1.75 }); } } - fabric.enableGLFiltering = false; - fabric.isWebglSupported = false; + fabric.config.configure({ + enableGLFiltering: false, + isWebglSupported: false + }); var visualTestLoop; if (fabric.isLikelyNode) { visualTestLoop = global.visualTestLoop; @@ -219,7 +221,7 @@ function toDataURL10(fabricCanvas, callback) { fabricCanvas.enableRetinaScaling = true; - fabric.devicePixelRatio = 2; + fabric.config.configure({ devicePixelRatio: 2 }); fabricCanvas.setDimensions({ width: 300, height: 300, @@ -261,7 +263,7 @@ function toDataURL11(fabricCanvas, callback) { fabricCanvas.enableRetinaScaling = false; - fabric.devicePixelRatio = 1; + fabric.config.configure({ devicePixelRatio: 1 }); fabricCanvas.setDimensions({ width: 300, height: 300, @@ -302,8 +304,8 @@ }); function toDataURL12(fabricCanvas, callback) { - fabricCanvas.enableRetinaScaling = 2; - fabric.devicePixelRatio = 2; + fabricCanvas.enableRetinaScaling = true; + fabric.config.configure({ devicePixelRatio: 2 }); fabricCanvas.setDimensions({ width: 300, height: 300, @@ -330,7 +332,7 @@ rect.shadow = new fabric.Shadow(shadow); fabricCanvas.add(rect); var dataUrl = fabricCanvas.toDataURL({ multiplier: 0.5, enableRetinaScaling: true }); - fabric.devicePixelRatio = 1; + fabric.config.configure({ devicePixelRatio: 1 }); callback(dataUrl); } diff --git a/test/visual/z_svg_export.js b/test/visual/z_svg_export.js index cd9bc3eddc9..591a7e7b5e0 100644 --- a/test/visual/z_svg_export.js +++ b/test/visual/z_svg_export.js @@ -1,6 +1,8 @@ (function() { - fabric.enableGLFiltering = false; - fabric.isWebglSupported = false; + fabric.config.configure({ + enableGLFiltering: false, + isWebglSupported: false + }); var visualTestLoop; var getAssetName; if (fabric.isLikelyNode) { From f7888956722e8a2d7df665cea34e3fa57231bb73 Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Sat, 27 Aug 2022 04:32:10 +0300 Subject: [PATCH 06/25] fontPaths --- HEADER.js | 3 +-- src/config.ts | 25 +++++++++++++++++++++++++ src/static_canvas.class.ts | 2 +- test/unit/itext.js | 9 +++++---- 4 files changed, 32 insertions(+), 7 deletions(-) diff --git a/HEADER.js b/HEADER.js index c97782f980b..d701be3a136 100644 --- a/HEADER.js +++ b/HEADER.js @@ -78,7 +78,7 @@ fabric.SHARED_ATTRIBUTES = [ fabric.reNonWord = /[ \n\.,;!\?\-]/; -fabric.fontPaths = { }; + fabric.iMatrix = iMatrix; @@ -92,7 +92,6 @@ config.configure({ 1 }); - /** * Cache Object for widths of chars in text rendering. */ diff --git a/src/config.ts b/src/config.ts index b1844f707ef..a396d3ccb74 100644 --- a/src/config.ts +++ b/src/config.ts @@ -112,6 +112,11 @@ export class Configuration { */ cachesBoundsOfCurve = true + /** + * Map of font files + */ + fontPaths: Record = {} + /** * Used in exporting methods (`toObject`, `toJSON`, `toSVG`) * Controls the percision of exported values @@ -125,6 +130,26 @@ export class Configuration { configure(config: Partial> = {}) { Object.assign(this, config); } + + /** + * Map of font files + */ + addFonts(paths: Record = {}) { + this.fontPaths = { + ...this.fontPaths, + ...paths + }; + } + + removeFonts(...fontFamilys: string[]) { + fontFamilys.forEach(fontFamily => { + delete this.fontPaths[fontFamily]; + }); + } + + clearFonts() { + this.fontPaths = {}; + } } export const config = new Configuration(); diff --git a/src/static_canvas.class.ts b/src/static_canvas.class.ts index 9f6e3a45d0d..5dc702313b4 100644 --- a/src/static_canvas.class.ts +++ b/src/static_canvas.class.ts @@ -1279,7 +1279,7 @@ import { removeFromArray } from './util/internals'; createSVGFontFacesMarkup: function() { var markup = '', fontList = { }, obj, fontFamily, style, row, rowIndex, _char, charIndex, i, len, - fontPaths = fabric.fontPaths, objects = []; + fontPaths = config.fontPaths, objects = []; this._objects.forEach(function add(object) { objects.push(object); diff --git a/test/unit/itext.js b/test/unit/itext.js index 817374088a9..310c863d788 100644 --- a/test/unit/itext.js +++ b/test/unit/itext.js @@ -58,6 +58,7 @@ hooks.afterEach(function() { canvas.clear(); canvas.cancelRequestedRender(); + fabric.config.clearFonts(); }); QUnit.test('constructor', function(assert) { @@ -696,10 +697,10 @@ }, fontFamily: 'Plaster' }); - fabric.fontPaths = { + fabric.config.addFonts({ Engagement: 'path-to-engagement-font-file', Plaster: 'path-to-plaster-font-file', - }; + }); canvas.add(iText); assert.equal(typeof iText.toSVG, 'function'); var parser = new DOMParser(), @@ -730,12 +731,12 @@ }, fontFamily: 'Poppins' }); - fabric.fontPaths = { + fabric.config.addFonts({ Engagement: 'path-to-engagement-font-file', Plaster: 'path-to-plaster-font-file', Poppins: 'path-to-poppins-font-file', Lacquer: 'path-to-lacquer-font-file' - }; + }); var subGroup = new fabric.Group([iText1]); var group = new fabric.Group([subGroup, iText2]); canvas.add(group); From 51aef740ed7bc1f47428d898e4fd5211e7227702 Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Sat, 27 Aug 2022 04:45:22 +0300 Subject: [PATCH 07/25] verbose flag on node test testem doesn't log errors for some inane reason --- .github/workflows/node-unit-tests.yml | 2 +- .github/workflows/visual-node.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/node-unit-tests.yml b/.github/workflows/node-unit-tests.yml index f79cddd3ca2..05d6fa81c56 100644 --- a/.github/workflows/node-unit-tests.yml +++ b/.github/workflows/node-unit-tests.yml @@ -26,4 +26,4 @@ jobs: node-version: ${{ matrix.node-version }} - run: npm ci - run: npm run build:fast - - run: npm run test -- -s unit -p 8080 -c node + - run: npm run test -- -s unit -p 8080 -c node -v diff --git a/.github/workflows/visual-node.yml b/.github/workflows/visual-node.yml index e7c07d06c20..b44df961949 100644 --- a/.github/workflows/visual-node.yml +++ b/.github/workflows/visual-node.yml @@ -26,4 +26,4 @@ jobs: node-version: ${{ matrix.node-version }} - run: npm ci - run: npm run build -- -f -x gestures,accessors - - run: npm run test -- -s visual -p 8080 -c node + - run: npm run test -- -s visual -p 8080 -c node -v From aa7f78f810bd39e23d5571e9205aa15128533b62 Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Sat, 27 Aug 2022 04:56:33 +0300 Subject: [PATCH 08/25] Revert "verbose flag on node test" This reverts commit 51aef740ed7bc1f47428d898e4fd5211e7227702. --- .github/workflows/node-unit-tests.yml | 2 +- .github/workflows/visual-node.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/node-unit-tests.yml b/.github/workflows/node-unit-tests.yml index 05d6fa81c56..f79cddd3ca2 100644 --- a/.github/workflows/node-unit-tests.yml +++ b/.github/workflows/node-unit-tests.yml @@ -26,4 +26,4 @@ jobs: node-version: ${{ matrix.node-version }} - run: npm ci - run: npm run build:fast - - run: npm run test -- -s unit -p 8080 -c node -v + - run: npm run test -- -s unit -p 8080 -c node diff --git a/.github/workflows/visual-node.yml b/.github/workflows/visual-node.yml index b44df961949..e7c07d06c20 100644 --- a/.github/workflows/visual-node.yml +++ b/.github/workflows/visual-node.yml @@ -26,4 +26,4 @@ jobs: node-version: ${{ matrix.node-version }} - run: npm ci - run: npm run build -- -f -x gestures,accessors - - run: npm run test -- -s visual -p 8080 -c node -v + - run: npm run test -- -s visual -p 8080 -c node From 684a4cd07db081c6690961b25c940f0fecb74e82 Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Sat, 27 Aug 2022 04:57:35 +0300 Subject: [PATCH 09/25] fix import --- test/visual/generic_rendering.js | 1 + 1 file changed, 1 insertion(+) diff --git a/test/visual/generic_rendering.js b/test/visual/generic_rendering.js index 44d1aa43b52..8cb2515f75c 100644 --- a/test/visual/generic_rendering.js +++ b/test/visual/generic_rendering.js @@ -13,6 +13,7 @@ if (process.env.launcher === 'Edge') { fabric.config.configure({ browserShadowBlurConstant: 1.75 }); } + getFixture = global.getFixture; } else { if (navigator.userAgent.indexOf('Firefox') !== -1) { From 5e15d510c2e87cf1f835430b95f25730b494e263 Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Sat, 27 Aug 2022 05:01:09 +0300 Subject: [PATCH 10/25] Update HEADER.js --- HEADER.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/HEADER.js b/HEADER.js index d701be3a136..9e0496f3dec 100644 --- a/HEADER.js +++ b/HEADER.js @@ -6,6 +6,7 @@ import { iMatrix, VERSION } from './src/constants'; var fabric = fabric || { version: VERSION, config, + iMatrix }; if (typeof exports !== 'undefined') { @@ -79,7 +80,6 @@ fabric.SHARED_ATTRIBUTES = [ fabric.reNonWord = /[ \n\.,;!\?\-]/; -fabric.iMatrix = iMatrix; /** From c7a5b409a34d12453cca38438c9d3daaff523f05 Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Sat, 27 Aug 2022 05:44:36 +0300 Subject: [PATCH 11/25] Update config.ts --- src/config.ts | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/src/config.ts b/src/config.ts index a396d3ccb74..66aecfbaf44 100644 --- a/src/config.ts +++ b/src/config.ts @@ -1,7 +1,8 @@ -import { WebGLPrecision } from "./typedefs" +import { WebGLPrecision } from "./typedefs"; +export type TConfiguration = Partial; -export class Configuration { +class BaseConfiguration { /** * Browser-specific constant to adjust CanvasRenderingContext2D.shadowBlur value, @@ -118,16 +119,21 @@ export class Configuration { fontPaths: Record = {} /** + * Defines the number of fraction digits to use when serializing object values. * Used in exporting methods (`toObject`, `toJSON`, `toSVG`) - * Controls the percision of exported values + * You can use it to increase/decrease precision of such values like left, top, scaleX, scaleY, etc. */ NUM_FRACTION_DIGITS = 4 +} + +export class Configuration extends BaseConfiguration { - constructor(config?: Partial>) { + constructor(config?: TConfiguration) { + super(); this.configure(config); } - configure(config: Partial> = {}) { + configure(config: TConfiguration = {}) { Object.assign(this, config); } @@ -141,7 +147,7 @@ export class Configuration { }; } - removeFonts(...fontFamilys: string[]) { + removeFonts(fontFamilys: string[] = []) { fontFamilys.forEach(fontFamily => { delete this.fontPaths[fontFamily]; }); @@ -150,6 +156,15 @@ export class Configuration { clearFonts() { this.fontPaths = {}; } + + restoreDefaults(keys?: (keyof T)[]) { + const defaults = new BaseConfiguration() as T; + const config = keys?.reduce((acc, key) => { + acc[key] = defaults[key]; + return acc; + }, {} as T) || defaults; + this.configure(config); + } } export const config = new Configuration(); From 9c324da70b4e9c0249f8ad4e8daf9917a297cbef Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Sat, 27 Aug 2022 07:23:09 +0300 Subject: [PATCH 12/25] fix const enum iteration https://github.com/rollup/plugins/issues/463 --- src/filters/base_filter.class.ts | 6 +++--- src/typedefs.ts | 16 +++++++++++++--- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/src/filters/base_filter.class.ts b/src/filters/base_filter.class.ts index 2ac9ce9c4ab..f3b46930bfa 100644 --- a/src/filters/base_filter.class.ts +++ b/src/filters/base_filter.class.ts @@ -1,7 +1,7 @@ //@ts-nocheck import { config } from "../config"; -import { WebGLPrecision } from "../typedefs"; +import { TWebGLPrecision } from "../typedefs"; (function(global) { @@ -77,9 +77,9 @@ import { WebGLPrecision } from "../typedefs"; createProgram: function(gl, fragmentSource, vertexSource) { fragmentSource = fragmentSource || this.fragmentSource; vertexSource = vertexSource || this.vertexSource; - if (config.webGLPrecision !== WebGLPrecision.high) { + if (config.webGLPrecision !== TWebGLPrecision.high) { fragmentSource = fragmentSource.replace( - new RegExp(`precision ${WebGLPrecision.high} float`,'g'), + new RegExp(`precision ${TWebGLPrecision.high} float`,'g'), `precision ${config.webGLPrecision} float` ); } diff --git a/src/typedefs.ts b/src/typedefs.ts index 8de7d423f9f..719a38de70d 100644 --- a/src/typedefs.ts +++ b/src/typedefs.ts @@ -57,8 +57,18 @@ export type TransformEvent = TEvent & T & { } } -export const enum WebGLPrecision { +export const enum TWebGLPrecision { low = 'lowp', - mediump = 'mediump', + medium = 'mediump', high = 'highp' -} \ No newline at end of file +} + +/** + * @todo remove once rollup supports transforming enums... ANNOYING! + * https://github.com/rollup/plugins/issues/463 + */ +export const WebGLPrecision = [ + TWebGLPrecision.low, + TWebGLPrecision.medium, + TWebGLPrecision.high +]; \ No newline at end of file From 479a13781d450f50209d271633fa7b922f75ea4a Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Sat, 27 Aug 2022 07:24:06 +0300 Subject: [PATCH 13/25] fix const enum iteration 9c324da7 https://github.com/rollup/plugins/issues/463 --- src/filters/webgl_backend.class.ts | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/src/filters/webgl_backend.class.ts b/src/filters/webgl_backend.class.ts index 3560abb094a..b5373235fda 100644 --- a/src/filters/webgl_backend.class.ts +++ b/src/filters/webgl_backend.class.ts @@ -1,7 +1,7 @@ //@ts-nocheck import { config } from "../config"; -import { WebGLPrecision } from "../typedefs"; +import { TWebGLPrecision, WebGLPrecision } from "../typedefs"; (function(global) { @@ -9,7 +9,7 @@ import { WebGLPrecision } from "../typedefs"; /** * Tests if webgl supports certain precision * @param {WebGL} Canvas WebGL context to test on - * @param {String} Precision to test can be any of following: 'lowp', 'mediump', 'highp' + * @param {TWebGLPrecision} Precision to test can be any of following * @returns {Boolean} Whether the user's browser WebGL supports given precision. */ function testPrecision(gl, precision){ @@ -36,18 +36,13 @@ import { WebGLPrecision } from "../typedefs"; var canvas = document.createElement('canvas'); var gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl'); var isSupported = false; - // eslint-disable-next-line if (gl) { config.configure({ maxTextureSize: gl.getParameter(gl.MAX_TEXTURE_SIZE) }); - isSupported = fabric.maxTextureSize >= tileSize; - for (const key in WebGLPrecision) { - if (testPrecision(gl, key)) { - config.configure({ - webGLPrecision: key - }); - break; - }; - } + isSupported = config.maxTextureSize >= tileSize; + const percisionKey = WebGLPrecision.find(key => testPrecision(gl, key)); + config.configure({ + webGLPrecision: percisionKey + }); } this.isSupported = isSupported; return isSupported; From cea2d9363f1dd642fd63cff9cbe662ace7de6dd6 Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Sat, 27 Aug 2022 07:24:14 +0300 Subject: [PATCH 14/25] Update config.ts --- src/config.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/config.ts b/src/config.ts index 66aecfbaf44..a6510508d0c 100644 --- a/src/config.ts +++ b/src/config.ts @@ -1,4 +1,4 @@ -import { WebGLPrecision } from "./typedefs"; +import { TWebGLPrecision } from "./typedefs"; export type TConfiguration = Partial; @@ -105,7 +105,7 @@ class BaseConfiguration { /** * WebGL */ - webGLPrecision?: WebGLPrecision + webGLPrecision?: TWebGLPrecision /** * If disabled boundsOfCurveCache is not used. For apps that make heavy usage of pencil drawing probably disabling it is better From 5287f1a0d5a27395348f77a9aa89a68e76183b35 Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Sat, 27 Aug 2022 07:25:02 +0300 Subject: [PATCH 15/25] migrate `NUM_FRACTION_DIGITS` --- src/mixins/itext.svg_export.ts | 5 +++-- src/mixins/object.svg_export.ts | 3 ++- src/pattern.class.ts | 6 +++++- src/shadow.class.ts | 3 ++- src/shapes/circle.class.ts | 2 +- src/shapes/object.class.ts | 12 +----------- src/shapes/path.class.ts | 6 +++++- src/shapes/polyline.class.ts | 6 +++++- src/static_canvas.class.ts | 2 +- src/util/misc/svgParsing.ts | 2 +- test/unit/object.js | 5 +++-- test/unit/text.js | 6 +++--- test/unit/text_to_svg.js | 8 ++++---- test/visual/z_svg_export.js | 2 +- 14 files changed, 37 insertions(+), 31 deletions(-) diff --git a/src/mixins/itext.svg_export.ts b/src/mixins/itext.svg_export.ts index 7b9efb0f406..2412e8c9c1a 100644 --- a/src/mixins/itext.svg_export.ts +++ b/src/mixins/itext.svg_export.ts @@ -1,6 +1,7 @@ //@ts-nocheck import { Color } from "../color"; +import { config } from "../config"; /* _TO_SVG_START_ */ (function(global) { @@ -104,7 +105,7 @@ import { Color } from "../color"; styleProps = this.getSvgSpanStyles(styleDecl, shouldUseWhitespace), fillStyles = styleProps ? 'style="' + styleProps + '"' : '', dy = styleDecl.deltaY, dySpan = '', - NUM_FRACTION_DIGITS = fabric.Object.NUM_FRACTION_DIGITS; + NUM_FRACTION_DIGITS = config.NUM_FRACTION_DIGITS; if (dy) { dySpan = ' dy="' + toFixed(dy, NUM_FRACTION_DIGITS) + '" '; } @@ -169,7 +170,7 @@ import { Color } from "../color"; }, _pushTextBgRect: function(textBgRects, color, left, top, width, height) { - var NUM_FRACTION_DIGITS = fabric.Object.NUM_FRACTION_DIGITS; + var NUM_FRACTION_DIGITS = config.NUM_FRACTION_DIGITS; textBgRects.push( '\t\t element diff --git a/src/shadow.class.ts b/src/shadow.class.ts index 0763d01a1a4..3c641573d1e 100644 --- a/src/shadow.class.ts +++ b/src/shadow.class.ts @@ -1,6 +1,7 @@ //@ts-nocheck import { Color } from "./color"; +import { config } from "./config"; (function(global) { var fabric = global.fabric || (global.fabric = { }), @@ -116,7 +117,7 @@ import { Color } from "./color"; * @return {String} SVG representation of a shadow */ toSVG: function(object) { - var fBoxX = 40, fBoxY = 40, NUM_FRACTION_DIGITS = fabric.Object.NUM_FRACTION_DIGITS, + var fBoxX = 40, fBoxY = 40, NUM_FRACTION_DIGITS = config.NUM_FRACTION_DIGITS, offset = fabric.util.rotateVector( { x: this.offsetX, y: this.offsetY }, fabric.util.degreesToRadians(-object.angle)), diff --git a/src/shapes/circle.class.ts b/src/shapes/circle.class.ts index c87f448b2c4..58100fbf937 100644 --- a/src/shapes/circle.class.ts +++ b/src/shapes/circle.class.ts @@ -177,7 +177,7 @@ Circle.fromElement = function(element, callback) { left = 0, top = 0, radius, - ...otherParsedAttributes, + ...otherParsedAttributes } = fabric.parseAttributes(element, Circle.ATTRIBUTE_NAMES); if (!radius || radius < 0) { diff --git a/src/shapes/object.class.ts b/src/shapes/object.class.ts index 80327f919de..c03fd5f5ba1 100644 --- a/src/shapes/object.class.ts +++ b/src/shapes/object.class.ts @@ -831,7 +831,7 @@ import { capValue } from '../util/misc/capValue'; * @return {Object} Object representation of an instance */ toObject: function(propertiesToInclude) { - var NUM_FRACTION_DIGITS = fabric.Object.NUM_FRACTION_DIGITS, + var NUM_FRACTION_DIGITS = config.NUM_FRACTION_DIGITS, object = { type: this.type, @@ -1913,16 +1913,6 @@ import { capValue } from '../util/misc/capValue'; extend(fabric.Object.prototype, fabric.Observable); - /** - * Defines the number of fraction digits to use when serializing object values. - * You can use it to increase/decrease precision of such values like left, top, scaleX, scaleY, etc. - * @static - * @memberOf fabric.Object - * @constant - * @type Number - */ - fabric.Object.NUM_FRACTION_DIGITS = 2; - /** * * @param {Function} klass diff --git a/src/shapes/path.class.ts b/src/shapes/path.class.ts index 3387d4de2bf..fe7d4720db4 100644 --- a/src/shapes/path.class.ts +++ b/src/shapes/path.class.ts @@ -1,4 +1,8 @@ //@ts-nocheck + +import { config } from "../config"; + + (function(global) { var fabric = global.fabric || (global.fabric = { }), min = fabric.util.array.min, @@ -194,7 +198,7 @@ }, _getOffsetTransform: function() { - var digits = fabric.Object.NUM_FRACTION_DIGITS; + var digits = config.NUM_FRACTION_DIGITS; return ' translate(' + toFixed(-this.pathOffset.x, digits) + ', ' + toFixed(-this.pathOffset.y, digits) + ')'; }, diff --git a/src/shapes/polyline.class.ts b/src/shapes/polyline.class.ts index ae701d628c5..9d2599b3d74 100644 --- a/src/shapes/polyline.class.ts +++ b/src/shapes/polyline.class.ts @@ -1,4 +1,8 @@ //@ts-nocheck + +import { config } from "../config"; + + (function(global) { var fabric = global.fabric || (global.fabric = { }), extend = fabric.util.object.extend, @@ -153,7 +157,7 @@ */ _toSVG: function() { var points = [], diffX = this.pathOffset.x, diffY = this.pathOffset.y, - NUM_FRACTION_DIGITS = fabric.Object.NUM_FRACTION_DIGITS; + NUM_FRACTION_DIGITS = config.NUM_FRACTION_DIGITS; for (var i = 0, len = this.points.length; i < len; i++) { points.push( diff --git a/src/static_canvas.class.ts b/src/static_canvas.class.ts index 5dc702313b4..24de62333ae 100644 --- a/src/static_canvas.class.ts +++ b/src/static_canvas.class.ts @@ -1197,7 +1197,7 @@ import { removeFromArray } from './util/internals'; var width = options.width || this.width, height = options.height || this.height, vpt, viewBox = 'viewBox="0 0 ' + this.width + ' ' + this.height + '" ', - NUM_FRACTION_DIGITS = fabric.Object.NUM_FRACTION_DIGITS; + NUM_FRACTION_DIGITS = config.NUM_FRACTION_DIGITS; if (options.viewBox) { viewBox = 'viewBox="' + diff --git a/src/util/misc/svgParsing.ts b/src/util/misc/svgParsing.ts index c0fc1d39024..2c43ba44006 100644 --- a/src/util/misc/svgParsing.ts +++ b/src/util/misc/svgParsing.ts @@ -134,4 +134,4 @@ export const parsePreserveAspectRatioAttribute = (attribute: string): TPreserveA * @return {String} transform matrix for svg */ export const matrixToSVG = (transform: TMat2D) => - 'matrix(' + transform.map((value) => toFixed(value, fabric.Object.NUM_FRACTION_DIGITS)).join(' ') + ')'; + 'matrix(' + transform.map((value) => toFixed(value, config.NUM_FRACTION_DIGITS)).join(' ') + ')'; diff --git a/test/unit/object.js b/test/unit/object.js index 50645e2e8c1..6376c3200e7 100644 --- a/test/unit/object.js +++ b/test/unit/object.js @@ -227,15 +227,16 @@ function testFractionDigits(fractionDigits, expectedValue) { - fabric.Object.NUM_FRACTION_DIGITS = fractionDigits; + fabric.config.configure({ NUM_FRACTION_DIGITS: fractionDigits }); testedProperties.forEach(function(property) { cObj.set(property, fractionalValue); + console.log(cObj.toObject()[property]) assert.equal(cObj.toObject()[property], expectedValue, 'value of ' + property + ' should have ' + fractionDigits + ' fractional digits'); }, this); - fabric.Object.NUM_FRACTION_DIGITS = fractionDigitsDefault; + fabric.config.configure({ NUM_FRACTION_DIGITS: fractionDigitsDefault }); } testFractionDigits.call(this, 2, 166.67); diff --git a/test/unit/text.js b/test/unit/text.js index 098cec8ec7e..03690ac24f3 100644 --- a/test/unit/text.js +++ b/test/unit/text.js @@ -607,17 +607,17 @@ } } }); - fabric.Object.NUM_FRACTION_DIGITS = 1; + fabric.config.configure({ NUM_FRACTION_DIGITS: 1 }); var SVG_1 = iText.toSVG(); // var SVG_1_EXPECTED = '\t\n\t\ttest foo bar-baz\n\t\n'; //assert.equal(SVG_1, SVG_1_EXPECTED, 'numbers have max 1 decimal'); - fabric.Object.NUM_FRACTION_DIGITS = 3; + fabric.config.configure({ NUM_FRACTION_DIGITS: 3 }); var SVG_2 = iText.toSVG(); // var SVG_2_EXPECTED = '\t\n\t\ttest foo bar-baz\n\t\n'; //assert.equal(SVG_2, SVG_2_EXPECTED, 'numbers have max 3 decimal'); assert.ok(SVG_2.length > SVG_1.length, 'SVG 2 has more decimal'); // put back to 2 or break all tests - fabric.Object.NUM_FRACTION_DIGITS = 2; + fabric.config.configure({ NUM_FRACTION_DIGITS: 2 }); }); QUnit.test('getSvgSpanStyles produces correct output', function(assert) { diff --git a/test/unit/text_to_svg.js b/test/unit/text_to_svg.js index 4f4300f48ef..50774c220ec 100644 --- a/test/unit/text_to_svg.js +++ b/test/unit/text_to_svg.js @@ -24,7 +24,7 @@ assert.equal(removeTranslate(text.toSVG()), removeTranslate(TEXT_SVG_MULTIPLESPACES)); }); QUnit.test('toSVG with deltaY', function(assert) { - fabric.Object.NUM_FRACTION_DIGITS = 0; + fabric.config.configure({ NUM_FRACTION_DIGITS: 0 }); var TEXT_SVG = '\n\t\txx\n\n'; var text = new fabric.Text('xx', { styles: { @@ -37,7 +37,7 @@ } }); assert.equal(removeTranslate(text.toSVG()), removeTranslate(TEXT_SVG)); - fabric.Object.NUM_FRACTION_DIGITS = 2; + fabric.config.configure({ NUM_FRACTION_DIGITS: 2 }); }); QUnit.test('toSVG with font', function(assert) { @@ -56,13 +56,13 @@ assert.equal(removeTranslate(text.toSVG()), removeTranslate(TEXT_SVG_WITH_FONT)); }); QUnit.test('toSVG with text as a clipPath', function(assert) { - fabric.Object.NUM_FRACTION_DIGITS = 0; + fabric.config.configure({ NUM_FRACTION_DIGITS: 0 }); fabric.Object.__uid = 0; var EXPECTED = '\n\n\t\t\ttext as clipPath\n\n\n\n'; var clipPath = new fabric.Text('text as clipPath'); var rect = new fabric.Rect({ width: 200, height: 100 }); rect.clipPath = clipPath; assert.equal(removeTranslate(rect.toSVG()), removeTranslate(EXPECTED)); - fabric.Object.NUM_FRACTION_DIGITS = 2; + fabric.config.configure({ NUM_FRACTION_DIGITS: 2 }); }); })(); diff --git a/test/visual/z_svg_export.js b/test/visual/z_svg_export.js index 591a7e7b5e0..44559f92f20 100644 --- a/test/visual/z_svg_export.js +++ b/test/visual/z_svg_export.js @@ -58,7 +58,7 @@ newModule: 'Export clippaths to SVG', percentage: 0.06, beforeEachHandler: function() { - fabric.Object.NUM_FRACTION_DIGITS = 4; + fabric.config.configure({ NUM_FRACTION_DIGITS: 4 }); fabric.Object.prototype.objectCaching = false; } }); From 7ac4a54204ea5cd9ebb7ea83350914cc9f19ffe6 Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Sat, 27 Aug 2022 07:27:14 +0300 Subject: [PATCH 16/25] Update object.js --- test/unit/object.js | 1 - 1 file changed, 1 deletion(-) diff --git a/test/unit/object.js b/test/unit/object.js index 6376c3200e7..0f9cf0d1340 100644 --- a/test/unit/object.js +++ b/test/unit/object.js @@ -231,7 +231,6 @@ testedProperties.forEach(function(property) { cObj.set(property, fractionalValue); - console.log(cObj.toObject()[property]) assert.equal(cObj.toObject()[property], expectedValue, 'value of ' + property + ' should have ' + fractionDigits + ' fractional digits'); }, this); From 9fb08eb9b1c8abca88f27a6ee630be0688e90e14 Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Sat, 27 Aug 2022 07:35:31 +0300 Subject: [PATCH 17/25] removed `isWebglSupported` redundant flag --- test/visual/clippath.js | 3 +-- test/visual/control_rendering.js | 3 +-- test/visual/freedraw.js | 3 +-- test/visual/generic_rendering.js | 3 +-- test/visual/resize_filter.js | 3 +-- test/visual/svg_import.js | 3 +-- test/visual/text.js | 3 +-- test/visual/toDataURL.js | 3 +-- test/visual/z_svg_export.js | 3 +-- 9 files changed, 9 insertions(+), 18 deletions(-) diff --git a/test/visual/clippath.js b/test/visual/clippath.js index 88099dd7bc7..e37fa682eb2 100644 --- a/test/visual/clippath.js +++ b/test/visual/clippath.js @@ -1,7 +1,6 @@ (function () { fabric.config.configure({ - enableGLFiltering: false, - isWebglSupported: false + enableGLFiltering: false }); fabric.Object.prototype.objectCaching = true; var visualTestLoop; diff --git a/test/visual/control_rendering.js b/test/visual/control_rendering.js index bce5c6f7146..c78e840a83e 100644 --- a/test/visual/control_rendering.js +++ b/test/visual/control_rendering.js @@ -25,8 +25,7 @@ } } fabric.config.configure({ - enableGLFiltering: false, - isWebglSupported: false + enableGLFiltering: false }); fabric.Object.prototype.objectCaching = true; var visualTestLoop; diff --git a/test/visual/freedraw.js b/test/visual/freedraw.js index 6151b42fd99..0fe457437ec 100644 --- a/test/visual/freedraw.js +++ b/test/visual/freedraw.js @@ -26,8 +26,7 @@ } } fabric.config.configure({ - enableGLFiltering: false, - isWebglSupported: false + enableGLFiltering: false }); fabric.Object.prototype.objectCaching = true; // eslint-disable-next-line diff --git a/test/visual/generic_rendering.js b/test/visual/generic_rendering.js index 8cb2515f75c..36e149292f7 100644 --- a/test/visual/generic_rendering.js +++ b/test/visual/generic_rendering.js @@ -28,8 +28,7 @@ getFixture = window.getFixture; } fabric.config.configure({ - enableGLFiltering: false, - isWebglSupported: false + enableGLFiltering: false }); fabric.Object.prototype.objectCaching = true; var visualTestLoop; diff --git a/test/visual/resize_filter.js b/test/visual/resize_filter.js index ae68fe9d2ce..0122c0ca30f 100644 --- a/test/visual/resize_filter.js +++ b/test/visual/resize_filter.js @@ -1,7 +1,6 @@ (function() { fabric.config.configure({ - enableGLFiltering: false, - isWebglSupported: false + enableGLFiltering: false }); var visualTestLoop; var getFixture; diff --git a/test/visual/svg_import.js b/test/visual/svg_import.js index 3afa6a23a2a..51a6fbe7906 100644 --- a/test/visual/svg_import.js +++ b/test/visual/svg_import.js @@ -1,7 +1,6 @@ (function() { fabric.config.configure({ - enableGLFiltering: false, - isWebglSupported: false + enableGLFiltering: false }); fabric.Object.prototype.objectCaching = true; var visualTestLoop; diff --git a/test/visual/text.js b/test/visual/text.js index 6b977046923..9f9c24019e1 100644 --- a/test/visual/text.js +++ b/test/visual/text.js @@ -1,7 +1,6 @@ (function() { fabric.config.configure({ - enableGLFiltering: false, - isWebglSupported: false + enableGLFiltering: false }); var visualTestLoop; if (fabric.isLikelyNode) { diff --git a/test/visual/toDataURL.js b/test/visual/toDataURL.js index 14c8343ce34..0f176b2efce 100644 --- a/test/visual/toDataURL.js +++ b/test/visual/toDataURL.js @@ -25,8 +25,7 @@ } } fabric.config.configure({ - enableGLFiltering: false, - isWebglSupported: false + enableGLFiltering: false }); var visualTestLoop; if (fabric.isLikelyNode) { diff --git a/test/visual/z_svg_export.js b/test/visual/z_svg_export.js index 44559f92f20..b73036c6288 100644 --- a/test/visual/z_svg_export.js +++ b/test/visual/z_svg_export.js @@ -1,7 +1,6 @@ (function() { fabric.config.configure({ - enableGLFiltering: false, - isWebglSupported: false + enableGLFiltering: false }); var visualTestLoop; var getAssetName; From 146fa5ec121740c2e868de208dce4b272eff7ae0 Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Sat, 27 Aug 2022 07:44:08 +0300 Subject: [PATCH 18/25] extract cache --- HEADER.js | 22 ------------------ src/cache.ts | 48 ++++++++++++++++++++++++++++++++++++++++ src/shapes/text.class.ts | 7 +++--- src/util/misc/misc.ts | 22 ------------------ src/util/path.ts | 7 +++--- test/unit/text.js | 6 ++--- test/unit/util.js | 30 ++++++++++++------------- 7 files changed, 74 insertions(+), 68 deletions(-) create mode 100644 src/cache.ts diff --git a/HEADER.js b/HEADER.js index 9e0496f3dec..4782b3eaddd 100644 --- a/HEADER.js +++ b/HEADER.js @@ -92,28 +92,6 @@ config.configure({ 1 }); -/** - * Cache Object for widths of chars in text rendering. - */ -fabric.charWidthsCache = {}; - -/** - * This object contains the result of arc to bezier 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 = { }; - - fabric.initFilterBackend = function() { if (config.enableGLFiltering && fabric.isWebglSupported && fabric.isWebglSupported(config.textureSize)) { console.log(`fabric: max texture size: ${config.maxTextureSize}`); diff --git a/src/cache.ts b/src/cache.ts new file mode 100644 index 00000000000..f0b89add76d --- /dev/null +++ b/src/cache.ts @@ -0,0 +1,48 @@ + +export class Cache { + /** + * Cache Object for widths of chars in text rendering. + */ + charWidthsCache = {} + + + /** + * Clear char widths cache for the given font family or all the cache if no + * fontFamily is specified. + * Use it if you know you are loading fonts in a lazy way and you are not waiting + * for custom fonts to load properly when adding text objects to the canvas. + * If a text object is added when its own font is not loaded yet, you will get wrong + * measurement and so wrong bounding boxes. + * After the font cache is cleared, either change the textObject text content or call + * initDimensions() to trigger a recalculation + * @memberOf fabric.util + * @param {String} [fontFamily] font family to clear + */ + clearFontCache(fontFamily?: string) { + fontFamily = (fontFamily || '').toLowerCase(); + if (!fontFamily) { + this.charWidthsCache = {}; + } + else if (this.charWidthsCache[fontFamily]) { + delete this.charWidthsCache[fontFamily]; + } + } + + /** + * This object contains the result of arc to bezier 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 + */ + 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 + */ + boundsOfCurveCache = {} +} + +export const cache = new Cache(); diff --git a/src/shapes/text.class.ts b/src/shapes/text.class.ts index c8a90f46486..8e8ef340499 100644 --- a/src/shapes/text.class.ts +++ b/src/shapes/text.class.ts @@ -1,5 +1,6 @@ //@ts-nocheck +import { cache } from "../cache"; import { DEFAULT_SVG_FONT_SIZE } from "../constants"; @@ -705,10 +706,10 @@ import { DEFAULT_SVG_FONT_SIZE } from "../constants"; */ getFontCache: function(decl) { var fontFamily = decl.fontFamily.toLowerCase(); - if (!fabric.charWidthsCache[fontFamily]) { - fabric.charWidthsCache[fontFamily] = { }; + if (!cache.charWidthsCache[fontFamily]) { + cache.charWidthsCache[fontFamily] = { }; } - var cache = fabric.charWidthsCache[fontFamily], + var cache = cache.charWidthsCache[fontFamily], cacheProp = decl.fontStyle.toLowerCase() + '_' + (decl.fontWeight + '').toLowerCase(); if (!cache[cacheProp]) { cache[cacheProp] = { }; diff --git a/src/util/misc/misc.ts b/src/util/misc/misc.ts index c101fe25482..e2e2fec85f3 100644 --- a/src/util/misc/misc.ts +++ b/src/util/misc/misc.ts @@ -367,28 +367,6 @@ import { makeBoundingBoxFromPoints } from './boundingBoxFromPoints'; return _isTransparent; }, - /** - * Clear char widths cache for the given font family or all the cache if no - * fontFamily is specified. - * Use it if you know you are loading fonts in a lazy way and you are not waiting - * for custom fonts to load properly when adding text objects to the canvas. - * If a text object is added when its own font is not loaded yet, you will get wrong - * measurement and so wrong bounding boxes. - * After the font cache is cleared, either change the textObject text content or call - * initDimensions() to trigger a recalculation - * @memberOf fabric.util - * @param {String} [fontFamily] font family to clear - */ - clearFabricFontCache: function(fontFamily) { - fontFamily = (fontFamily || '').toLowerCase(); - if (!fontFamily) { - fabric.charWidthsCache = { }; - } - else if (fabric.charWidthsCache[fontFamily]) { - delete fabric.charWidthsCache[fontFamily]; - } - }, - /** * Given current aspect ratio, determines the max width and height that can * respect the total allowed area for the cache. diff --git a/src/util/path.ts b/src/util/path.ts index 62e4875288e..61c62fff1c6 100644 --- a/src/util/path.ts +++ b/src/util/path.ts @@ -1,5 +1,6 @@ //@ts-nocheck +import { cache } from "../cache"; import { config } from "../config"; import { commaWsp, rePathCommand } from "../parser/constants"; import { Point } from '../point.class'; @@ -131,8 +132,8 @@ import { Point } from '../point.class'; var argsString; if (config.cachesBoundsOfCurve) { argsString = _join.call(arguments); - if (fabric.boundsOfCurveCache[argsString]) { - return fabric.boundsOfCurveCache[argsString]; + if (cache.boundsOfCurveCache[argsString]) { + return cache.boundsOfCurveCache[argsString]; } } @@ -204,7 +205,7 @@ import { Point } from '../point.class'; } ]; if (config.cachesBoundsOfCurve) { - fabric.boundsOfCurveCache[argsString] = result; + cache.boundsOfCurveCache[argsString] = result; } return result; } diff --git a/test/unit/text.js b/test/unit/text.js index 03690ac24f3..db4dbcbc35b 100644 --- a/test/unit/text.js +++ b/test/unit/text.js @@ -390,7 +390,7 @@ QUnit.test('getFontCache works with fontWeight numbers', function(assert) { var text = new fabric.Text('xxx', { fontWeight: 400 }); text.initDimensions(); - var cache = fabric.charWidthsCache[text.fontFamily.toLowerCase()]; + var cache = fabric.cache.charWidthsCache[text.fontFamily.toLowerCase()]; var cacheProp = text.fontStyle + '_400'; assert.equal(cacheProp in cache, true, '400 is converted to string'); }); @@ -741,13 +741,13 @@ }); QUnit.test('_measureChar handles 0 width chars', function(assert) { - fabric.charWidthsCache = {}; + fabric.cache.clearFontCache(); var zwc = '\u200b'; var text = new fabric.Text(''); var style = text.getCompleteStyleDeclaration(0, 0); var box = text._measureChar('a', style, zwc, style); var box2 = text._measureChar('a', style, zwc, style); - assert.equal(fabric.charWidthsCache[text.fontFamily.toLowerCase()].normal_normal[zwc], 0, 'zwc is a 0 width char'); + assert.equal(fabric.cache.charWidthsCache[text.fontFamily.toLowerCase()].normal_normal[zwc], 0, 'zwc is a 0 width char'); assert.equal(box.kernedWidth, box2.kernedWidth, '2 measurements of the same string return the same number'); }); diff --git a/test/unit/util.js b/test/unit/util.js index a54dcab10e0..f00ebb06884 100644 --- a/test/unit/util.js +++ b/test/unit/util.js @@ -711,21 +711,21 @@ assert.equal(fabric.util.getKlass('Sepia2', 'fabric.Image.filters'), fabric.Image.filters.Sepia2); }); - QUnit.test('clearFabricFontCache', function(assert) { - assert.ok(typeof fabric.util.clearFabricFontCache === 'function'); - fabric.charWidthsCache = { arial: { some: 'cache'}, helvetica: { some: 'cache'} }; - fabric.util.clearFabricFontCache('arial'); - assert.equal(fabric.charWidthsCache.arial, undefined, 'arial cache is deleted'); - assert.equal(fabric.charWidthsCache.helvetica.some, 'cache', 'helvetica cache is still available'); - fabric.util.clearFabricFontCache(); - assert.deepEqual(fabric.charWidthsCache, { }, 'all cache is deleted'); - }); - - QUnit.test('clearFabricFontCache wrong case', function(assert) { - fabric.charWidthsCache = { arial: { some: 'cache'}, helvetica: { some: 'cache'} }; - fabric.util.clearFabricFontCache('ARIAL'); - assert.equal(fabric.charWidthsCache.arial, undefined, 'arial cache is deleted'); - assert.equal(fabric.charWidthsCache.helvetica.some, 'cache', 'helvetica cache is still available'); + QUnit.test('clearFontCache', function(assert) { + assert.ok(typeof fabric.cache.clearFontCache === 'function'); + fabric.cache.charWidthsCache = { arial: { some: 'cache'}, helvetica: { some: 'cache'} }; + fabric.cache.clearFontCache('arial'); + assert.equal(fabric.cache.charWidthsCache.arial, undefined, 'arial cache is deleted'); + assert.equal(fabric.cache.charWidthsCache.helvetica.some, 'cache', 'helvetica cache is still available'); + fabric.cache.clearFontCache(); + assert.deepEqual(fabric.cache.charWidthsCache, { }, 'all cache is deleted'); + }); + + QUnit.test('clearFontCache wrong case', function(assert) { + fabric.cache.charWidthsCache = { arial: { some: 'cache'}, helvetica: { some: 'cache'} }; + fabric.cache.clearFontCache('ARIAL'); + assert.equal(fabric.cache.charWidthsCache.arial, undefined, 'arial cache is deleted'); + assert.equal(fabric.cache.charWidthsCache.helvetica.some, 'cache', 'helvetica cache is still available'); }); QUnit.test('parsePreserveAspectRatioAttribute', function(assert) { From 48d64c123a16b7428845cc992c740786a5598ed5 Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Sat, 27 Aug 2022 08:04:22 +0300 Subject: [PATCH 19/25] Update config.ts --- src/config.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/config.ts b/src/config.ts index a6510508d0c..4f8d74d579e 100644 --- a/src/config.ts +++ b/src/config.ts @@ -114,9 +114,10 @@ class BaseConfiguration { cachesBoundsOfCurve = true /** + * Map of font files * Map of font files */ - fontPaths: Record = {} + fontPaths: Record = {} /** * Defines the number of fraction digits to use when serializing object values. @@ -140,7 +141,7 @@ export class Configuration extends BaseConfiguration { /** * Map of font files */ - addFonts(paths: Record = {}) { + addFonts(paths: Record = {}) { this.fontPaths = { ...this.fontPaths, ...paths From 6c061009c81aba86cc0f6abf82ce3febef634465 Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Sat, 27 Aug 2022 08:05:03 +0300 Subject: [PATCH 20/25] move getFontCache --- src/cache.ts | 20 ++++++++++++++++++-- src/shapes/text.class.ts | 23 +---------------------- test/unit/text.js | 4 ++-- 3 files changed, 21 insertions(+), 26 deletions(-) diff --git a/src/cache.ts b/src/cache.ts index f0b89add76d..ff56089cbf8 100644 --- a/src/cache.ts +++ b/src/cache.ts @@ -1,9 +1,25 @@ export class Cache { /** - * Cache Object for widths of chars in text rendering. + * Cache of widths of chars in text rendering. */ - charWidthsCache = {} + charWidthsCache: Record>> = {} + + /** + * @return {Object} reference to cache + */ + getFontCache({ fontFamily, fontStyle, fontWeight }: { fontFamily: string, fontStyle: string, fontWeight: string | number }) { + fontFamily = fontFamily.toLowerCase(); + if (!this.charWidthsCache[fontFamily]) { + this.charWidthsCache[fontFamily] = {}; + } + const fontCache = this.charWidthsCache[fontFamily]; + const cacheKey = `${fontStyle.toLowerCase()}_${(fontWeight + '').toLowerCase()}`; + if (!fontCache[cacheKey]) { + fontCache[cacheKey] = {}; + } + return fontCache[cacheKey]; + } /** diff --git a/src/shapes/text.class.ts b/src/shapes/text.class.ts index 8e8ef340499..3ceb33bd871 100644 --- a/src/shapes/text.class.ts +++ b/src/shapes/text.class.ts @@ -696,27 +696,6 @@ import { DEFAULT_SVG_FONT_SIZE } from "../constants"; this._removeShadow(ctx); }, - /** - * @private - * @param {Object} decl style declaration for cache - * @param {String} decl.fontFamily fontFamily - * @param {String} decl.fontStyle fontStyle - * @param {String} decl.fontWeight fontWeight - * @return {Object} reference to cache - */ - getFontCache: function(decl) { - var fontFamily = decl.fontFamily.toLowerCase(); - if (!cache.charWidthsCache[fontFamily]) { - cache.charWidthsCache[fontFamily] = { }; - } - var cache = cache.charWidthsCache[fontFamily], - cacheProp = decl.fontStyle.toLowerCase() + '_' + (decl.fontWeight + '').toLowerCase(); - if (!cache[cacheProp]) { - cache[cacheProp] = { }; - } - return cache[cacheProp]; - }, - /** * measure and return the width of a single character. * possibly overridden to accommodate different measure logic or @@ -729,7 +708,7 @@ import { DEFAULT_SVG_FONT_SIZE } from "../constants"; */ _measureChar: function(_char, charStyle, previousChar, prevCharStyle) { // first i try to return from cache - var fontCache = this.getFontCache(charStyle), fontDeclaration = this._getFontDeclaration(charStyle), + var fontCache = cache.getFontCache(charStyle), fontDeclaration = this._getFontDeclaration(charStyle), previousFontDeclaration = this._getFontDeclaration(prevCharStyle), couple = previousChar + _char, stylesAreEqual = fontDeclaration === previousFontDeclaration, width, coupleWidth, previousWidth, fontMultiplier = charStyle.fontSize / this.CACHE_FONT_SIZE, kernedWidth; diff --git a/test/unit/text.js b/test/unit/text.js index db4dbcbc35b..779621fd248 100644 --- a/test/unit/text.js +++ b/test/unit/text.js @@ -400,8 +400,8 @@ text.initDimensions(); var text2 = new fabric.Text('xxx', { fontWeight: 'bOLd', fontStyle: 'nORMAl' }); text2.initDimensions(); - var cache = text.getFontCache(text); - var cache2 = text2.getFontCache(text2); + var cache = fabric.cache.getFontCache(text); + var cache2 = fabric.cache.getFontCache(text2); assert.equal(cache, cache2, 'you get the same cache'); }); // moved From 5611a87d426904833ebb7113b05cf408d79284b8 Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Sat, 27 Aug 2022 08:09:34 +0300 Subject: [PATCH 21/25] Revert "move getFontCache" This reverts commit 6c061009c81aba86cc0f6abf82ce3febef634465. --- src/cache.ts | 20 ++------------------ src/shapes/text.class.ts | 23 ++++++++++++++++++++++- test/unit/text.js | 4 ++-- 3 files changed, 26 insertions(+), 21 deletions(-) diff --git a/src/cache.ts b/src/cache.ts index ff56089cbf8..f0b89add76d 100644 --- a/src/cache.ts +++ b/src/cache.ts @@ -1,25 +1,9 @@ export class Cache { /** - * Cache of widths of chars in text rendering. + * Cache Object for widths of chars in text rendering. */ - charWidthsCache: Record>> = {} - - /** - * @return {Object} reference to cache - */ - getFontCache({ fontFamily, fontStyle, fontWeight }: { fontFamily: string, fontStyle: string, fontWeight: string | number }) { - fontFamily = fontFamily.toLowerCase(); - if (!this.charWidthsCache[fontFamily]) { - this.charWidthsCache[fontFamily] = {}; - } - const fontCache = this.charWidthsCache[fontFamily]; - const cacheKey = `${fontStyle.toLowerCase()}_${(fontWeight + '').toLowerCase()}`; - if (!fontCache[cacheKey]) { - fontCache[cacheKey] = {}; - } - return fontCache[cacheKey]; - } + charWidthsCache = {} /** diff --git a/src/shapes/text.class.ts b/src/shapes/text.class.ts index 3ceb33bd871..8e8ef340499 100644 --- a/src/shapes/text.class.ts +++ b/src/shapes/text.class.ts @@ -696,6 +696,27 @@ import { DEFAULT_SVG_FONT_SIZE } from "../constants"; this._removeShadow(ctx); }, + /** + * @private + * @param {Object} decl style declaration for cache + * @param {String} decl.fontFamily fontFamily + * @param {String} decl.fontStyle fontStyle + * @param {String} decl.fontWeight fontWeight + * @return {Object} reference to cache + */ + getFontCache: function(decl) { + var fontFamily = decl.fontFamily.toLowerCase(); + if (!cache.charWidthsCache[fontFamily]) { + cache.charWidthsCache[fontFamily] = { }; + } + var cache = cache.charWidthsCache[fontFamily], + cacheProp = decl.fontStyle.toLowerCase() + '_' + (decl.fontWeight + '').toLowerCase(); + if (!cache[cacheProp]) { + cache[cacheProp] = { }; + } + return cache[cacheProp]; + }, + /** * measure and return the width of a single character. * possibly overridden to accommodate different measure logic or @@ -708,7 +729,7 @@ import { DEFAULT_SVG_FONT_SIZE } from "../constants"; */ _measureChar: function(_char, charStyle, previousChar, prevCharStyle) { // first i try to return from cache - var fontCache = cache.getFontCache(charStyle), fontDeclaration = this._getFontDeclaration(charStyle), + var fontCache = this.getFontCache(charStyle), fontDeclaration = this._getFontDeclaration(charStyle), previousFontDeclaration = this._getFontDeclaration(prevCharStyle), couple = previousChar + _char, stylesAreEqual = fontDeclaration === previousFontDeclaration, width, coupleWidth, previousWidth, fontMultiplier = charStyle.fontSize / this.CACHE_FONT_SIZE, kernedWidth; diff --git a/test/unit/text.js b/test/unit/text.js index 779621fd248..db4dbcbc35b 100644 --- a/test/unit/text.js +++ b/test/unit/text.js @@ -400,8 +400,8 @@ text.initDimensions(); var text2 = new fabric.Text('xxx', { fontWeight: 'bOLd', fontStyle: 'nORMAl' }); text2.initDimensions(); - var cache = fabric.cache.getFontCache(text); - var cache2 = fabric.cache.getFontCache(text2); + var cache = text.getFontCache(text); + var cache2 = text2.getFontCache(text2); assert.equal(cache, cache2, 'you get the same cache'); }); // moved From 34728bdbe7ae64187319e6b6f1537e9499e2de81 Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Sat, 27 Aug 2022 08:09:38 +0300 Subject: [PATCH 22/25] Revert "extract cache" This reverts commit 146fa5ec121740c2e868de208dce4b272eff7ae0. --- HEADER.js | 22 ++++++++++++++++++ src/cache.ts | 48 ---------------------------------------- src/shapes/text.class.ts | 7 +++--- src/util/misc/misc.ts | 22 ++++++++++++++++++ src/util/path.ts | 7 +++--- test/unit/text.js | 6 ++--- test/unit/util.js | 30 ++++++++++++------------- 7 files changed, 68 insertions(+), 74 deletions(-) delete mode 100644 src/cache.ts diff --git a/HEADER.js b/HEADER.js index 4782b3eaddd..9e0496f3dec 100644 --- a/HEADER.js +++ b/HEADER.js @@ -92,6 +92,28 @@ config.configure({ 1 }); +/** + * Cache Object for widths of chars in text rendering. + */ +fabric.charWidthsCache = {}; + +/** + * This object contains the result of arc to bezier 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 = { }; + + fabric.initFilterBackend = function() { if (config.enableGLFiltering && fabric.isWebglSupported && fabric.isWebglSupported(config.textureSize)) { console.log(`fabric: max texture size: ${config.maxTextureSize}`); diff --git a/src/cache.ts b/src/cache.ts deleted file mode 100644 index f0b89add76d..00000000000 --- a/src/cache.ts +++ /dev/null @@ -1,48 +0,0 @@ - -export class Cache { - /** - * Cache Object for widths of chars in text rendering. - */ - charWidthsCache = {} - - - /** - * Clear char widths cache for the given font family or all the cache if no - * fontFamily is specified. - * Use it if you know you are loading fonts in a lazy way and you are not waiting - * for custom fonts to load properly when adding text objects to the canvas. - * If a text object is added when its own font is not loaded yet, you will get wrong - * measurement and so wrong bounding boxes. - * After the font cache is cleared, either change the textObject text content or call - * initDimensions() to trigger a recalculation - * @memberOf fabric.util - * @param {String} [fontFamily] font family to clear - */ - clearFontCache(fontFamily?: string) { - fontFamily = (fontFamily || '').toLowerCase(); - if (!fontFamily) { - this.charWidthsCache = {}; - } - else if (this.charWidthsCache[fontFamily]) { - delete this.charWidthsCache[fontFamily]; - } - } - - /** - * This object contains the result of arc to bezier 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 - */ - 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 - */ - boundsOfCurveCache = {} -} - -export const cache = new Cache(); diff --git a/src/shapes/text.class.ts b/src/shapes/text.class.ts index 8e8ef340499..c8a90f46486 100644 --- a/src/shapes/text.class.ts +++ b/src/shapes/text.class.ts @@ -1,6 +1,5 @@ //@ts-nocheck -import { cache } from "../cache"; import { DEFAULT_SVG_FONT_SIZE } from "../constants"; @@ -706,10 +705,10 @@ import { DEFAULT_SVG_FONT_SIZE } from "../constants"; */ getFontCache: function(decl) { var fontFamily = decl.fontFamily.toLowerCase(); - if (!cache.charWidthsCache[fontFamily]) { - cache.charWidthsCache[fontFamily] = { }; + if (!fabric.charWidthsCache[fontFamily]) { + fabric.charWidthsCache[fontFamily] = { }; } - var cache = cache.charWidthsCache[fontFamily], + var cache = fabric.charWidthsCache[fontFamily], cacheProp = decl.fontStyle.toLowerCase() + '_' + (decl.fontWeight + '').toLowerCase(); if (!cache[cacheProp]) { cache[cacheProp] = { }; diff --git a/src/util/misc/misc.ts b/src/util/misc/misc.ts index e2e2fec85f3..c101fe25482 100644 --- a/src/util/misc/misc.ts +++ b/src/util/misc/misc.ts @@ -367,6 +367,28 @@ import { makeBoundingBoxFromPoints } from './boundingBoxFromPoints'; return _isTransparent; }, + /** + * Clear char widths cache for the given font family or all the cache if no + * fontFamily is specified. + * Use it if you know you are loading fonts in a lazy way and you are not waiting + * for custom fonts to load properly when adding text objects to the canvas. + * If a text object is added when its own font is not loaded yet, you will get wrong + * measurement and so wrong bounding boxes. + * After the font cache is cleared, either change the textObject text content or call + * initDimensions() to trigger a recalculation + * @memberOf fabric.util + * @param {String} [fontFamily] font family to clear + */ + clearFabricFontCache: function(fontFamily) { + fontFamily = (fontFamily || '').toLowerCase(); + if (!fontFamily) { + fabric.charWidthsCache = { }; + } + else if (fabric.charWidthsCache[fontFamily]) { + delete fabric.charWidthsCache[fontFamily]; + } + }, + /** * Given current aspect ratio, determines the max width and height that can * respect the total allowed area for the cache. diff --git a/src/util/path.ts b/src/util/path.ts index 61c62fff1c6..62e4875288e 100644 --- a/src/util/path.ts +++ b/src/util/path.ts @@ -1,6 +1,5 @@ //@ts-nocheck -import { cache } from "../cache"; import { config } from "../config"; import { commaWsp, rePathCommand } from "../parser/constants"; import { Point } from '../point.class'; @@ -132,8 +131,8 @@ import { Point } from '../point.class'; var argsString; if (config.cachesBoundsOfCurve) { argsString = _join.call(arguments); - if (cache.boundsOfCurveCache[argsString]) { - return cache.boundsOfCurveCache[argsString]; + if (fabric.boundsOfCurveCache[argsString]) { + return fabric.boundsOfCurveCache[argsString]; } } @@ -205,7 +204,7 @@ import { Point } from '../point.class'; } ]; if (config.cachesBoundsOfCurve) { - cache.boundsOfCurveCache[argsString] = result; + fabric.boundsOfCurveCache[argsString] = result; } return result; } diff --git a/test/unit/text.js b/test/unit/text.js index db4dbcbc35b..03690ac24f3 100644 --- a/test/unit/text.js +++ b/test/unit/text.js @@ -390,7 +390,7 @@ QUnit.test('getFontCache works with fontWeight numbers', function(assert) { var text = new fabric.Text('xxx', { fontWeight: 400 }); text.initDimensions(); - var cache = fabric.cache.charWidthsCache[text.fontFamily.toLowerCase()]; + var cache = fabric.charWidthsCache[text.fontFamily.toLowerCase()]; var cacheProp = text.fontStyle + '_400'; assert.equal(cacheProp in cache, true, '400 is converted to string'); }); @@ -741,13 +741,13 @@ }); QUnit.test('_measureChar handles 0 width chars', function(assert) { - fabric.cache.clearFontCache(); + fabric.charWidthsCache = {}; var zwc = '\u200b'; var text = new fabric.Text(''); var style = text.getCompleteStyleDeclaration(0, 0); var box = text._measureChar('a', style, zwc, style); var box2 = text._measureChar('a', style, zwc, style); - assert.equal(fabric.cache.charWidthsCache[text.fontFamily.toLowerCase()].normal_normal[zwc], 0, 'zwc is a 0 width char'); + assert.equal(fabric.charWidthsCache[text.fontFamily.toLowerCase()].normal_normal[zwc], 0, 'zwc is a 0 width char'); assert.equal(box.kernedWidth, box2.kernedWidth, '2 measurements of the same string return the same number'); }); diff --git a/test/unit/util.js b/test/unit/util.js index f00ebb06884..a54dcab10e0 100644 --- a/test/unit/util.js +++ b/test/unit/util.js @@ -711,21 +711,21 @@ assert.equal(fabric.util.getKlass('Sepia2', 'fabric.Image.filters'), fabric.Image.filters.Sepia2); }); - QUnit.test('clearFontCache', function(assert) { - assert.ok(typeof fabric.cache.clearFontCache === 'function'); - fabric.cache.charWidthsCache = { arial: { some: 'cache'}, helvetica: { some: 'cache'} }; - fabric.cache.clearFontCache('arial'); - assert.equal(fabric.cache.charWidthsCache.arial, undefined, 'arial cache is deleted'); - assert.equal(fabric.cache.charWidthsCache.helvetica.some, 'cache', 'helvetica cache is still available'); - fabric.cache.clearFontCache(); - assert.deepEqual(fabric.cache.charWidthsCache, { }, 'all cache is deleted'); - }); - - QUnit.test('clearFontCache wrong case', function(assert) { - fabric.cache.charWidthsCache = { arial: { some: 'cache'}, helvetica: { some: 'cache'} }; - fabric.cache.clearFontCache('ARIAL'); - assert.equal(fabric.cache.charWidthsCache.arial, undefined, 'arial cache is deleted'); - assert.equal(fabric.cache.charWidthsCache.helvetica.some, 'cache', 'helvetica cache is still available'); + QUnit.test('clearFabricFontCache', function(assert) { + assert.ok(typeof fabric.util.clearFabricFontCache === 'function'); + fabric.charWidthsCache = { arial: { some: 'cache'}, helvetica: { some: 'cache'} }; + fabric.util.clearFabricFontCache('arial'); + assert.equal(fabric.charWidthsCache.arial, undefined, 'arial cache is deleted'); + assert.equal(fabric.charWidthsCache.helvetica.some, 'cache', 'helvetica cache is still available'); + fabric.util.clearFabricFontCache(); + assert.deepEqual(fabric.charWidthsCache, { }, 'all cache is deleted'); + }); + + QUnit.test('clearFabricFontCache wrong case', function(assert) { + fabric.charWidthsCache = { arial: { some: 'cache'}, helvetica: { some: 'cache'} }; + fabric.util.clearFabricFontCache('ARIAL'); + assert.equal(fabric.charWidthsCache.arial, undefined, 'arial cache is deleted'); + assert.equal(fabric.charWidthsCache.helvetica.some, 'cache', 'helvetica cache is still available'); }); QUnit.test('parsePreserveAspectRatioAttribute', function(assert) { From 47e0eb884ff37b75449170598e2549e93c938d11 Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Sat, 27 Aug 2022 08:10:31 +0300 Subject: [PATCH 23/25] Revert "Revert "extract cache"" This reverts commit 34728bdbe7ae64187319e6b6f1537e9499e2de81. --- HEADER.js | 22 ------------------ src/cache.ts | 48 ++++++++++++++++++++++++++++++++++++++++ src/shapes/text.class.ts | 7 +++--- src/util/misc/misc.ts | 22 ------------------ src/util/path.ts | 7 +++--- test/unit/text.js | 6 ++--- test/unit/util.js | 30 ++++++++++++------------- 7 files changed, 74 insertions(+), 68 deletions(-) create mode 100644 src/cache.ts diff --git a/HEADER.js b/HEADER.js index 9e0496f3dec..4782b3eaddd 100644 --- a/HEADER.js +++ b/HEADER.js @@ -92,28 +92,6 @@ config.configure({ 1 }); -/** - * Cache Object for widths of chars in text rendering. - */ -fabric.charWidthsCache = {}; - -/** - * This object contains the result of arc to bezier 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 = { }; - - fabric.initFilterBackend = function() { if (config.enableGLFiltering && fabric.isWebglSupported && fabric.isWebglSupported(config.textureSize)) { console.log(`fabric: max texture size: ${config.maxTextureSize}`); diff --git a/src/cache.ts b/src/cache.ts new file mode 100644 index 00000000000..f0b89add76d --- /dev/null +++ b/src/cache.ts @@ -0,0 +1,48 @@ + +export class Cache { + /** + * Cache Object for widths of chars in text rendering. + */ + charWidthsCache = {} + + + /** + * Clear char widths cache for the given font family or all the cache if no + * fontFamily is specified. + * Use it if you know you are loading fonts in a lazy way and you are not waiting + * for custom fonts to load properly when adding text objects to the canvas. + * If a text object is added when its own font is not loaded yet, you will get wrong + * measurement and so wrong bounding boxes. + * After the font cache is cleared, either change the textObject text content or call + * initDimensions() to trigger a recalculation + * @memberOf fabric.util + * @param {String} [fontFamily] font family to clear + */ + clearFontCache(fontFamily?: string) { + fontFamily = (fontFamily || '').toLowerCase(); + if (!fontFamily) { + this.charWidthsCache = {}; + } + else if (this.charWidthsCache[fontFamily]) { + delete this.charWidthsCache[fontFamily]; + } + } + + /** + * This object contains the result of arc to bezier 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 + */ + 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 + */ + boundsOfCurveCache = {} +} + +export const cache = new Cache(); diff --git a/src/shapes/text.class.ts b/src/shapes/text.class.ts index c8a90f46486..8e8ef340499 100644 --- a/src/shapes/text.class.ts +++ b/src/shapes/text.class.ts @@ -1,5 +1,6 @@ //@ts-nocheck +import { cache } from "../cache"; import { DEFAULT_SVG_FONT_SIZE } from "../constants"; @@ -705,10 +706,10 @@ import { DEFAULT_SVG_FONT_SIZE } from "../constants"; */ getFontCache: function(decl) { var fontFamily = decl.fontFamily.toLowerCase(); - if (!fabric.charWidthsCache[fontFamily]) { - fabric.charWidthsCache[fontFamily] = { }; + if (!cache.charWidthsCache[fontFamily]) { + cache.charWidthsCache[fontFamily] = { }; } - var cache = fabric.charWidthsCache[fontFamily], + var cache = cache.charWidthsCache[fontFamily], cacheProp = decl.fontStyle.toLowerCase() + '_' + (decl.fontWeight + '').toLowerCase(); if (!cache[cacheProp]) { cache[cacheProp] = { }; diff --git a/src/util/misc/misc.ts b/src/util/misc/misc.ts index c101fe25482..e2e2fec85f3 100644 --- a/src/util/misc/misc.ts +++ b/src/util/misc/misc.ts @@ -367,28 +367,6 @@ import { makeBoundingBoxFromPoints } from './boundingBoxFromPoints'; return _isTransparent; }, - /** - * Clear char widths cache for the given font family or all the cache if no - * fontFamily is specified. - * Use it if you know you are loading fonts in a lazy way and you are not waiting - * for custom fonts to load properly when adding text objects to the canvas. - * If a text object is added when its own font is not loaded yet, you will get wrong - * measurement and so wrong bounding boxes. - * After the font cache is cleared, either change the textObject text content or call - * initDimensions() to trigger a recalculation - * @memberOf fabric.util - * @param {String} [fontFamily] font family to clear - */ - clearFabricFontCache: function(fontFamily) { - fontFamily = (fontFamily || '').toLowerCase(); - if (!fontFamily) { - fabric.charWidthsCache = { }; - } - else if (fabric.charWidthsCache[fontFamily]) { - delete fabric.charWidthsCache[fontFamily]; - } - }, - /** * Given current aspect ratio, determines the max width and height that can * respect the total allowed area for the cache. diff --git a/src/util/path.ts b/src/util/path.ts index 62e4875288e..61c62fff1c6 100644 --- a/src/util/path.ts +++ b/src/util/path.ts @@ -1,5 +1,6 @@ //@ts-nocheck +import { cache } from "../cache"; import { config } from "../config"; import { commaWsp, rePathCommand } from "../parser/constants"; import { Point } from '../point.class'; @@ -131,8 +132,8 @@ import { Point } from '../point.class'; var argsString; if (config.cachesBoundsOfCurve) { argsString = _join.call(arguments); - if (fabric.boundsOfCurveCache[argsString]) { - return fabric.boundsOfCurveCache[argsString]; + if (cache.boundsOfCurveCache[argsString]) { + return cache.boundsOfCurveCache[argsString]; } } @@ -204,7 +205,7 @@ import { Point } from '../point.class'; } ]; if (config.cachesBoundsOfCurve) { - fabric.boundsOfCurveCache[argsString] = result; + cache.boundsOfCurveCache[argsString] = result; } return result; } diff --git a/test/unit/text.js b/test/unit/text.js index 03690ac24f3..db4dbcbc35b 100644 --- a/test/unit/text.js +++ b/test/unit/text.js @@ -390,7 +390,7 @@ QUnit.test('getFontCache works with fontWeight numbers', function(assert) { var text = new fabric.Text('xxx', { fontWeight: 400 }); text.initDimensions(); - var cache = fabric.charWidthsCache[text.fontFamily.toLowerCase()]; + var cache = fabric.cache.charWidthsCache[text.fontFamily.toLowerCase()]; var cacheProp = text.fontStyle + '_400'; assert.equal(cacheProp in cache, true, '400 is converted to string'); }); @@ -741,13 +741,13 @@ }); QUnit.test('_measureChar handles 0 width chars', function(assert) { - fabric.charWidthsCache = {}; + fabric.cache.clearFontCache(); var zwc = '\u200b'; var text = new fabric.Text(''); var style = text.getCompleteStyleDeclaration(0, 0); var box = text._measureChar('a', style, zwc, style); var box2 = text._measureChar('a', style, zwc, style); - assert.equal(fabric.charWidthsCache[text.fontFamily.toLowerCase()].normal_normal[zwc], 0, 'zwc is a 0 width char'); + assert.equal(fabric.cache.charWidthsCache[text.fontFamily.toLowerCase()].normal_normal[zwc], 0, 'zwc is a 0 width char'); assert.equal(box.kernedWidth, box2.kernedWidth, '2 measurements of the same string return the same number'); }); diff --git a/test/unit/util.js b/test/unit/util.js index a54dcab10e0..f00ebb06884 100644 --- a/test/unit/util.js +++ b/test/unit/util.js @@ -711,21 +711,21 @@ assert.equal(fabric.util.getKlass('Sepia2', 'fabric.Image.filters'), fabric.Image.filters.Sepia2); }); - QUnit.test('clearFabricFontCache', function(assert) { - assert.ok(typeof fabric.util.clearFabricFontCache === 'function'); - fabric.charWidthsCache = { arial: { some: 'cache'}, helvetica: { some: 'cache'} }; - fabric.util.clearFabricFontCache('arial'); - assert.equal(fabric.charWidthsCache.arial, undefined, 'arial cache is deleted'); - assert.equal(fabric.charWidthsCache.helvetica.some, 'cache', 'helvetica cache is still available'); - fabric.util.clearFabricFontCache(); - assert.deepEqual(fabric.charWidthsCache, { }, 'all cache is deleted'); - }); - - QUnit.test('clearFabricFontCache wrong case', function(assert) { - fabric.charWidthsCache = { arial: { some: 'cache'}, helvetica: { some: 'cache'} }; - fabric.util.clearFabricFontCache('ARIAL'); - assert.equal(fabric.charWidthsCache.arial, undefined, 'arial cache is deleted'); - assert.equal(fabric.charWidthsCache.helvetica.some, 'cache', 'helvetica cache is still available'); + QUnit.test('clearFontCache', function(assert) { + assert.ok(typeof fabric.cache.clearFontCache === 'function'); + fabric.cache.charWidthsCache = { arial: { some: 'cache'}, helvetica: { some: 'cache'} }; + fabric.cache.clearFontCache('arial'); + assert.equal(fabric.cache.charWidthsCache.arial, undefined, 'arial cache is deleted'); + assert.equal(fabric.cache.charWidthsCache.helvetica.some, 'cache', 'helvetica cache is still available'); + fabric.cache.clearFontCache(); + assert.deepEqual(fabric.cache.charWidthsCache, { }, 'all cache is deleted'); + }); + + QUnit.test('clearFontCache wrong case', function(assert) { + fabric.cache.charWidthsCache = { arial: { some: 'cache'}, helvetica: { some: 'cache'} }; + fabric.cache.clearFontCache('ARIAL'); + assert.equal(fabric.cache.charWidthsCache.arial, undefined, 'arial cache is deleted'); + assert.equal(fabric.cache.charWidthsCache.helvetica.some, 'cache', 'helvetica cache is still available'); }); QUnit.test('parsePreserveAspectRatioAttribute', function(assert) { From c8cafa6b138befb0d225c1096e6088eda66b9a61 Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Sat, 27 Aug 2022 08:10:37 +0300 Subject: [PATCH 24/25] Revert "Revert "move getFontCache"" This reverts commit 5611a87d426904833ebb7113b05cf408d79284b8. --- src/cache.ts | 20 ++++++++++++++++++-- src/shapes/text.class.ts | 23 +---------------------- test/unit/text.js | 4 ++-- 3 files changed, 21 insertions(+), 26 deletions(-) diff --git a/src/cache.ts b/src/cache.ts index f0b89add76d..ff56089cbf8 100644 --- a/src/cache.ts +++ b/src/cache.ts @@ -1,9 +1,25 @@ export class Cache { /** - * Cache Object for widths of chars in text rendering. + * Cache of widths of chars in text rendering. */ - charWidthsCache = {} + charWidthsCache: Record>> = {} + + /** + * @return {Object} reference to cache + */ + getFontCache({ fontFamily, fontStyle, fontWeight }: { fontFamily: string, fontStyle: string, fontWeight: string | number }) { + fontFamily = fontFamily.toLowerCase(); + if (!this.charWidthsCache[fontFamily]) { + this.charWidthsCache[fontFamily] = {}; + } + const fontCache = this.charWidthsCache[fontFamily]; + const cacheKey = `${fontStyle.toLowerCase()}_${(fontWeight + '').toLowerCase()}`; + if (!fontCache[cacheKey]) { + fontCache[cacheKey] = {}; + } + return fontCache[cacheKey]; + } /** diff --git a/src/shapes/text.class.ts b/src/shapes/text.class.ts index 8e8ef340499..3ceb33bd871 100644 --- a/src/shapes/text.class.ts +++ b/src/shapes/text.class.ts @@ -696,27 +696,6 @@ import { DEFAULT_SVG_FONT_SIZE } from "../constants"; this._removeShadow(ctx); }, - /** - * @private - * @param {Object} decl style declaration for cache - * @param {String} decl.fontFamily fontFamily - * @param {String} decl.fontStyle fontStyle - * @param {String} decl.fontWeight fontWeight - * @return {Object} reference to cache - */ - getFontCache: function(decl) { - var fontFamily = decl.fontFamily.toLowerCase(); - if (!cache.charWidthsCache[fontFamily]) { - cache.charWidthsCache[fontFamily] = { }; - } - var cache = cache.charWidthsCache[fontFamily], - cacheProp = decl.fontStyle.toLowerCase() + '_' + (decl.fontWeight + '').toLowerCase(); - if (!cache[cacheProp]) { - cache[cacheProp] = { }; - } - return cache[cacheProp]; - }, - /** * measure and return the width of a single character. * possibly overridden to accommodate different measure logic or @@ -729,7 +708,7 @@ import { DEFAULT_SVG_FONT_SIZE } from "../constants"; */ _measureChar: function(_char, charStyle, previousChar, prevCharStyle) { // first i try to return from cache - var fontCache = this.getFontCache(charStyle), fontDeclaration = this._getFontDeclaration(charStyle), + var fontCache = cache.getFontCache(charStyle), fontDeclaration = this._getFontDeclaration(charStyle), previousFontDeclaration = this._getFontDeclaration(prevCharStyle), couple = previousChar + _char, stylesAreEqual = fontDeclaration === previousFontDeclaration, width, coupleWidth, previousWidth, fontMultiplier = charStyle.fontSize / this.CACHE_FONT_SIZE, kernedWidth; diff --git a/test/unit/text.js b/test/unit/text.js index db4dbcbc35b..779621fd248 100644 --- a/test/unit/text.js +++ b/test/unit/text.js @@ -400,8 +400,8 @@ text.initDimensions(); var text2 = new fabric.Text('xxx', { fontWeight: 'bOLd', fontStyle: 'nORMAl' }); text2.initDimensions(); - var cache = text.getFontCache(text); - var cache2 = text2.getFontCache(text2); + var cache = fabric.cache.getFontCache(text); + var cache2 = fabric.cache.getFontCache(text2); assert.equal(cache, cache2, 'you get the same cache'); }); // moved From 7dc9e5ddb34760145b0cb99dc17387e8a65975db Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Sat, 27 Aug 2022 08:20:37 +0300 Subject: [PATCH 25/25] Update HEADER.js --- HEADER.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/HEADER.js b/HEADER.js index 4782b3eaddd..ae0eb562cdf 100644 --- a/HEADER.js +++ b/HEADER.js @@ -1,11 +1,13 @@ /*! Fabric.js Copyright 2008-2015, Printio (Juriy Zaytsev, Maxim Chernyak) */ +import { cache } from './src/cache'; import { config } from './src/config'; import { iMatrix, VERSION } from './src/constants'; var fabric = fabric || { version: VERSION, config, + cache, iMatrix };