From 49c7682bde2e643d20524c66b0988efcb3a140b0 Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Wed, 20 Sep 2023 06:43:21 +0530 Subject: [PATCH 01/14] use exisiting DOM API + math --- src/Pattern/Pattern.ts | 5 ++++- src/canvas/StaticCanvas.ts | 5 ++--- src/gradient/Gradient.ts | 13 ++++++++++--- src/shapes/Line.ts | 2 +- src/shapes/Object/Object.ts | 22 ++++++---------------- src/shapes/Text/Text.ts | 11 +++-------- 6 files changed, 26 insertions(+), 32 deletions(-) diff --git a/src/Pattern/Pattern.ts b/src/Pattern/Pattern.ts index bbfeefb2e00..26e02b57698 100644 --- a/src/Pattern/Pattern.ts +++ b/src/Pattern/Pattern.ts @@ -138,7 +138,10 @@ export class Pattern { return null; } - return ctx.createPattern(this.source, this.repeat)!; + const pattern = ctx.createPattern(this.source, this.repeat)!; + this.patternTransform && + pattern.setTransform(new DOMMatrix(this.patternTransform)); + return pattern; } /** diff --git a/src/canvas/StaticCanvas.ts b/src/canvas/StaticCanvas.ts index a8c399e70a9..180dcbfcfb1 100644 --- a/src/canvas/StaticCanvas.ts +++ b/src/canvas/StaticCanvas.ts @@ -650,14 +650,13 @@ export class StaticCanvas< ctx.lineTo(this.width, this.height); ctx.lineTo(0, this.height); ctx.closePath(); - ctx.fillStyle = isAFiller ? fill.toLive(ctx /* this */)! : fill; + ctx.fillStyle = isAFiller ? fill.toLive(ctx, this)! : fill; if (needsVpt) { ctx.transform(...v); } if (isAFiller) { ctx.transform(1, 0, 0, 1, fill.offsetX || 0, fill.offsetY || 0); - const m = ((fill as Gradient<'linear'>).gradientTransform || - (fill as Pattern).patternTransform) as TMat2D; + const m = ((fill as Gradient<'linear'>).gradientTransform m && ctx.transform(...m); } ctx.fill(); diff --git a/src/gradient/Gradient.ts b/src/gradient/Gradient.ts index 37d7349507f..d162a16e6b1 100644 --- a/src/gradient/Gradient.ts +++ b/src/gradient/Gradient.ts @@ -2,7 +2,7 @@ import { Color } from '../color/Color'; import { iMatrix } from '../constants'; import { parseTransformAttribute } from '../parser/parseTransformAttribute'; import type { FabricObject } from '../shapes/Object/FabricObject'; -import type { TMat2D } from '../typedefs'; +import type { TMat2D, TSize } from '../typedefs'; import { uid } from '../util/internals/uid'; import { pick } from '../util/misc/pick'; import { matrixToSVG } from '../util/misc/svgParsing'; @@ -295,10 +295,17 @@ export class Gradient< * @param {CanvasRenderingContext2D} ctx Context to render on * @return {CanvasGradient} */ - toLive(ctx: CanvasRenderingContext2D): CanvasGradient { + toLive(ctx: CanvasRenderingContext2D, size: TSize): CanvasGradient { const coords = this.coords as GradientCoords<'radial'>; const gradient = - this.type === 'linear' + this.type === 'linear' && this.gradientUnits === 'percentage' + ? ctx.createLinearGradient( + coords.x1 * size.width, + coords.y1 * size.height, + coords.x2 * size.width, + coords.y2 * size.height + ) + : this.type === 'linear' ? ctx.createLinearGradient(coords.x1, coords.y1, coords.x2, coords.y2) : ctx.createRadialGradient( coords.x1, diff --git a/src/shapes/Line.ts b/src/shapes/Line.ts index 953a3af8340..0ac2ec73a0c 100644 --- a/src/shapes/Line.ts +++ b/src/shapes/Line.ts @@ -132,7 +132,7 @@ export class Line< // (by copying fillStyle to strokeStyle, since line is stroked, not filled) const origStrokeStyle = ctx.strokeStyle; if (isFiller(this.stroke)) { - ctx.strokeStyle = this.stroke.toLive(ctx)!; + ctx.strokeStyle = this.stroke.toLive(ctx, this)!; } else { ctx.strokeStyle = this.stroke ?? ctx.fillStyle; } diff --git a/src/shapes/Object/Object.ts b/src/shapes/Object/Object.ts index 8571463e00e..5df03047ed1 100644 --- a/src/shapes/Object/Object.ts +++ b/src/shapes/Object/Object.ts @@ -1047,11 +1047,7 @@ export class FabricObject< ctx.lineJoin = decl.strokeLineJoin; ctx.miterLimit = decl.strokeMiterLimit; if (isFiller(stroke)) { - if ( - (stroke as Gradient<'linear'>).gradientUnits === 'percentage' || - (stroke as Gradient<'linear'>).gradientTransform || - (stroke as Pattern).patternTransform - ) { + if ((stroke as Gradient<'linear'>).gradientTransform) { // need to transform gradient in a pattern. // this is a slow process. If you are hitting this codepath, and the object // is not using caching, you should consider switching it on. @@ -1059,7 +1055,7 @@ export class FabricObject< this._applyPatternForTransformedGradient(ctx, stroke); } else { // is a simple gradient or pattern - ctx.strokeStyle = stroke.toLive(ctx)!; + ctx.strokeStyle = stroke.toLive(ctx, this)!; this._applyPatternGradientTransform(ctx, stroke); } } else { @@ -1072,7 +1068,7 @@ export class FabricObject< _setFillStyles(ctx: CanvasRenderingContext2D, { fill }: Pick) { if (fill) { if (isFiller(fill)) { - ctx.fillStyle = fill.toLive(ctx)!; + ctx.fillStyle = fill.toLive(ctx, this)!; this._applyPatternGradientTransform(ctx, fill); } else { ctx.fillStyle = fill; @@ -1155,17 +1151,11 @@ export class FabricObject< if (!isFiller(filler)) { return { offsetX: 0, offsetY: 0 }; } - const t = - (filler as Gradient<'linear'>).gradientTransform || - (filler as Pattern).patternTransform; + const t = (filler as Gradient<'linear'>).gradientTransform; const offsetX = -this.width / 2 + filler.offsetX || 0, offsetY = -this.height / 2 + filler.offsetY || 0; - if ((filler as Gradient<'linear'>).gradientUnits === 'percentage') { - ctx.transform(this.width, 0, 0, this.height, offsetX, offsetY); - } else { - ctx.transform(1, 0, 0, 1, offsetX, offsetY); - } + ctx.transform(1, 0, 0, 1, offsetX, offsetY); if (t) { ctx.transform(t[0], t[1], t[2], t[3], t[4], t[5]); } @@ -1280,7 +1270,7 @@ export class FabricObject< dims.zoomY / this.scaleY / retinaScaling ); this._applyPatternGradientTransform(pCtx, filler); - pCtx.fillStyle = filler.toLive(ctx)!; + pCtx.fillStyle = filler.toLive(ctx, this)!; pCtx.fill(); ctx.translate( -this.width / 2 - this.strokeWidth / 2, diff --git a/src/shapes/Text/Text.ts b/src/shapes/Text/Text.ts index 0bd28cbefb7..bda2f690a3f 100644 --- a/src/shapes/Text/Text.ts +++ b/src/shapes/Text/Text.ts @@ -44,7 +44,6 @@ import { import { CENTER, LEFT, RIGHT, TOP, BOTTOM } from '../../constants'; import { isFiller } from '../../util/typeAssertions'; import type { Gradient } from '../../gradient/Gradient'; -import type { Pattern } from '../../Pattern'; import type { CSSRules } from '../../parser/typedefs'; let measuringContext: CanvasRenderingContext2D | null; @@ -1257,7 +1256,7 @@ export class Text< pCtx.lineTo(0, height); pCtx.closePath(); pCtx.translate(width / 2, height / 2); - pCtx.fillStyle = filler.toLive(pCtx)!; + pCtx.fillStyle = filler.toLive(pCtx, this)!; this._applyPatternGradientTransform(pCtx, filler); pCtx.fill(); return pCtx.createPattern(pCanvas, 'no-repeat')!; @@ -1270,11 +1269,7 @@ export class Text< ): { offsetX: number; offsetY: number } { let offsetX: number, offsetY: number; if (isFiller(filler)) { - if ( - (filler as Gradient<'linear'>).gradientUnits === 'percentage' || - (filler as Gradient<'linear'>).gradientTransform || - (filler as Pattern).patternTransform - ) { + if ((filler as Gradient<'linear'>).gradientTransform) { // need to transform gradient in a pattern. // this is a slow process. If you are hitting this codepath, and the object // is not using caching, you should consider switching it on. @@ -1286,7 +1281,7 @@ export class Text< return { offsetX, offsetY }; } else { // is a simple gradient or pattern - ctx[property] = filler.toLive(ctx)!; + ctx[property] = filler.toLive(ctx, this)!; return this._applyPatternGradientTransform(ctx, filler); } } else { From f2df473b1d839935370cede83e803816c3db2e3c Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Wed, 20 Sep 2023 06:56:05 +0530 Subject: [PATCH 02/14] offset --- src/Pattern/Pattern.ts | 14 ++++++++++++-- src/canvas/StaticCanvas.ts | 1 - src/gradient/Gradient.ts | 17 +++++++++-------- src/shapes/Object/Object.ts | 4 ++-- 4 files changed, 23 insertions(+), 13 deletions(-) diff --git a/src/Pattern/Pattern.ts b/src/Pattern/Pattern.ts index 26e02b57698..c83a79ebf2b 100644 --- a/src/Pattern/Pattern.ts +++ b/src/Pattern/Pattern.ts @@ -11,6 +11,7 @@ import type { PatternOptions, SerializedPatternOptions, } from './types'; +import { createTranslateMatrix, multiplyTransformMatrixArray } from '../util'; /** * @see {@link http://fabricjs.com/patterns demo} @@ -139,8 +140,17 @@ export class Pattern { } const pattern = ctx.createPattern(this.source, this.repeat)!; - this.patternTransform && - pattern.setTransform(new DOMMatrix(this.patternTransform)); + const { patternTransform, offsetX = 0, offsetY = 0 } = this; + if (patternTransform || offsetX || offsetY) { + pattern.setTransform( + new DOMMatrix( + multiplyTransformMatrixArray([ + createTranslateMatrix(offsetX, offsetY), + patternTransform, + ]) + ) + ); + } return pattern; } diff --git a/src/canvas/StaticCanvas.ts b/src/canvas/StaticCanvas.ts index 180dcbfcfb1..a10c7ddb522 100644 --- a/src/canvas/StaticCanvas.ts +++ b/src/canvas/StaticCanvas.ts @@ -655,7 +655,6 @@ export class StaticCanvas< ctx.transform(...v); } if (isAFiller) { - ctx.transform(1, 0, 0, 1, fill.offsetX || 0, fill.offsetY || 0); const m = ((fill as Gradient<'linear'>).gradientTransform m && ctx.transform(...m); } diff --git a/src/gradient/Gradient.ts b/src/gradient/Gradient.ts index d162a16e6b1..eb52a61fbaf 100644 --- a/src/gradient/Gradient.ts +++ b/src/gradient/Gradient.ts @@ -297,22 +297,23 @@ export class Gradient< */ toLive(ctx: CanvasRenderingContext2D, size: TSize): CanvasGradient { const coords = this.coords as GradientCoords<'radial'>; + const { offsetX = 0, offsetY = 0 } = this; const gradient = this.type === 'linear' && this.gradientUnits === 'percentage' ? ctx.createLinearGradient( - coords.x1 * size.width, - coords.y1 * size.height, - coords.x2 * size.width, - coords.y2 * size.height + coords.x1 * size.width + offsetX, + coords.y1 * size.height + offsetY, + coords.x2 * size.width + offsetX, + coords.y2 * size.height + offsetY ) : this.type === 'linear' ? ctx.createLinearGradient(coords.x1, coords.y1, coords.x2, coords.y2) : ctx.createRadialGradient( - coords.x1, - coords.y1, + coords.x1 + offsetX, + coords.y1 + offsetY, coords.r1, - coords.x2, - coords.y2, + coords.x2 + offsetX, + coords.y2 + offsetY, coords.r2 ); diff --git a/src/shapes/Object/Object.ts b/src/shapes/Object/Object.ts index 5df03047ed1..ab844fd4cb7 100644 --- a/src/shapes/Object/Object.ts +++ b/src/shapes/Object/Object.ts @@ -1152,8 +1152,8 @@ export class FabricObject< return { offsetX: 0, offsetY: 0 }; } const t = (filler as Gradient<'linear'>).gradientTransform; - const offsetX = -this.width / 2 + filler.offsetX || 0, - offsetY = -this.height / 2 + filler.offsetY || 0; + const offsetX = -this.width / 2, + offsetY = -this.height / 2; ctx.transform(1, 0, 0, 1, offsetX, offsetY); if (t) { From 0574c3dafb475d6bb3955a8753fc3b7308e271cc Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Wed, 20 Sep 2023 07:10:11 +0530 Subject: [PATCH 03/14] correction --- src/Pattern/Pattern.ts | 16 +++++++++++++--- src/canvas/StaticCanvas.ts | 2 +- src/gradient/Gradient.ts | 35 ++++++++++++++++++++++++----------- src/shapes/Object/Object.ts | 6 +----- 4 files changed, 39 insertions(+), 20 deletions(-) diff --git a/src/Pattern/Pattern.ts b/src/Pattern/Pattern.ts index c83a79ebf2b..5f8d835dc89 100644 --- a/src/Pattern/Pattern.ts +++ b/src/Pattern/Pattern.ts @@ -12,6 +12,8 @@ import type { SerializedPatternOptions, } from './types'; import { createTranslateMatrix, multiplyTransformMatrixArray } from '../util'; +import type { StaticCanvas } from '../canvas/StaticCanvas'; +import { FabricObject } from '../shapes/Object/Object'; /** * @see {@link http://fabricjs.com/patterns demo} @@ -126,7 +128,10 @@ export class Pattern { * @param {CanvasRenderingContext2D} ctx Context to create pattern * @return {CanvasPattern} */ - toLive(ctx: CanvasRenderingContext2D): CanvasPattern | null { + toLive( + ctx: CanvasRenderingContext2D, + target: StaticCanvas | FabricObject + ): CanvasPattern | null { if ( // if the image failed to load, return, and allow rest to continue loading !this.source || @@ -141,11 +146,16 @@ export class Pattern { const pattern = ctx.createPattern(this.source, this.repeat)!; const { patternTransform, offsetX = 0, offsetY = 0 } = this; - if (patternTransform || offsetX || offsetY) { + const { x, y } = + // correct rendering position from object rendering origin (center) to tl + target instanceof FabricObject + ? { x: -target.width / 2, y: -target.height / 2 } + : { x: 0, y: 0 }; + if (patternTransform || offsetX || offsetY || x || y) { pattern.setTransform( new DOMMatrix( multiplyTransformMatrixArray([ - createTranslateMatrix(offsetX, offsetY), + createTranslateMatrix(offsetX + x, offsetY + y), patternTransform, ]) ) diff --git a/src/canvas/StaticCanvas.ts b/src/canvas/StaticCanvas.ts index a10c7ddb522..1f250a778db 100644 --- a/src/canvas/StaticCanvas.ts +++ b/src/canvas/StaticCanvas.ts @@ -655,7 +655,7 @@ export class StaticCanvas< ctx.transform(...v); } if (isAFiller) { - const m = ((fill as Gradient<'linear'>).gradientTransform + const m = (fill as Gradient<'linear'>).gradientTransform; m && ctx.transform(...m); } ctx.fill(); diff --git a/src/gradient/Gradient.ts b/src/gradient/Gradient.ts index eb52a61fbaf..3fe8e335953 100644 --- a/src/gradient/Gradient.ts +++ b/src/gradient/Gradient.ts @@ -1,8 +1,8 @@ import { Color } from '../color/Color'; import { iMatrix } from '../constants'; import { parseTransformAttribute } from '../parser/parseTransformAttribute'; -import type { FabricObject } from '../shapes/Object/FabricObject'; -import type { TMat2D, TSize } from '../typedefs'; +import { FabricObject } from '../shapes/Object/Object'; +import type { TMat2D } from '../typedefs'; import { uid } from '../util/internals/uid'; import { pick } from '../util/misc/pick'; import { matrixToSVG } from '../util/misc/svgParsing'; @@ -20,6 +20,7 @@ import type { } from './typedefs'; import { classRegistry } from '../ClassRegistry'; import { isPath } from '../util/typeAssertions'; +import type { StaticCanvas } from '../canvas/StaticCanvas'; /** * Gradient class @@ -295,25 +296,37 @@ export class Gradient< * @param {CanvasRenderingContext2D} ctx Context to render on * @return {CanvasGradient} */ - toLive(ctx: CanvasRenderingContext2D, size: TSize): CanvasGradient { + toLive( + ctx: CanvasRenderingContext2D, + target: StaticCanvas | FabricObject + ): CanvasGradient { const coords = this.coords as GradientCoords<'radial'>; const { offsetX = 0, offsetY = 0 } = this; + const { x, y } = + // correct rendering position from object rendering origin (center) to tl + target instanceof FabricObject + ? { x: -target.width / 2, y: -target.height / 2 } + : { x: 0, y: 0 }; + const { x: sx, y: sy } = + target instanceof FabricObject + ? { x: target.width, y: target.height } + : { x: 0, y: 0 }; const gradient = this.type === 'linear' && this.gradientUnits === 'percentage' ? ctx.createLinearGradient( - coords.x1 * size.width + offsetX, - coords.y1 * size.height + offsetY, - coords.x2 * size.width + offsetX, - coords.y2 * size.height + offsetY + coords.x1 * sx + offsetX + x, + coords.y1 * sy + offsetY + y, + coords.x2 * sx + offsetX + x, + coords.y2 * sy + offsetY + y ) : this.type === 'linear' ? ctx.createLinearGradient(coords.x1, coords.y1, coords.x2, coords.y2) : ctx.createRadialGradient( - coords.x1 + offsetX, - coords.y1 + offsetY, + coords.x1 + offsetX + x, + coords.y1 + offsetY + y, coords.r1, - coords.x2 + offsetX, - coords.y2 + offsetY, + coords.x2 + offsetX + x, + coords.y2 + offsetY + y, coords.r2 ); diff --git a/src/shapes/Object/Object.ts b/src/shapes/Object/Object.ts index ab844fd4cb7..f3d53a22f3c 100644 --- a/src/shapes/Object/Object.ts +++ b/src/shapes/Object/Object.ts @@ -1152,14 +1152,10 @@ export class FabricObject< return { offsetX: 0, offsetY: 0 }; } const t = (filler as Gradient<'linear'>).gradientTransform; - const offsetX = -this.width / 2, - offsetY = -this.height / 2; - - ctx.transform(1, 0, 0, 1, offsetX, offsetY); if (t) { ctx.transform(t[0], t[1], t[2], t[3], t[4], t[5]); } - return { offsetX: offsetX, offsetY: offsetY }; + return { offsetX: 0, offsetY: 0 }; } /** From 30e259040d56b48e4e6f04c0ef5a8a57986b1426 Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Wed, 20 Sep 2023 07:49:03 +0530 Subject: [PATCH 04/14] gradientTransform --- src/Pattern/Pattern.ts | 2 +- src/canvas/StaticCanvas.ts | 5 ---- src/gradient/Gradient.ts | 51 +++++++++++++++++++++---------------- src/shapes/Object/Object.ts | 34 +------------------------ src/shapes/Text/Text.ts | 50 +----------------------------------- 5 files changed, 32 insertions(+), 110 deletions(-) diff --git a/src/Pattern/Pattern.ts b/src/Pattern/Pattern.ts index 5f8d835dc89..d23984154c1 100644 --- a/src/Pattern/Pattern.ts +++ b/src/Pattern/Pattern.ts @@ -155,8 +155,8 @@ export class Pattern { pattern.setTransform( new DOMMatrix( multiplyTransformMatrixArray([ - createTranslateMatrix(offsetX + x, offsetY + y), patternTransform, + createTranslateMatrix(offsetX + x, offsetY + y), ]) ) ); diff --git a/src/canvas/StaticCanvas.ts b/src/canvas/StaticCanvas.ts index 1f250a778db..c5b827e8ca1 100644 --- a/src/canvas/StaticCanvas.ts +++ b/src/canvas/StaticCanvas.ts @@ -1,7 +1,6 @@ import { config } from '../config'; import { CENTER, VERSION } from '../constants'; import type { CanvasEvents, StaticCanvasEvents } from '../EventTypeDefs'; -import type { Gradient } from '../gradient/Gradient'; import { createCollectionMixin } from '../Collection'; import { CommonMethods } from '../CommonMethods'; import type { Pattern } from '../Pattern'; @@ -654,10 +653,6 @@ export class StaticCanvas< if (needsVpt) { ctx.transform(...v); } - if (isAFiller) { - const m = (fill as Gradient<'linear'>).gradientTransform; - m && ctx.transform(...m); - } ctx.fill(); ctx.restore(); } diff --git a/src/gradient/Gradient.ts b/src/gradient/Gradient.ts index 3fe8e335953..4c51b709ac4 100644 --- a/src/gradient/Gradient.ts +++ b/src/gradient/Gradient.ts @@ -21,6 +21,12 @@ import type { import { classRegistry } from '../ClassRegistry'; import { isPath } from '../util/typeAssertions'; import type { StaticCanvas } from '../canvas/StaticCanvas'; +import { + createScaleMatrix, + createTranslateMatrix, + multiplyTransformMatrixArray, +} from '../util'; +import { Point } from '../Point'; /** * Gradient class @@ -307,28 +313,29 @@ export class Gradient< target instanceof FabricObject ? { x: -target.width / 2, y: -target.height / 2 } : { x: 0, y: 0 }; - const { x: sx, y: sy } = - target instanceof FabricObject - ? { x: target.width, y: target.height } - : { x: 0, y: 0 }; - const gradient = - this.type === 'linear' && this.gradientUnits === 'percentage' - ? ctx.createLinearGradient( - coords.x1 * sx + offsetX + x, - coords.y1 * sy + offsetY + y, - coords.x2 * sx + offsetX + x, - coords.y2 * sy + offsetY + y - ) - : this.type === 'linear' - ? ctx.createLinearGradient(coords.x1, coords.y1, coords.x2, coords.y2) - : ctx.createRadialGradient( - coords.x1 + offsetX + x, - coords.y1 + offsetY + y, - coords.r1, - coords.x2 + offsetX + x, - coords.y2 + offsetY + y, - coords.r2 - ); + + let gradient: CanvasGradient; + if (this.type === 'linear') { + const transform = multiplyTransformMatrixArray([ + this.gradientTransform, + createTranslateMatrix(offsetX + x, offsetY + y), + this.gradientUnits === 'percentage' && + createScaleMatrix(target.width, target.height), + ]); + const { x1, y1, x2, y2 } = this.coords; + const p1 = new Point(x1, y1).transform(transform); + const p2 = new Point(x2, y2).transform(transform); + gradient = ctx.createLinearGradient(p1.x, p1.y, p2.x, p2.y); + } else { + gradient = ctx.createRadialGradient( + coords.x1 + offsetX + x, + coords.y1 + offsetY + y, + coords.r1, + coords.x2 + offsetX + x, + coords.y2 + offsetY + y, + coords.r2 + ); + } this.colorStops.forEach(({ color, opacity, offset }) => { gradient.addColorStop( diff --git a/src/shapes/Object/Object.ts b/src/shapes/Object/Object.ts index f3d53a22f3c..a5dd77cc5ee 100644 --- a/src/shapes/Object/Object.ts +++ b/src/shapes/Object/Object.ts @@ -48,7 +48,6 @@ import { stateProperties, } from './defaultValues'; import type { Gradient } from '../../gradient/Gradient'; -import type { Pattern } from '../../Pattern'; import type { Canvas } from '../../canvas/Canvas'; import type { SerializedObjectProps } from './types/SerializedObjectProps'; import type { ObjectProps } from './types/ObjectProps'; @@ -1047,17 +1046,7 @@ export class FabricObject< ctx.lineJoin = decl.strokeLineJoin; ctx.miterLimit = decl.strokeMiterLimit; if (isFiller(stroke)) { - if ((stroke as Gradient<'linear'>).gradientTransform) { - // need to transform gradient in a pattern. - // this is a slow process. If you are hitting this codepath, and the object - // is not using caching, you should consider switching it on. - // we need a canvas as big as the current object caching canvas. - this._applyPatternForTransformedGradient(ctx, stroke); - } else { - // is a simple gradient or pattern - ctx.strokeStyle = stroke.toLive(ctx, this)!; - this._applyPatternGradientTransform(ctx, stroke); - } + ctx.strokeStyle = stroke.toLive(ctx, this)!; } else { // is a color ctx.strokeStyle = decl.stroke as string; @@ -1069,7 +1058,6 @@ export class FabricObject< if (fill) { if (isFiller(fill)) { ctx.fillStyle = fill.toLive(ctx, this)!; - this._applyPatternGradientTransform(ctx, fill); } else { ctx.fillStyle = fill; } @@ -1139,25 +1127,6 @@ export class FabricObject< ctx.shadowBlur = ctx.shadowOffsetX = ctx.shadowOffsetY = 0; } - /** - * @private - * @param {CanvasRenderingContext2D} ctx Context to render on - * @param {TFiller} filler {@link Pattern} or {@link Gradient} - */ - _applyPatternGradientTransform( - ctx: CanvasRenderingContext2D, - filler: TFiller - ) { - if (!isFiller(filler)) { - return { offsetX: 0, offsetY: 0 }; - } - const t = (filler as Gradient<'linear'>).gradientTransform; - if (t) { - ctx.transform(t[0], t[1], t[2], t[3], t[4], t[5]); - } - return { offsetX: 0, offsetY: 0 }; - } - /** * @private * @param {CanvasRenderingContext2D} ctx Context to render on @@ -1265,7 +1234,6 @@ export class FabricObject< dims.zoomX / this.scaleX / retinaScaling, dims.zoomY / this.scaleY / retinaScaling ); - this._applyPatternGradientTransform(pCtx, filler); pCtx.fillStyle = filler.toLive(ctx, this)!; pCtx.fill(); ctx.translate( diff --git a/src/shapes/Text/Text.ts b/src/shapes/Text/Text.ts index bda2f690a3f..1effc68f5b9 100644 --- a/src/shapes/Text/Text.ts +++ b/src/shapes/Text/Text.ts @@ -43,7 +43,6 @@ import { } from './constants'; import { CENTER, LEFT, RIGHT, TOP, BOTTOM } from '../../constants'; import { isFiller } from '../../util/typeAssertions'; -import type { Gradient } from '../../gradient/Gradient'; import type { CSSRules } from '../../parser/typedefs'; let measuringContext: CanvasRenderingContext2D | null; @@ -1230,60 +1229,13 @@ export class Text< ctx.restore(); } - /** - * This function try to patch the missing gradientTransform on canvas gradients. - * transforming a context to transform the gradient, is going to transform the stroke too. - * we want to transform the gradient but not the stroke operation, so we create - * a transformed gradient on a pattern and then we use the pattern instead of the gradient. - * this method has drawbacks: is slow, is in low resolution, needs a patch for when the size - * is limited. - * @private - * @param {TFiller} filler a fabric gradient instance - * @return {CanvasPattern} a pattern to use as fill/stroke style - */ - _applyPatternGradientTransformText(filler: TFiller) { - const pCanvas = createCanvasElement(), - // TODO: verify compatibility with strokeUniform - width = this.width + this.strokeWidth, - height = this.height + this.strokeWidth, - pCtx = pCanvas.getContext('2d')!; - pCanvas.width = width; - pCanvas.height = height; - pCtx.beginPath(); - pCtx.moveTo(0, 0); - pCtx.lineTo(width, 0); - pCtx.lineTo(width, height); - pCtx.lineTo(0, height); - pCtx.closePath(); - pCtx.translate(width / 2, height / 2); - pCtx.fillStyle = filler.toLive(pCtx, this)!; - this._applyPatternGradientTransform(pCtx, filler); - pCtx.fill(); - return pCtx.createPattern(pCanvas, 'no-repeat')!; - } - handleFiller( ctx: CanvasRenderingContext2D, property: `${T}Style`, filler: TFiller | string ): { offsetX: number; offsetY: number } { - let offsetX: number, offsetY: number; if (isFiller(filler)) { - if ((filler as Gradient<'linear'>).gradientTransform) { - // need to transform gradient in a pattern. - // this is a slow process. If you are hitting this codepath, and the object - // is not using caching, you should consider switching it on. - // we need a canvas as big as the current object caching canvas. - offsetX = -this.width / 2; - offsetY = -this.height / 2; - ctx.translate(offsetX, offsetY); - ctx[property] = this._applyPatternGradientTransformText(filler); - return { offsetX, offsetY }; - } else { - // is a simple gradient or pattern - ctx[property] = filler.toLive(ctx, this)!; - return this._applyPatternGradientTransform(ctx, filler); - } + ctx[property] = filler.toLive(ctx, this)!; } else { // is a color ctx[property] = filler; From 8e394b10c05511b56046c888caa96f2c7f74b258 Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Wed, 20 Sep 2023 09:08:48 +0530 Subject: [PATCH 05/14] test fix test --- e2e/setup/index.ts | 4 +- e2e/setup/setupApp.ts | 3 +- e2e/tests/gradient-transform/common.ts | 55 ++++++++++++++++++ e2e/tests/gradient-transform/index.spec.ts | 34 +++++++++++ .../index.spec.ts-snapshots/rect.png | Bin 0 -> 32587 bytes .../index.spec.ts-snapshots/text.png | Bin 0 -> 73435 bytes e2e/tests/gradient-transform/index.ts | 18 ++++++ e2e/tests/template-with-node/index.spec.ts | 2 +- e2e/utils/CanvasUtil.ts | 12 +++- 9 files changed, 122 insertions(+), 6 deletions(-) create mode 100644 e2e/tests/gradient-transform/common.ts create mode 100644 e2e/tests/gradient-transform/index.spec.ts create mode 100644 e2e/tests/gradient-transform/index.spec.ts-snapshots/rect.png create mode 100644 e2e/tests/gradient-transform/index.spec.ts-snapshots/text.png create mode 100644 e2e/tests/gradient-transform/index.ts diff --git a/e2e/setup/index.ts b/e2e/setup/index.ts index ad4334b852c..2def17be811 100644 --- a/e2e/setup/index.ts +++ b/e2e/setup/index.ts @@ -2,11 +2,11 @@ import setupApp from './setupApp'; import setupCoverage from './setupCoverage'; import setupSelectors from './setupSelectors'; -export default () => { +export default (testConfig?: () => any) => { // call first setupSelectors(); // call before using fabric setupCoverage(); // call at the end - navigates the page - setupApp(); + setupApp(testConfig); }; diff --git a/e2e/setup/setupApp.ts b/e2e/setup/setupApp.ts index 95c362619b6..cd61360efd5 100644 --- a/e2e/setup/setupApp.ts +++ b/e2e/setup/setupApp.ts @@ -4,8 +4,9 @@ import path from 'path'; import imports from '../imports'; import { JSDOM } from 'jsdom'; -export default () => { +export default (testConfig: () => any = () => {}) => { test.beforeEach(async ({ page }, { file }) => { + await page.exposeFunction('testConfig', testConfig); await page.goto('/e2e/site'); // expose imports for consumption page.addScriptTag({ diff --git a/e2e/tests/gradient-transform/common.ts b/e2e/tests/gradient-transform/common.ts new file mode 100644 index 00000000000..a91d5676859 --- /dev/null +++ b/e2e/tests/gradient-transform/common.ts @@ -0,0 +1,55 @@ +/** + * Runs from both the browser and node + */ + +import type { StaticCanvas } from 'fabric'; + +// eslint-disable-next-line @typescript-eslint/consistent-type-imports +export function render( + canvas: StaticCanvas, + fabric: typeof import('fabric'), + { type }: { type: 'text' | 'rect' } +) { + const targets = new Array(8).fill(0).map((_, index) => { + const angle = index * 45; + const gradient = new fabric.Gradient({ + coords: { + x1: 0, + y1: 0, + x2: 1, + y2: 1, + }, + gradientUnits: 'percentage', + // offsetX: 150, + gradientTransform: fabric.util.createRotateMatrix({ angle }), + colorStops: [ + { + offset: 0, + color: 'red', + }, + { + offset: 1, + color: 'blue', + }, + ], + }); + + return type === 'text' + ? new fabric.Text(`Gradient\n${angle}°`, { + fontSize: 50, + fontWeight: 'bold', + fill: gradient, + left: (index % 4) * 250, + top: Math.floor(index / 4) * 250, + }) + : new fabric.Rect({ + width: 150, + height: 150, + fill: gradient, + left: (index % 4) * 250, + top: Math.floor(index / 4) * 250, + }); + }); + + canvas.add(...targets); +} diff --git a/e2e/tests/gradient-transform/index.spec.ts b/e2e/tests/gradient-transform/index.spec.ts new file mode 100644 index 00000000000..b249924f088 --- /dev/null +++ b/e2e/tests/gradient-transform/index.spec.ts @@ -0,0 +1,34 @@ +import { expect, test } from '@playwright/test'; +import setup from '../../setup'; +import { CanvasUtil } from '../../utils/CanvasUtil'; +import { createNodeSnapshot } from '../../utils/createNodeSnapshot'; +import { render } from './common'; + +for (const type of ['rect', 'text'] as const) { + test.describe(`Gradient transform on ${type}`, () => { + setup(() => ({ type })); + test(`Gradient transform on ${type}`, async ({ page }, config) => { + await test.step('browser', async () => { + expect( + await new CanvasUtil(page).screenshot({ element: 'main' }), + 'browser snapshot' + ).toMatchSnapshot({ + name: `${type}.png`, + maxDiffPixelRatio: 0.05, + }); + }); + + await test.step('node', async () => { + // we want the browser snapshot of a test to be committed and not the node snapshot + config.config.updateSnapshots = 'none'; + expect( + await createNodeSnapshot( + (canvas, fabric) => render(canvas, fabric, { type }), + { width: 1000, height: 400 } + ), + 'node snapshot should match browser snapshot' + ).toMatchSnapshot({ name: `${type}.png`, maxDiffPixelRatio: 0.05 }); + }); + }); + }); +} diff --git a/e2e/tests/gradient-transform/index.spec.ts-snapshots/rect.png b/e2e/tests/gradient-transform/index.spec.ts-snapshots/rect.png new file mode 100644 index 0000000000000000000000000000000000000000..568e95f6fd01bfd28c00029355d61939c911c70a GIT binary patch literal 32587 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 z%0ANHmAM#|$+deN4D>&4hZ{uIm3BBm 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 Ur3Qw7pZ|i9zPVnxj!Wc!0J+?;>;M1& literal 0 HcmV?d00001 diff --git a/e2e/tests/gradient-transform/index.ts b/e2e/tests/gradient-transform/index.ts new file mode 100644 index 00000000000..4c1ae424812 --- /dev/null +++ b/e2e/tests/gradient-transform/index.ts @@ -0,0 +1,18 @@ +/** + * Runs in the **BROWSER** + */ + +import * as fabric from 'fabric'; +import { before } from '../test'; +import { render } from './common'; + +before('#canvas', async (el) => { + const canvas = new fabric.StaticCanvas(el, { + width: 1000, + height: 400, + backgroundColor: 'white', + enableRetinaScaling: false, + }); + await render(canvas, fabric, await testConfig()); + return canvas; +}); diff --git a/e2e/tests/template-with-node/index.spec.ts b/e2e/tests/template-with-node/index.spec.ts index 9a4426ca1fc..cb9ff34ae54 100644 --- a/e2e/tests/template-with-node/index.spec.ts +++ b/e2e/tests/template-with-node/index.spec.ts @@ -9,7 +9,7 @@ setup(); test('TEST NAME', async ({ page }, config) => { await test.step('browser', async () => { expect( - await new CanvasUtil(page).screenshot(), + await new CanvasUtil(page).screenshot({ element: 'main' }), 'browser snapshot' ).toMatchSnapshot({ name: 'textbox.png', diff --git a/e2e/utils/CanvasUtil.ts b/e2e/utils/CanvasUtil.ts index d5a67672d05..c6755a4267f 100644 --- a/e2e/utils/CanvasUtil.ts +++ b/e2e/utils/CanvasUtil.ts @@ -25,9 +25,17 @@ export class CanvasUtil { return this.page.keyboard.press(`${modifier}+KeyV`); } - screenshot(options: LocatorScreenshotOptions = {}) { + screenshot({ + element = 'wrapper', + ...options + }: LocatorScreenshotOptions & { element?: 'wrapper' | 'top' | 'main' } = {}) { + const selector = { + wrapper: `canvas_wrapper=${this.selector}`, + top: `canvas_top=${this.selector}`, + main: this.selector, + }[element]; return this.page - .locator(`canvas_wrapper=${this.selector}`) + .locator(selector) .screenshot({ omitBackground: true, ...options }); } From 4852712aede988fdca4a04966d3f051f3ecbce11 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 20 Sep 2023 03:57:17 +0000 Subject: [PATCH 06/14] update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a3656a5464..a7e153e25db 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## [next] +- fix(): gradient transform + rm workarounds [#9359](https://github.com/fabricjs/fabric.js/pull/9359) - fix(IText): cursor width under group [#9341](https://github.com/fabricjs/fabric.js/pull/9341) - TS(Canvas): constructor optional el [#9348](https://github.com/fabricjs/fabric.js/pull/9348) From 08ebe26506f1ad9d254abda94f41aecd508acf5b Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Wed, 20 Sep 2023 09:40:24 +0530 Subject: [PATCH 07/14] update node template --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a7e153e25db..48beeedf322 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,9 @@ ## [next] - fix(): gradient transform + rm workarounds [#9359](https://github.com/fabricjs/fabric.js/pull/9359) + **BREAKING**: + - `toLive(ctx)` => `toLive(ctx, target)` + - rm(): `_applyPatternGradientTransformText`, `_applyPatternGradientTransform` - fix(IText): cursor width under group [#9341](https://github.com/fabricjs/fabric.js/pull/9341) - TS(Canvas): constructor optional el [#9348](https://github.com/fabricjs/fabric.js/pull/9348) From 7db3da41beecfdf108bd9dc17157cfaf9d4651d1 Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Wed, 20 Sep 2023 09:49:38 +0530 Subject: [PATCH 08/14] cleanup c cleaner Update index.spec.ts --- e2e/setup/index.ts | 3 +++ e2e/setup/setupApp.ts | 3 +++ e2e/tests/gradient-transform/common.ts | 16 +++++++++++----- e2e/tests/gradient-transform/index.spec.ts | 14 ++++++++------ e2e/tests/gradient-transform/index.ts | 13 +++---------- e2e/tests/template-with-node/common.ts | 1 + e2e/tests/template-with-node/index.ts | 11 +++++++++-- e2e/tests/test.ts | 11 +++++++---- 8 files changed, 45 insertions(+), 27 deletions(-) diff --git a/e2e/setup/index.ts b/e2e/setup/index.ts index 2def17be811..dd85398bc73 100644 --- a/e2e/setup/index.ts +++ b/e2e/setup/index.ts @@ -2,6 +2,9 @@ import setupApp from './setupApp'; import setupCoverage from './setupCoverage'; import setupSelectors from './setupSelectors'; +/** + * @param {Function} [testConfig] pass data/config from the test to the browser + */ export default (testConfig?: () => any) => { // call first setupSelectors(); diff --git a/e2e/setup/setupApp.ts b/e2e/setup/setupApp.ts index cd61360efd5..d5225e9f39e 100644 --- a/e2e/setup/setupApp.ts +++ b/e2e/setup/setupApp.ts @@ -4,6 +4,9 @@ import path from 'path'; import imports from '../imports'; import { JSDOM } from 'jsdom'; +/** + * @param {Function} [testConfig] pass data/config from the test to the browser + */ export default (testConfig: () => any = () => {}) => { test.beforeEach(async ({ page }, { file }) => { await page.exposeFunction('testConfig', testConfig); diff --git a/e2e/tests/gradient-transform/common.ts b/e2e/tests/gradient-transform/common.ts index a91d5676859..eb3f13955dd 100644 --- a/e2e/tests/gradient-transform/common.ts +++ b/e2e/tests/gradient-transform/common.ts @@ -2,13 +2,10 @@ * Runs from both the browser and node */ -import type { StaticCanvas } from 'fabric'; - -// eslint-disable-next-line @typescript-eslint/consistent-type-imports export function render( - canvas: StaticCanvas, + // eslint-disable-next-line @typescript-eslint/consistent-type-imports fabric: typeof import('fabric'), - { type }: { type: 'text' | 'rect' } + { type, el }: { type: 'text' | 'rect'; el?: HTMLCanvasElement } ) { const targets = new Array(8).fill(0).map((_, index) => { const angle = index * 45; @@ -51,5 +48,14 @@ export function render( }); }); + const canvas = new fabric.StaticCanvas(el, { + width: 1000, + height: 400, + backgroundColor: 'white', + enableRetinaScaling: false, + }); canvas.add(...targets); + canvas.renderAll(); + + return { canvas }; } diff --git a/e2e/tests/gradient-transform/index.spec.ts b/e2e/tests/gradient-transform/index.spec.ts index b249924f088..a00e35b877f 100644 --- a/e2e/tests/gradient-transform/index.spec.ts +++ b/e2e/tests/gradient-transform/index.spec.ts @@ -1,13 +1,13 @@ import { expect, test } from '@playwright/test'; +import type { StaticCanvas } from 'fabric/node'; import setup from '../../setup'; import { CanvasUtil } from '../../utils/CanvasUtil'; -import { createNodeSnapshot } from '../../utils/createNodeSnapshot'; import { render } from './common'; for (const type of ['rect', 'text'] as const) { test.describe(`Gradient transform on ${type}`, () => { setup(() => ({ type })); - test(`Gradient transform on ${type}`, async ({ page }, config) => { + test('Gradient transform', async ({ page }, config) => { await test.step('browser', async () => { expect( await new CanvasUtil(page).screenshot({ element: 'main' }), @@ -22,10 +22,12 @@ for (const type of ['rect', 'text'] as const) { // we want the browser snapshot of a test to be committed and not the node snapshot config.config.updateSnapshots = 'none'; expect( - await createNodeSnapshot( - (canvas, fabric) => render(canvas, fabric, { type }), - { width: 1000, height: 400 } - ), + ( + (await render(await import('../../..'), { type })) + .canvas as StaticCanvas + ) + .getNodeCanvas() + .toBuffer(), 'node snapshot should match browser snapshot' ).toMatchSnapshot({ name: `${type}.png`, maxDiffPixelRatio: 0.05 }); }); diff --git a/e2e/tests/gradient-transform/index.ts b/e2e/tests/gradient-transform/index.ts index 4c1ae424812..f5e657eb8ee 100644 --- a/e2e/tests/gradient-transform/index.ts +++ b/e2e/tests/gradient-transform/index.ts @@ -6,13 +6,6 @@ import * as fabric from 'fabric'; import { before } from '../test'; import { render } from './common'; -before('#canvas', async (el) => { - const canvas = new fabric.StaticCanvas(el, { - width: 1000, - height: 400, - backgroundColor: 'white', - enableRetinaScaling: false, - }); - await render(canvas, fabric, await testConfig()); - return canvas; -}); +before('#canvas', async (el) => + render(fabric, { ...(await testConfig()), el }) +); diff --git a/e2e/tests/template-with-node/common.ts b/e2e/tests/template-with-node/common.ts index e00e20cf30a..daaead0bb2a 100644 --- a/e2e/tests/template-with-node/common.ts +++ b/e2e/tests/template-with-node/common.ts @@ -14,4 +14,5 @@ export function render(canvas: StaticCanvas, fabric: typeof import('fabric')) { canvas.add(textbox); canvas.centerObjectH(textbox); canvas.renderAll(); + return { textbox }; } diff --git a/e2e/tests/template-with-node/index.ts b/e2e/tests/template-with-node/index.ts index 8b22350eec2..6de80209733 100644 --- a/e2e/tests/template-with-node/index.ts +++ b/e2e/tests/template-with-node/index.ts @@ -3,7 +3,14 @@ */ import * as fabric from 'fabric'; -import { beforeAll } from '../test'; +import { before } from '../test'; import { render } from './common'; -beforeAll((canvas) => render(canvas, fabric), { enableRetinaScaling: false }); +before('#canvas', async (el) => { + const canvas = new fabric.StaticCanvas(el, { + backgroundColor: 'white', + enableRetinaScaling: false, + }); + const objects = await render(canvas, fabric); + return { canvas, objects }; +}); diff --git a/e2e/tests/test.ts b/e2e/tests/test.ts index 91af2e38331..3a9ef78b6f2 100644 --- a/e2e/tests/test.ts +++ b/e2e/tests/test.ts @@ -3,10 +3,13 @@ */ import type { Object as FabricObject } from 'fabric'; -import { Canvas } from 'fabric'; +import { Canvas, type StaticCanvas } from 'fabric'; import * as fabric from 'fabric'; -const canvasMap = (window.canvasMap = new Map()); +const canvasMap = (window.canvasMap = new Map< + HTMLCanvasElement, + Canvas | StaticCanvas +>()); const objectMap = (window.objectMap = new Map()); type AsyncReturnValue = T | Promise; @@ -33,7 +36,7 @@ export function before( * @returns a map of objects for playwright to access during tests */ cb: (canvas: HTMLCanvasElement) => AsyncReturnValue<{ - canvas: Canvas; + canvas: Canvas | StaticCanvas; objects?: Record; }> ) { @@ -72,7 +75,7 @@ export function beforeAll( export function after( selector: string, - cb: (canvas: Canvas) => AsyncReturnValue + cb: (canvas: Canvas | StaticCanvas) => AsyncReturnValue ) { teardownTasks.push(() => { const el = document.querySelector(selector); From 297bdb186befc4a5884b746d117044bfadb3f25f Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Wed, 20 Sep 2023 10:15:51 +0530 Subject: [PATCH 09/14] cleaner template --- e2e/tests/template-with-node/common.ts | 19 +++++++++++++------ e2e/tests/template-with-node/index.spec.ts | 6 ++++-- e2e/tests/template-with-node/index.ts | 9 +-------- 3 files changed, 18 insertions(+), 16 deletions(-) diff --git a/e2e/tests/template-with-node/common.ts b/e2e/tests/template-with-node/common.ts index daaead0bb2a..bb0a57c0227 100644 --- a/e2e/tests/template-with-node/common.ts +++ b/e2e/tests/template-with-node/common.ts @@ -2,11 +2,17 @@ * Runs from both the browser and node */ -import type { StaticCanvas } from 'fabric'; - -// eslint-disable-next-line @typescript-eslint/consistent-type-imports -export function render(canvas: StaticCanvas, fabric: typeof import('fabric')) { - canvas.setDimensions({ width: 200, height: 70 }); +export function render( + // eslint-disable-next-line @typescript-eslint/consistent-type-imports + fabric: typeof import('fabric'), + { el }: { el?: HTMLCanvasElement } = {} +) { + const canvas = new fabric.StaticCanvas(el, { + width: 200, + height: 70, + backgroundColor: 'white', + enableRetinaScaling: false, + }); const textbox = new fabric.Textbox('fabric.js test', { width: 200, top: 20, @@ -14,5 +20,6 @@ export function render(canvas: StaticCanvas, fabric: typeof import('fabric')) { canvas.add(textbox); canvas.centerObjectH(textbox); canvas.renderAll(); - return { textbox }; + + return { canvas, objects: { textbox } }; } diff --git a/e2e/tests/template-with-node/index.spec.ts b/e2e/tests/template-with-node/index.spec.ts index cb9ff34ae54..b45315c0e46 100644 --- a/e2e/tests/template-with-node/index.spec.ts +++ b/e2e/tests/template-with-node/index.spec.ts @@ -1,7 +1,7 @@ import { expect, test } from '@playwright/test'; +import type { StaticCanvas } from 'fabric/node'; import setup from '../../setup'; import { CanvasUtil } from '../../utils/CanvasUtil'; -import { createNodeSnapshot } from '../../utils/createNodeSnapshot'; import { render } from './common'; setup(); @@ -21,7 +21,9 @@ test('TEST NAME', async ({ page }, config) => { // we want the browser snapshot of a test to be committed and not the node snapshot config.config.updateSnapshots = 'none'; expect( - await createNodeSnapshot(render), + ((await render(await import('../../..'))).canvas as StaticCanvas) + .getNodeCanvas() + .toBuffer(), 'node snapshot should match browser snapshot' ).toMatchSnapshot({ name: 'textbox.png', maxDiffPixelRatio: 0.05 }); }); diff --git a/e2e/tests/template-with-node/index.ts b/e2e/tests/template-with-node/index.ts index 6de80209733..51b4dd0e7c1 100644 --- a/e2e/tests/template-with-node/index.ts +++ b/e2e/tests/template-with-node/index.ts @@ -6,11 +6,4 @@ import * as fabric from 'fabric'; import { before } from '../test'; import { render } from './common'; -before('#canvas', async (el) => { - const canvas = new fabric.StaticCanvas(el, { - backgroundColor: 'white', - enableRetinaScaling: false, - }); - const objects = await render(canvas, fabric); - return { canvas, objects }; -}); +before('#canvas', (el) => render(fabric, { el })); From ea6635697e9d37a0dcd8d6a1528cec01fd129a5d Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Wed, 20 Sep 2023 10:32:37 +0530 Subject: [PATCH 10/14] cleanup --- src/Pattern/Pattern.ts | 4 ++-- src/canvas/StaticCanvas.ts | 3 +-- src/shapes/Line.ts | 8 +++----- src/shapes/Object/Object.ts | 13 ++----------- src/shapes/Text/Text.ts | 7 +------ 5 files changed, 9 insertions(+), 26 deletions(-) diff --git a/src/Pattern/Pattern.ts b/src/Pattern/Pattern.ts index d23984154c1..1c94f11b59e 100644 --- a/src/Pattern/Pattern.ts +++ b/src/Pattern/Pattern.ts @@ -131,7 +131,7 @@ export class Pattern { toLive( ctx: CanvasRenderingContext2D, target: StaticCanvas | FabricObject - ): CanvasPattern | null { + ): CanvasPattern | '' { if ( // if the image failed to load, return, and allow rest to continue loading !this.source || @@ -141,7 +141,7 @@ export class Pattern { this.source.naturalWidth === 0 || this.source.naturalHeight === 0)) ) { - return null; + return ''; } const pattern = ctx.createPattern(this.source, this.repeat)!; diff --git a/src/canvas/StaticCanvas.ts b/src/canvas/StaticCanvas.ts index c5b827e8ca1..f20c717cfc1 100644 --- a/src/canvas/StaticCanvas.ts +++ b/src/canvas/StaticCanvas.ts @@ -640,7 +640,6 @@ export class StaticCanvas< if (!fill && !object) { return; } - const isAFiller = isFiller(fill); if (fill) { ctx.save(); ctx.beginPath(); @@ -649,7 +648,7 @@ export class StaticCanvas< ctx.lineTo(this.width, this.height); ctx.lineTo(0, this.height); ctx.closePath(); - ctx.fillStyle = isAFiller ? fill.toLive(ctx, this)! : fill; + ctx.fillStyle = isFiller(fill) ? fill.toLive(ctx, this) : fill; if (needsVpt) { ctx.transform(...v); } diff --git a/src/shapes/Line.ts b/src/shapes/Line.ts index 0ac2ec73a0c..d1135b040dd 100644 --- a/src/shapes/Line.ts +++ b/src/shapes/Line.ts @@ -131,11 +131,9 @@ export class Line< // make sure setting "fill" changes color of a line // (by copying fillStyle to strokeStyle, since line is stroked, not filled) const origStrokeStyle = ctx.strokeStyle; - if (isFiller(this.stroke)) { - ctx.strokeStyle = this.stroke.toLive(ctx, this)!; - } else { - ctx.strokeStyle = this.stroke ?? ctx.fillStyle; - } + ctx.strokeStyle = isFiller(this.stroke) + ? this.stroke.toLive(ctx, this) + : this.stroke ?? ctx.fillStyle; this.stroke && this._renderStroke(ctx); ctx.strokeStyle = origStrokeStyle; } diff --git a/src/shapes/Object/Object.ts b/src/shapes/Object/Object.ts index a5dd77cc5ee..f2c1c01bbbf 100644 --- a/src/shapes/Object/Object.ts +++ b/src/shapes/Object/Object.ts @@ -1045,22 +1045,13 @@ export class FabricObject< ctx.lineDashOffset = decl.strokeDashOffset; ctx.lineJoin = decl.strokeLineJoin; ctx.miterLimit = decl.strokeMiterLimit; - if (isFiller(stroke)) { - ctx.strokeStyle = stroke.toLive(ctx, this)!; - } else { - // is a color - ctx.strokeStyle = decl.stroke as string; - } + ctx.strokeStyle = isFiller(stroke) ? stroke.toLive(ctx, this) : stroke; } } _setFillStyles(ctx: CanvasRenderingContext2D, { fill }: Pick) { if (fill) { - if (isFiller(fill)) { - ctx.fillStyle = fill.toLive(ctx, this)!; - } else { - ctx.fillStyle = fill; - } + ctx.fillStyle = isFiller(fill) ? fill.toLive(ctx, this) : fill; } } diff --git a/src/shapes/Text/Text.ts b/src/shapes/Text/Text.ts index 1effc68f5b9..8656896ee1f 100644 --- a/src/shapes/Text/Text.ts +++ b/src/shapes/Text/Text.ts @@ -1234,12 +1234,7 @@ export class Text< property: `${T}Style`, filler: TFiller | string ): { offsetX: number; offsetY: number } { - if (isFiller(filler)) { - ctx[property] = filler.toLive(ctx, this)!; - } else { - // is a color - ctx[property] = filler; - } + ctx[property] = isFiller(filler) ? filler.toLive(ctx, this) : filler; return { offsetX: 0, offsetY: 0 }; } From 581bd257473750e24d04d5264a15d3ec33fb51fe Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Wed, 20 Sep 2023 10:40:32 +0530 Subject: [PATCH 11/14] Update index.node.ts --- index.node.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/index.node.ts b/index.node.ts index 0be09bd7bc2..0ccde639494 100644 --- a/index.node.ts +++ b/index.node.ts @@ -1,6 +1,7 @@ // first we set the env variable by importing the node env file import { getNodeCanvas } from './src/env/node'; +import { DOMMatrix } from 'canvas'; import type { JpegConfig, PngConfig } from 'canvas'; import { Canvas as CanvasBase, @@ -10,6 +11,9 @@ import { FabricObject } from './src/shapes/Object/Object'; FabricObject.ownDefaults.objectCaching = false; +// @ts-expect-error global polyfill +global.DOMMatrix = DOMMatrix; + export * from './fabric'; export class StaticCanvas extends StaticCanvasBase { From 9ae9bc315caa529eb029ea73b883d657c3fb18e2 Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Wed, 20 Sep 2023 11:00:36 +0530 Subject: [PATCH 12/14] more cleanup --- src/gradient/Gradient.ts | 2 +- src/shapes/Object/Object.ts | 60 +++---------------------------- src/shapes/Text/Text.ts | 66 ++++++----------------------------- test/visual/golden/text6.png | Bin 7675 -> 7858 bytes 4 files changed, 16 insertions(+), 112 deletions(-) diff --git a/src/gradient/Gradient.ts b/src/gradient/Gradient.ts index 4c51b709ac4..7476805bc1f 100644 --- a/src/gradient/Gradient.ts +++ b/src/gradient/Gradient.ts @@ -306,7 +306,6 @@ export class Gradient< ctx: CanvasRenderingContext2D, target: StaticCanvas | FabricObject ): CanvasGradient { - const coords = this.coords as GradientCoords<'radial'>; const { offsetX = 0, offsetY = 0 } = this; const { x, y } = // correct rendering position from object rendering origin (center) to tl @@ -327,6 +326,7 @@ export class Gradient< const p2 = new Point(x2, y2).transform(transform); gradient = ctx.createLinearGradient(p1.x, p1.y, p2.x, p2.y); } else { + const coords = this.coords as GradientCoords<'radial'>; gradient = ctx.createRadialGradient( coords.x1 + offsetX + x, coords.y1 + offsetY + y, diff --git a/src/shapes/Object/Object.ts b/src/shapes/Object/Object.ts index f2c1c01bbbf..6c060f87e71 100644 --- a/src/shapes/Object/Object.ts +++ b/src/shapes/Object/Object.ts @@ -47,7 +47,6 @@ import { fabricObjectDefaultValues, stateProperties, } from './defaultValues'; -import type { Gradient } from '../../gradient/Gradient'; import type { Canvas } from '../../canvas/Canvas'; import type { SerializedObjectProps } from './types/SerializedObjectProps'; import type { ObjectProps } from './types/ObjectProps'; @@ -1029,7 +1028,7 @@ export class FabricObject< _setStrokeStyles( ctx: CanvasRenderingContext2D, decl: Pick< - this, + ObjectProps, | 'stroke' | 'strokeWidth' | 'strokeLineCap' @@ -1049,7 +1048,10 @@ export class FabricObject< } } - _setFillStyles(ctx: CanvasRenderingContext2D, { fill }: Pick) { + _setFillStyles( + ctx: CanvasRenderingContext2D, + { fill }: Pick + ) { if (fill) { ctx.fillStyle = isFiller(fill) ? fill.toLive(ctx, this) : fill; } @@ -1186,58 +1188,6 @@ export class FabricObject< ctx.restore(); } - /** - * This function try to patch the missing gradientTransform on canvas gradients. - * transforming a context to transform the gradient, is going to transform the stroke too. - * we want to transform the gradient but not the stroke operation, so we create - * a transformed gradient on a pattern and then we use the pattern instead of the gradient. - * this method has drawbacks: is slow, is in low resolution, needs a patch for when the size - * is limited. - * @private - * @param {CanvasRenderingContext2D} ctx Context to render on - * @param {Gradient} filler - */ - _applyPatternForTransformedGradient( - ctx: CanvasRenderingContext2D, - filler: TFiller - ) { - const dims = this._limitCacheSize(this._getCacheCanvasDimensions()), - pCanvas = createCanvasElement(), - retinaScaling = this.getCanvasRetinaScaling(), - width = dims.x / this.scaleX / retinaScaling, - height = dims.y / this.scaleY / retinaScaling; - // in case width and height are less than 1px, we have to round up. - // since the pattern is no-repeat, this is fine - pCanvas.width = Math.ceil(width); - pCanvas.height = Math.ceil(height); - const pCtx = pCanvas.getContext('2d'); - if (!pCtx) { - return; - } - pCtx.beginPath(); - pCtx.moveTo(0, 0); - pCtx.lineTo(width, 0); - pCtx.lineTo(width, height); - pCtx.lineTo(0, height); - pCtx.closePath(); - pCtx.translate(width / 2, height / 2); - pCtx.scale( - dims.zoomX / this.scaleX / retinaScaling, - dims.zoomY / this.scaleY / retinaScaling - ); - pCtx.fillStyle = filler.toLive(ctx, this)!; - pCtx.fill(); - ctx.translate( - -this.width / 2 - this.strokeWidth / 2, - -this.height / 2 - this.strokeWidth / 2 - ); - ctx.scale( - (retinaScaling * this.scaleX) / dims.zoomX, - (retinaScaling * this.scaleY) / dims.zoomY - ); - ctx.strokeStyle = pCtx.createPattern(pCanvas, 'no-repeat') ?? ''; - } - /** * This function is an helper for svg import. it returns the center of the object in the svg * untransformed coordinates diff --git a/src/shapes/Text/Text.ts b/src/shapes/Text/Text.ts index 8656896ee1f..fcee5c9f40a 100644 --- a/src/shapes/Text/Text.ts +++ b/src/shapes/Text/Text.ts @@ -13,7 +13,6 @@ import type { Abortable, TCacheCanvasDimensions, TClassProperties, - TFiller, TOptions, } from '../../typedefs'; import { classRegistry } from '../../ClassRegistry'; @@ -42,7 +41,6 @@ import { JUSTIFY_RIGHT, } from './constants'; import { CENTER, LEFT, RIGHT, TOP, BOTTOM } from '../../constants'; -import { isFiller } from '../../util/typeAssertions'; import type { CSSRules } from '../../parser/typedefs'; let measuringContext: CanvasRenderingContext2D | null; @@ -1229,48 +1227,6 @@ export class Text< ctx.restore(); } - handleFiller( - ctx: CanvasRenderingContext2D, - property: `${T}Style`, - filler: TFiller | string - ): { offsetX: number; offsetY: number } { - ctx[property] = isFiller(filler) ? filler.toLive(ctx, this) : filler; - return { offsetX: 0, offsetY: 0 }; - } - - /** - * This function prepare the canvas for a stroke style, and stroke and strokeWidth - * need to be sent in as defined - * @param {CanvasRenderingContext2D} ctx - * @param {CompleteTextStyleDeclaration} style with stroke and strokeWidth defined - * @returns - */ - _setStrokeStyles( - ctx: CanvasRenderingContext2D, - { - stroke, - strokeWidth, - }: Pick - ) { - ctx.lineWidth = strokeWidth; - ctx.lineCap = this.strokeLineCap; - ctx.lineDashOffset = this.strokeDashOffset; - ctx.lineJoin = this.strokeLineJoin; - ctx.miterLimit = this.strokeMiterLimit; - return this.handleFiller(ctx, 'strokeStyle', stroke!); - } - - /** - * This function prepare the canvas for a ill style, and fill - * need to be sent in as defined - * @param {CanvasRenderingContext2D} ctx - * @param {CompleteTextStyleDeclaration} style with ill defined - * @returns - */ - _setFillStyles(ctx: CanvasRenderingContext2D, { fill }: Pick) { - return this.handleFiller(ctx, 'fillStyle', fill!); - } - /** * @private * @param {String} method @@ -1312,21 +1268,19 @@ export class Text< } if (shouldFill) { - const fillOffsets = this._setFillStyles(ctx, fullDecl); - ctx.fillText( - _char, - left - fillOffsets.offsetX, - top - fillOffsets.offsetY - ); + this._setFillStyles(ctx, fullDecl); + ctx.fillText(_char, left, top); } if (shouldStroke) { - const strokeOffsets = this._setStrokeStyles(ctx, fullDecl); - ctx.strokeText( - _char, - left - strokeOffsets.offsetX, - top - strokeOffsets.offsetY - ); + this._setStrokeStyles(ctx, { + strokeLineCap: this.strokeLineCap, + strokeDashOffset: this.strokeDashOffset, + strokeLineJoin: this.strokeLineJoin, + strokeMiterLimit: this.strokeMiterLimit, + ...fullDecl, + }); + ctx.strokeText(_char, left, top); } ctx.restore(); diff --git a/test/visual/golden/text6.png b/test/visual/golden/text6.png index 6f6691e61bed4766d885bbedf3d030e1ccce9d71..ec47fb81dac10bce1382b2aa90601d78d509bb95 100644 GIT binary patch literal 7858 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 literal 7675 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 From cdbe7a6c57fae899aa3ca813bf160035162a22fb Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Wed, 20 Sep 2023 12:54:39 +0530 Subject: [PATCH 13/14] radial percentage --- src/gradient/Gradient.ts | 30 ++++++++++++------------------ 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/src/gradient/Gradient.ts b/src/gradient/Gradient.ts index 7476805bc1f..bcaadc7f143 100644 --- a/src/gradient/Gradient.ts +++ b/src/gradient/Gradient.ts @@ -312,29 +312,23 @@ export class Gradient< target instanceof FabricObject ? { x: -target.width / 2, y: -target.height / 2 } : { x: 0, y: 0 }; + const transform = multiplyTransformMatrixArray([ + this.gradientTransform, + createTranslateMatrix(offsetX + x, offsetY + y), + this.gradientUnits === 'percentage' && + createScaleMatrix(target.width, target.height), + ]); + const { x1, y1, x2, y2 } = this.coords; + const p1 = new Point(x1, y1).transform(transform); + const p2 = new Point(x2, y2).transform(transform); let gradient: CanvasGradient; if (this.type === 'linear') { - const transform = multiplyTransformMatrixArray([ - this.gradientTransform, - createTranslateMatrix(offsetX + x, offsetY + y), - this.gradientUnits === 'percentage' && - createScaleMatrix(target.width, target.height), - ]); - const { x1, y1, x2, y2 } = this.coords; - const p1 = new Point(x1, y1).transform(transform); - const p2 = new Point(x2, y2).transform(transform); gradient = ctx.createLinearGradient(p1.x, p1.y, p2.x, p2.y); } else { - const coords = this.coords as GradientCoords<'radial'>; - gradient = ctx.createRadialGradient( - coords.x1 + offsetX + x, - coords.y1 + offsetY + y, - coords.r1, - coords.x2 + offsetX + x, - coords.y2 + offsetY + y, - coords.r2 - ); + const { r1, r2 } = this.coords as GradientCoords<'radial'>; + const r = new Point(r1, r2).transform(transform, true); + gradient = ctx.createRadialGradient(p1.x, p1.y, r.x, p2.x, p2.y, r.y); } this.colorStops.forEach(({ color, opacity, offset }) => { From 5a5c38e66d1396c5708ca498e4cca1c3d49dc0b7 Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Wed, 20 Sep 2023 13:16:38 +0530 Subject: [PATCH 14/14] Update Gradient.ts --- src/gradient/Gradient.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/gradient/Gradient.ts b/src/gradient/Gradient.ts index bcaadc7f143..9c6daa41500 100644 --- a/src/gradient/Gradient.ts +++ b/src/gradient/Gradient.ts @@ -24,6 +24,7 @@ import type { StaticCanvas } from '../canvas/StaticCanvas'; import { createScaleMatrix, createTranslateMatrix, + magnitude, multiplyTransformMatrixArray, } from '../util'; import { Point } from '../Point'; @@ -327,8 +328,15 @@ export class Gradient< gradient = ctx.createLinearGradient(p1.x, p1.y, p2.x, p2.y); } else { const { r1, r2 } = this.coords as GradientCoords<'radial'>; - const r = new Point(r1, r2).transform(transform, true); - gradient = ctx.createRadialGradient(p1.x, p1.y, r.x, p2.x, p2.y, r.y); + const hypot = magnitude(new Point(1, 0).transform(transform, true)); + gradient = ctx.createRadialGradient( + p1.x, + p1.y, + r1 * hypot, + p2.x, + p2.y, + r2 * hypot + ); } this.colorStops.forEach(({ color, opacity, offset }) => {