From a0a87e187b62070dcaabc738999574814859a6bb Mon Sep 17 00:00:00 2001 From: airnan Date: Thu, 1 Jun 2023 00:26:05 -0400 Subject: [PATCH 1/8] move paint operation to contextData --- .../elements/canvasElements/CVContextData.js | 198 ++++++++++++++---- .../elements/canvasElements/CVShapeElement.js | 18 +- .../elements/canvasElements/CVSolidElement.js | 3 +- .../elements/canvasElements/CVTextElement.js | 34 +-- player/js/renderers/CanvasRendererBase.js | 81 ++++--- 5 files changed, 248 insertions(+), 86 deletions(-) diff --git a/player/js/elements/canvasElements/CVContextData.js b/player/js/elements/canvasElements/CVContextData.js index f5d8b93d0..f3c132220 100644 --- a/player/js/elements/canvasElements/CVContextData.js +++ b/player/js/elements/canvasElements/CVContextData.js @@ -3,28 +3,45 @@ import { } from '../../utils/helpers/arrays'; import Matrix from '../../3rd_party/transformation-matrix'; +function CanvasContext() { + this.opacity = -1; + this.transform = createTypedArray('float32', 16); + this.fillStyle = ''; + this.strokeStyle = ''; + this.lineWidth = ''; + this.lineCap = ''; + this.lineJoin = ''; + this.miterLimit = ''; + this.id = Math.random(); +} + function CVContextData() { - this.saved = []; + this.stack = []; this.cArrPos = 0; this.cTr = new Matrix(); - this.cO = 1; var i; var len = 15; - this.savedOp = createTypedArray('float32', len); for (i = 0; i < len; i += 1) { - this.saved[i] = createTypedArray('float32', 16); + var canvasContext = new CanvasContext(); + this.stack[i] = canvasContext; } this._length = len; + this.nativeContext = null; + this.transformMat = new Matrix(); + this.currentOpacity = 1; + this.currentFillStyle = ''; + this.currentStrokeStyle = ''; + this.currentLineWidth = ''; + this.currentLineCap = ''; + this.currentLineJoin = ''; + this.currentMiterLimit = ''; } CVContextData.prototype.duplicate = function () { var newLength = this._length * 2; - var currentSavedOp = this.savedOp; - this.savedOp = createTypedArray('float32', newLength); - this.savedOp.set(currentSavedOp); var i = 0; for (i = this._length; i < newLength; i += 1) { - this.saved[i] = createTypedArray('float32', 16); + this.stack[i] = new CanvasContext(); } this._length = newLength; }; @@ -32,59 +49,162 @@ CVContextData.prototype.duplicate = function () { CVContextData.prototype.reset = function () { this.cArrPos = 0; this.cTr.reset(); - this.cO = 1; + this.stack[this.cArrPos].opacity = 1; }; -CVContextData.prototype.popTransform = function () { - var popped = this.saved[this.cArrPos]; +CVContextData.prototype.restore = function (forceRestore) { + this.cArrPos -= 1; + var currentContext = this.stack[this.cArrPos]; + var transform = currentContext.transform; var i; var arr = this.cTr.props; for (i = 0; i < 16; i += 1) { - arr[i] = popped[i]; + arr[i] = transform[i]; + } + this.nativeContext.setTransform(transform[0], transform[1], transform[4], transform[5], transform[12], transform[13]); + if (forceRestore || (currentContext.opacity !== -1 && this.currentOpacity !== currentContext.opacity)) { + this.nativeContext.globalAlpha = currentContext.opacity; + this.currentOpacity = currentContext.opacity; + } + if (forceRestore || (currentContext.fillStyle && this.currentFillStyle !== currentContext.fillStyle)) { + this.nativeContext.fillStyle = currentContext.fillStyle; + this.currentFillStyle = currentContext.fillStyle; + } + if (forceRestore || (currentContext.strokeStyle && this.currentStrokeStyle !== currentContext.strokeStyle)) { + this.nativeContext.strokeStyle = currentContext.strokeStyle; + this.currentStrokeStyle = currentContext.strokeStyle; + } + if (forceRestore || (currentContext.lineWidth && this.currentLineWidth !== currentContext.lineWidth)) { + this.nativeContext.lineWidth = currentContext.lineWidth; + this.currentLineWidth = currentContext.lineWidth; + } + if (forceRestore || (currentContext.lineCap && this.currentLineCap !== currentContext.lineCap)) { + this.nativeContext.lineCap = currentContext.lineCap; + this.currentLineCap = currentContext.lineCap; + } + if (forceRestore || (currentContext.lineJoin && this.currentLineJoin !== currentContext.lineJoin)) { + this.nativeContext.lineJoin = currentContext.lineJoin; + this.currentLineJoin = currentContext.lineJoin; + } + if (currentContext.miterLimit && this.currentMiterLimit !== currentContext.miterLimit) { + this.nativeContext.miterLimit = currentContext.miterLimit; + this.currentMiterLimit = currentContext.miterLimit; } - return popped; -}; - -CVContextData.prototype.popOpacity = function () { - var popped = this.savedOp[this.cArrPos]; - this.cO = popped; - return popped; -}; - -CVContextData.prototype.pop = function () { - this.cArrPos -= 1; - var transform = this.popTransform(); - var opacity = this.popOpacity(); - return { - transform: transform, - opacity: opacity, - }; }; -CVContextData.prototype.push = function () { +CVContextData.prototype.save = function () { var props = this.cTr.props; if (this._length <= this.cArrPos) { this.duplicate(); } + + var currentStack = this.stack[this.cArrPos]; var i; - var arr = this.saved[this.cArrPos]; for (i = 0; i < 16; i += 1) { - arr[i] = props[i]; + currentStack.transform[i] = props[i]; } - this.savedOp[this.cArrPos] = this.cO; this.cArrPos += 1; + var newStack = this.stack[this.cArrPos]; + newStack.opacity = currentStack.opacity; + newStack.fillStyle = currentStack.fillStyle; + newStack.strokeStyle = currentStack.strokeStyle; + newStack.lineWidth = currentStack.lineWidth; + newStack.lineCap = currentStack.lineCap; + newStack.lineJoin = currentStack.lineJoin; + newStack.miterLimit = currentStack.miterLimit; }; -CVContextData.prototype.getTransform = function () { - return this.cTr; +CVContextData.prototype.setOpacity = function (value) { + this.stack[this.cArrPos].opacity = value; }; -CVContextData.prototype.getOpacity = function () { - return this.cO; +CVContextData.prototype.setContext = function (value) { + this.nativeContext = value; }; -CVContextData.prototype.setOpacity = function (value) { - this.cO = value; +CVContextData.prototype.fillStyle = function (value) { + if (this.stack[this.cArrPos].fillStyle !== value) { + if (this.currentFillStyle !== value) { + this.currentFillStyle = value; + this.nativeContext.fillStyle = value; + } + this.stack[this.cArrPos].fillStyle = value; + } +}; + +CVContextData.prototype.strokeStyle = function (value) { + if (this.stack[this.cArrPos].strokeStyle !== value) { + if (this.currentStrokeStyle !== value) { + this.nativeContext.strokeStyle = value; + this.currentStrokeStyle = value; + } + this.stack[this.cArrPos].strokeStyle = value; + } +}; + +CVContextData.prototype.lineWidth = function (value) { + if (this.stack[this.cArrPos].lineWidth !== value) { + if (this.currentLineWidth !== value) { + this.nativeContext.lineWidth = value; + this.currentLineWidth = value; + } + this.stack[this.cArrPos].lineWidth = value; + } +}; + +CVContextData.prototype.lineCap = function (value) { + if (this.stack[this.cArrPos].lineCap !== value) { + if (this.currentLineCap !== value) { + this.nativeContext.lineCap = value; + this.currentLineCap = value; + } + this.stack[this.cArrPos].lineCap = value; + } +}; + +CVContextData.prototype.lineJoin = function (value) { + if (this.stack[this.cArrPos].lineJoin !== value) { + if (this.currentLineJoin !== value) { + this.nativeContext.lineJoin = value; + this.currentLineJoin = value; + } + this.stack[this.cArrPos].lineJoin = value; + } +}; + +CVContextData.prototype.miterLimit = function (value) { + if (this.stack[this.cArrPos].miterLimit !== value) { + if (this.currentMiterLimit !== value) { + this.nativeContext.miterLimit = value; + this.currentMiterLimit = value; + } + this.stack[this.cArrPos].miterLimit = value; + } +}; + +CVContextData.prototype.transform = function (props) { + this.transformMat.cloneFromProps(props); + // Taking the last transform value from the stored stack of transforms + var currentTransform = this.cTr; + // Applying the last transform value after the new transform to respect the order of transformations + this.transformMat.multiply(currentTransform); + // Storing the new transformed value in the stored transform + currentTransform.cloneFromProps(this.transformMat.props); + var trProps = currentTransform.props; + // Applying the new transform to the canvas + this.nativeContext.setTransform(trProps[0], trProps[1], trProps[4], trProps[5], trProps[12], trProps[13]); +}; + +CVContextData.prototype.opacity = function (op) { + var currentOpacity = this.stack[this.cArrPos].opacity; + currentOpacity *= op < 0 ? 0 : op; + if (this.stack[this.cArrPos].opacity !== currentOpacity) { + if (this.currentOpacity !== op) { + this.nativeContext.globalAlpha = op; + this.currentOpacity = op; + } + this.stack[this.cArrPos].opacity = currentOpacity; + } }; export default CVContextData; diff --git a/player/js/elements/canvasElements/CVShapeElement.js b/player/js/elements/canvasElements/CVShapeElement.js index f37cf51fa..62f39507d 100644 --- a/player/js/elements/canvasElements/CVShapeElement.js +++ b/player/js/elements/canvasElements/CVShapeElement.js @@ -295,13 +295,19 @@ CVShapeElement.prototype.drawLayer = function () { renderer.save(); elems = currentStyle.elements; if (type === 'st' || type === 'gs') { - ctx.strokeStyle = type === 'st' ? currentStyle.co : currentStyle.grd; - ctx.lineWidth = currentStyle.wi; - ctx.lineCap = currentStyle.lc; - ctx.lineJoin = currentStyle.lj; - ctx.miterLimit = currentStyle.ml || 0; + renderer.ctxStrokeStyle(type === 'st' ? currentStyle.co : currentStyle.grd); + // ctx.strokeStyle = type === 'st' ? currentStyle.co : currentStyle.grd; + renderer.ctxLineWidth(currentStyle.wi); + // ctx.lineWidth = currentStyle.wi; + renderer.ctxLineCap(currentStyle.lc); + // ctx.lineCap = currentStyle.lc; + renderer.ctxLineJoin(currentStyle.lj); + // ctx.lineJoin = currentStyle.lj; + renderer.ctxMiterLimit(currentStyle.ml || 0); + // ctx.miterLimit = currentStyle.ml || 0; } else { - ctx.fillStyle = type === 'fl' ? currentStyle.co : currentStyle.grd; + renderer.ctxFillStyle(type === 'fl' ? currentStyle.co : currentStyle.grd); + // ctx.fillStyle = type === 'fl' ? currentStyle.co : currentStyle.grd; } renderer.ctxOpacity(currentStyle.coOp); if (type !== 'st' && type !== 'gs') { diff --git a/player/js/elements/canvasElements/CVSolidElement.js b/player/js/elements/canvasElements/CVSolidElement.js index 61eadd805..e532e462b 100644 --- a/player/js/elements/canvasElements/CVSolidElement.js +++ b/player/js/elements/canvasElements/CVSolidElement.js @@ -20,7 +20,8 @@ CVSolidElement.prototype.prepareFrame = IImageElement.prototype.prepareFrame; CVSolidElement.prototype.renderInnerContent = function () { var ctx = this.canvasContext; - ctx.fillStyle = this.data.sc; + this.globalData.renderer.ctxFillStyle(this.data.sc); + // ctx.fillStyle = this.data.sc; ctx.fillRect(0, 0, this.data.sw, this.data.sh); // }; diff --git a/player/js/elements/canvasElements/CVTextElement.js b/player/js/elements/canvasElements/CVTextElement.js index b815e8421..662289b34 100644 --- a/player/js/elements/canvasElements/CVTextElement.js +++ b/player/js/elements/canvasElements/CVTextElement.js @@ -130,9 +130,12 @@ CVTextElement.prototype.renderInnerContent = function () { this.validateText(); var ctx = this.canvasContext; ctx.font = this.values.fValue; - ctx.lineCap = 'butt'; - ctx.lineJoin = 'miter'; - ctx.miterLimit = 4; + this.globalData.renderer.ctxLineCap('butt'); + // ctx.lineCap = 'butt'; + this.globalData.renderer.ctxLineJoin('miter'); + // ctx.lineJoin = 'miter'; + this.globalData.renderer.ctxMiterLimit(4); + // ctx.miterLimit = 4; if (!this.data.singleShape) { this.textAnimator.getMeasures(this.textProperty.currentData, this.lettersChangedFlag); @@ -155,23 +158,26 @@ CVTextElement.prototype.renderInnerContent = function () { var lastStrokeW = null; var commands; var pathArr; + var renderer = this.globalData.renderer; for (i = 0; i < len; i += 1) { if (!letters[i].n) { renderedLetter = renderedLetters[i]; if (renderedLetter) { - this.globalData.renderer.save(); - this.globalData.renderer.ctxTransform(renderedLetter.p); - this.globalData.renderer.ctxOpacity(renderedLetter.o); + renderer.save(); + renderer.ctxTransform(renderedLetter.p); + renderer.ctxOpacity(renderedLetter.o); } if (this.fill) { if (renderedLetter && renderedLetter.fc) { if (lastFill !== renderedLetter.fc) { + renderer.ctxFillStyle(renderedLetter.fc); lastFill = renderedLetter.fc; - ctx.fillStyle = renderedLetter.fc; + // ctx.fillStyle = renderedLetter.fc; } } else if (lastFill !== this.values.fill) { lastFill = this.values.fill; - ctx.fillStyle = this.values.fill; + renderer.ctxFillStyle(this.values.fill); + // ctx.fillStyle = this.values.fill; } commands = this.textSpans[i].elem; jLen = commands.length; @@ -192,20 +198,24 @@ CVTextElement.prototype.renderInnerContent = function () { if (renderedLetter && renderedLetter.sw) { if (lastStrokeW !== renderedLetter.sw) { lastStrokeW = renderedLetter.sw; - ctx.lineWidth = renderedLetter.sw; + renderer.ctxLineWidth(renderedLetter.sw); + // ctx.lineWidth = renderedLetter.sw; } } else if (lastStrokeW !== this.values.sWidth) { lastStrokeW = this.values.sWidth; - ctx.lineWidth = this.values.sWidth; + renderer.ctxLineWidth(this.values.sWidth); + // ctx.lineWidth = this.values.sWidth; } if (renderedLetter && renderedLetter.sc) { if (lastStroke !== renderedLetter.sc) { lastStroke = renderedLetter.sc; - ctx.strokeStyle = renderedLetter.sc; + renderer.ctxStrokeStyle(renderedLetter.sc); + // ctx.strokeStyle = renderedLetter.sc; } } else if (lastStroke !== this.values.stroke) { lastStroke = this.values.stroke; - ctx.strokeStyle = this.values.stroke; + renderer.ctxStrokeStyle(this.values.stroke); + // ctx.strokeStyle = this.values.stroke; } commands = this.textSpans[i].elem; jLen = commands.length; diff --git a/player/js/renderers/CanvasRendererBase.js b/player/js/renderers/CanvasRendererBase.js index 6c801ece6..47e918bd4 100644 --- a/player/js/renderers/CanvasRendererBase.js +++ b/player/js/renderers/CanvasRendererBase.js @@ -72,35 +72,66 @@ CanvasRendererBase.prototype.ctxTransform = function (props) { this.canvasContext.transform(props[0], props[1], props[4], props[5], props[12], props[13]); return; } - // Resetting the canvas transform matrix to the new transform - this.transformMat.cloneFromProps(props); - // Taking the last transform value from the stored stack of transforms - var currentTransform = this.contextData.getTransform(); - // Applying the last transform value after the new transform to respect the order of transformations - this.transformMat.multiply(currentTransform); - // Storing the new transformed value in the stored transform - currentTransform.cloneFromProps(this.transformMat.props); - var trProps = currentTransform.props; - // Applying the new transform to the canvas - this.canvasContext.setTransform(trProps[0], trProps[1], trProps[4], trProps[5], trProps[12], trProps[13]); + this.contextData.transform(props); }; CanvasRendererBase.prototype.ctxOpacity = function (op) { /* if(op === 1){ return; } */ - var currentOpacity = this.contextData.getOpacity(); if (!this.renderConfig.clearCanvas) { this.canvasContext.globalAlpha *= op < 0 ? 0 : op; - this.globalData.currentGlobalAlpha = currentOpacity; return; } - currentOpacity *= op < 0 ? 0 : op; - this.contextData.setOpacity(currentOpacity); - if (this.globalData.currentGlobalAlpha !== currentOpacity) { - this.canvasContext.globalAlpha = currentOpacity; - this.globalData.currentGlobalAlpha = currentOpacity; + this.contextData.opacity(op); +}; + +CanvasRendererBase.prototype.ctxFillStyle = function (value) { + if (!this.renderConfig.clearCanvas) { + this.canvasContext.fillStyle = value; + return; + } + this.contextData.fillStyle(value); +}; + +CanvasRendererBase.prototype.ctxStrokeStyle = function (value) { + if (!this.renderConfig.clearCanvas) { + this.canvasContext.strokeStyle = value; + return; + } + this.contextData.strokeStyle(value); +}; + +CanvasRendererBase.prototype.ctxLineWidth = function (value) { + if (!this.renderConfig.clearCanvas) { + this.canvasContext.lineWidth = value; + return; } + this.contextData.lineWidth(value); +}; + +CanvasRendererBase.prototype.ctxLineCap = function (value) { + if (!this.renderConfig.clearCanvas) { + this.canvasContext.lineCap = value; + return; + } + this.contextData.lineCap(value); +}; + +CanvasRendererBase.prototype.ctxLineJoin = function (value) { + if (!this.renderConfig.clearCanvas) { + this.canvasContext.lineJoin = value; + return; + } + this.contextData.lineJoin(value); +}; + +CanvasRendererBase.prototype.ctxMiterLimit = function (value) { + if (!this.renderConfig.clearCanvas) { + this.canvasContext.miterLimit = value; + return; + } + this.contextData.miterLimit(value); }; CanvasRendererBase.prototype.reset = function () { @@ -119,7 +150,7 @@ CanvasRendererBase.prototype.save = function (actionFlag) { if (actionFlag) { this.canvasContext.save(); } - this.contextData.push(); + this.contextData.save(); }; CanvasRendererBase.prototype.restore = function (actionFlag) { @@ -131,14 +162,7 @@ CanvasRendererBase.prototype.restore = function (actionFlag) { this.canvasContext.restore(); this.globalData.blendMode = 'source-over'; } - var popped = this.contextData.pop(); - var transform = popped.transform; - var opacity = popped.opacity; - this.canvasContext.setTransform(transform[0], transform[1], transform[4], transform[5], transform[12], transform[13]); - if (this.globalData.currentGlobalAlpha !== opacity) { - this.canvasContext.globalAlpha = opacity; - this.globalData.currentGlobalAlpha = opacity; - } + this.contextData.restore(actionFlag); }; CanvasRendererBase.prototype.configAnimation = function (animData) { @@ -164,6 +188,7 @@ CanvasRendererBase.prototype.configAnimation = function (animData) { } else { this.canvasContext = this.renderConfig.context; } + this.contextData.setContext(this.canvasContext); this.data = animData; this.layers = animData.layers; this.transformCanvas = { @@ -300,7 +325,7 @@ CanvasRendererBase.prototype.renderFrame = function (num, forceRender) { this.checkLayers(num); } - for (i = 0; i < len; i += 1) { + for (i = len - 1; i >= 0; i -= 1) { if (this.completeLayers || this.elements[i]) { this.elements[i].prepareFrame(num - this.layers[i].st); } From adc3194b1b6acfe88baa25d7716177b737f556ff Mon Sep 17 00:00:00 2001 From: airnan Date: Thu, 1 Jun 2023 01:10:04 -0400 Subject: [PATCH 2/8] apply styles only when drawing shapes --- .../elements/canvasElements/CVContextData.js | 121 +++++++++++------- .../elements/canvasElements/CVShapeElement.js | 6 +- .../elements/canvasElements/CVSolidElement.js | 5 +- .../elements/canvasElements/CVTextElement.js | 6 +- player/js/renderers/CanvasRendererBase.js | 24 ++++ 5 files changed, 108 insertions(+), 54 deletions(-) diff --git a/player/js/elements/canvasElements/CVContextData.js b/player/js/elements/canvasElements/CVContextData.js index f3c132220..8ade4e029 100644 --- a/player/js/elements/canvasElements/CVContextData.js +++ b/player/js/elements/canvasElements/CVContextData.js @@ -29,11 +29,23 @@ function CVContextData() { this.nativeContext = null; this.transformMat = new Matrix(); this.currentOpacity = 1; + // this.currentFillStyle = ''; + this.appliedFillStyle = ''; + // this.currentStrokeStyle = ''; + this.appliedStrokeStyle = ''; + // this.currentLineWidth = ''; + this.appliedLineWidth = ''; + // this.currentLineCap = ''; + this.appliedLineCap = ''; + // this.currentLineJoin = ''; + this.appliedLineJoin = ''; + // + this.appliedMiterLimit = ''; this.currentMiterLimit = ''; } @@ -61,35 +73,26 @@ CVContextData.prototype.restore = function (forceRestore) { for (i = 0; i < 16; i += 1) { arr[i] = transform[i]; } + if (forceRestore) { + var prevStack = this.stack[this.cArrPos + 1]; + this.appliedFillStyle = prevStack.fillStyle; + this.appliedStrokeStyle = prevStack.strokeStyle; + this.appliedLineWidth = prevStack.lineWidth; + this.appliedLineCap = prevStack.lineCap; + this.appliedLineJoin = prevStack.lineJoin; + this.appliedMiterLimit = prevStack.miterLimit; + } this.nativeContext.setTransform(transform[0], transform[1], transform[4], transform[5], transform[12], transform[13]); if (forceRestore || (currentContext.opacity !== -1 && this.currentOpacity !== currentContext.opacity)) { this.nativeContext.globalAlpha = currentContext.opacity; this.currentOpacity = currentContext.opacity; } - if (forceRestore || (currentContext.fillStyle && this.currentFillStyle !== currentContext.fillStyle)) { - this.nativeContext.fillStyle = currentContext.fillStyle; - this.currentFillStyle = currentContext.fillStyle; - } - if (forceRestore || (currentContext.strokeStyle && this.currentStrokeStyle !== currentContext.strokeStyle)) { - this.nativeContext.strokeStyle = currentContext.strokeStyle; - this.currentStrokeStyle = currentContext.strokeStyle; - } - if (forceRestore || (currentContext.lineWidth && this.currentLineWidth !== currentContext.lineWidth)) { - this.nativeContext.lineWidth = currentContext.lineWidth; - this.currentLineWidth = currentContext.lineWidth; - } - if (forceRestore || (currentContext.lineCap && this.currentLineCap !== currentContext.lineCap)) { - this.nativeContext.lineCap = currentContext.lineCap; - this.currentLineCap = currentContext.lineCap; - } - if (forceRestore || (currentContext.lineJoin && this.currentLineJoin !== currentContext.lineJoin)) { - this.nativeContext.lineJoin = currentContext.lineJoin; - this.currentLineJoin = currentContext.lineJoin; - } - if (currentContext.miterLimit && this.currentMiterLimit !== currentContext.miterLimit) { - this.nativeContext.miterLimit = currentContext.miterLimit; - this.currentMiterLimit = currentContext.miterLimit; - } + this.currentFillStyle = currentContext.fillStyle; + this.currentStrokeStyle = currentContext.strokeStyle; + this.currentLineWidth = currentContext.lineWidth; + this.currentLineCap = currentContext.lineCap; + this.currentLineJoin = currentContext.lineJoin; + this.currentMiterLimit = currentContext.miterLimit; }; CVContextData.prototype.save = function () { @@ -124,60 +127,42 @@ CVContextData.prototype.setContext = function (value) { CVContextData.prototype.fillStyle = function (value) { if (this.stack[this.cArrPos].fillStyle !== value) { - if (this.currentFillStyle !== value) { - this.currentFillStyle = value; - this.nativeContext.fillStyle = value; - } + this.currentFillStyle = value; this.stack[this.cArrPos].fillStyle = value; } }; CVContextData.prototype.strokeStyle = function (value) { if (this.stack[this.cArrPos].strokeStyle !== value) { - if (this.currentStrokeStyle !== value) { - this.nativeContext.strokeStyle = value; - this.currentStrokeStyle = value; - } + this.currentStrokeStyle = value; this.stack[this.cArrPos].strokeStyle = value; } }; CVContextData.prototype.lineWidth = function (value) { if (this.stack[this.cArrPos].lineWidth !== value) { - if (this.currentLineWidth !== value) { - this.nativeContext.lineWidth = value; - this.currentLineWidth = value; - } + this.currentLineWidth = value; this.stack[this.cArrPos].lineWidth = value; } }; CVContextData.prototype.lineCap = function (value) { if (this.stack[this.cArrPos].lineCap !== value) { - if (this.currentLineCap !== value) { - this.nativeContext.lineCap = value; - this.currentLineCap = value; - } + this.currentLineCap = value; this.stack[this.cArrPos].lineCap = value; } }; CVContextData.prototype.lineJoin = function (value) { if (this.stack[this.cArrPos].lineJoin !== value) { - if (this.currentLineJoin !== value) { - this.nativeContext.lineJoin = value; - this.currentLineJoin = value; - } + this.currentLineJoin = value; this.stack[this.cArrPos].lineJoin = value; } }; CVContextData.prototype.miterLimit = function (value) { if (this.stack[this.cArrPos].miterLimit !== value) { - if (this.currentMiterLimit !== value) { - this.nativeContext.miterLimit = value; - this.currentMiterLimit = value; - } + this.currentMiterLimit = value; this.stack[this.cArrPos].miterLimit = value; } }; @@ -207,4 +192,44 @@ CVContextData.prototype.opacity = function (op) { } }; +CVContextData.prototype.fill = function (rule) { + if (this.appliedFillStyle !== this.currentFillStyle) { + this.appliedFillStyle = this.currentFillStyle; + this.nativeContext.fillStyle = this.appliedFillStyle; + } + this.nativeContext.fill(rule); +}; + +CVContextData.prototype.fillRect = function (x, y, w, h) { + if (this.appliedFillStyle !== this.currentFillStyle) { + this.appliedFillStyle = this.currentFillStyle; + this.nativeContext.fillStyle = this.appliedFillStyle; + } + this.nativeContext.fillRect(x, y, w, h); +}; + +CVContextData.prototype.stroke = function () { + if (this.appliedStrokeStyle !== this.currentStrokeStyle) { + this.appliedStrokeStyle = this.currentStrokeStyle; + this.nativeContext.strokeStyle = this.appliedStrokeStyle; + } + if (this.appliedLineWidth !== this.currentLineWidth) { + this.appliedLineWidth = this.currentLineWidth; + this.nativeContext.lineWidth = this.appliedLineWidth; + } + if (this.appliedLineCap !== this.currentLineCap) { + this.appliedLineCap = this.currentLineCap; + this.nativeContext.lineCap = this.appliedLineCap; + } + if (this.appliedLineJoin !== this.currentLineJoin) { + this.appliedLineJoin = this.currentLineJoin; + this.nativeContext.lineJoin = this.appliedLineJoin; + } + if (this.appliedMiterLimit !== this.currentMiterLimit) { + this.appliedMiterLimit = this.currentMiterLimit; + this.nativeContext.miterLimit = this.appliedMiterLimit; + } + this.nativeContext.stroke(); +}; + export default CVContextData; diff --git a/player/js/elements/canvasElements/CVShapeElement.js b/player/js/elements/canvasElements/CVShapeElement.js index 62f39507d..3217bf5d4 100644 --- a/player/js/elements/canvasElements/CVShapeElement.js +++ b/player/js/elements/canvasElements/CVShapeElement.js @@ -336,14 +336,16 @@ CVShapeElement.prototype.drawLayer = function () { } } if (type === 'st' || type === 'gs') { - ctx.stroke(); + // ctx.stroke(); + renderer.ctxStroke(); if (currentStyle.da) { ctx.setLineDash(this.dashResetter); } } } if (type !== 'st' && type !== 'gs') { - ctx.fill(currentStyle.r); + // ctx.fill(currentStyle.r); + this.globalData.renderer.ctxFill(currentStyle.r); } renderer.restore(); } diff --git a/player/js/elements/canvasElements/CVSolidElement.js b/player/js/elements/canvasElements/CVSolidElement.js index e532e462b..23e0d983a 100644 --- a/player/js/elements/canvasElements/CVSolidElement.js +++ b/player/js/elements/canvasElements/CVSolidElement.js @@ -19,10 +19,11 @@ CVSolidElement.prototype.initElement = SVGShapeElement.prototype.initElement; CVSolidElement.prototype.prepareFrame = IImageElement.prototype.prepareFrame; CVSolidElement.prototype.renderInnerContent = function () { - var ctx = this.canvasContext; + // var ctx = this.canvasContext; this.globalData.renderer.ctxFillStyle(this.data.sc); // ctx.fillStyle = this.data.sc; - ctx.fillRect(0, 0, this.data.sw, this.data.sh); + this.globalData.renderer.ctxFillRect(0, 0, this.data.sw, this.data.sh); + // ctx.fillRect(0, 0, this.data.sw, this.data.sh); // }; diff --git a/player/js/elements/canvasElements/CVTextElement.js b/player/js/elements/canvasElements/CVTextElement.js index 662289b34..52ef05e37 100644 --- a/player/js/elements/canvasElements/CVTextElement.js +++ b/player/js/elements/canvasElements/CVTextElement.js @@ -191,7 +191,8 @@ CVTextElement.prototype.renderInnerContent = function () { } } this.globalData.canvasContext.closePath(); - this.globalData.canvasContext.fill(); + renderer.ctxFill(); + // this.globalData.canvasContext.fill(); /// ctx.fillText(this.textSpans[i].val,0,0); } if (this.stroke) { @@ -229,7 +230,8 @@ CVTextElement.prototype.renderInnerContent = function () { } } this.globalData.canvasContext.closePath(); - this.globalData.canvasContext.stroke(); + renderer.ctxStroke(); + // this.globalData.canvasContext.stroke(); /// ctx.strokeText(letters[i].val,0,0); } if (renderedLetter) { diff --git a/player/js/renderers/CanvasRendererBase.js b/player/js/renderers/CanvasRendererBase.js index 47e918bd4..0b2f99cac 100644 --- a/player/js/renderers/CanvasRendererBase.js +++ b/player/js/renderers/CanvasRendererBase.js @@ -134,6 +134,30 @@ CanvasRendererBase.prototype.ctxMiterLimit = function (value) { this.contextData.miterLimit(value); }; +CanvasRendererBase.prototype.ctxFill = function (rule) { + if (!this.renderConfig.clearCanvas) { + this.canvasContext.fill(rule); + return; + } + this.contextData.fill(rule); +}; + +CanvasRendererBase.prototype.ctxFillRect = function (x, y, w, h) { + if (!this.renderConfig.clearCanvas) { + this.canvasContext.fillRect(x, y, w, h); + return; + } + this.contextData.fillRect(x, y, w, h); +}; + +CanvasRendererBase.prototype.ctxStroke = function () { + if (!this.renderConfig.clearCanvas) { + this.canvasContext.stroke(); + return; + } + this.contextData.stroke(); +}; + CanvasRendererBase.prototype.reset = function () { if (!this.renderConfig.clearCanvas) { this.canvasContext.restore(); From dc5bed7c2cad013a36c78460dc9a0fece438d20a Mon Sep 17 00:00:00 2001 From: airnan Date: Thu, 1 Jun 2023 01:45:35 -0400 Subject: [PATCH 3/8] copy context methods to renderer --- .../elements/canvasElements/CVContextData.js | 6 +- player/js/renderers/CanvasRenderer.js | 14 +++ player/js/renderers/CanvasRendererBase.js | 113 +++--------------- 3 files changed, 33 insertions(+), 100 deletions(-) diff --git a/player/js/elements/canvasElements/CVContextData.js b/player/js/elements/canvasElements/CVContextData.js index 8ade4e029..88df6fac9 100644 --- a/player/js/elements/canvasElements/CVContextData.js +++ b/player/js/elements/canvasElements/CVContextData.js @@ -74,6 +74,7 @@ CVContextData.prototype.restore = function (forceRestore) { arr[i] = transform[i]; } if (forceRestore) { + this.nativeContext.restore(); var prevStack = this.stack[this.cArrPos + 1]; this.appliedFillStyle = prevStack.fillStyle; this.appliedStrokeStyle = prevStack.strokeStyle; @@ -95,7 +96,10 @@ CVContextData.prototype.restore = function (forceRestore) { this.currentMiterLimit = currentContext.miterLimit; }; -CVContextData.prototype.save = function () { +CVContextData.prototype.save = function (saveOnNativeFlag) { + if (saveOnNativeFlag) { + this.nativeContext.save(); + } var props = this.cTr.props; if (this._length <= this.cArrPos) { this.duplicate(); diff --git a/player/js/renderers/CanvasRenderer.js b/player/js/renderers/CanvasRenderer.js index 231d06f06..0a7c98829 100644 --- a/player/js/renderers/CanvasRenderer.js +++ b/player/js/renderers/CanvasRenderer.js @@ -36,6 +36,20 @@ function CanvasRenderer(animationItem, config) { this.transformMat = new Matrix(); this.completeLayers = false; this.rendererType = 'canvas'; + if (this.renderConfig.clearCanvas) { + this.ctxTransform = this.contextData.transform.bind(this.contextData); + this.ctxOpacity = this.contextData.opacity.bind(this.contextData); + this.ctxFillStyle = this.contextData.fillStyle.bind(this.contextData); + this.ctxStrokeStyle = this.contextData.strokeStyle.bind(this.contextData); + this.ctxLineWidth = this.contextData.lineWidth.bind(this.contextData); + this.ctxLineCap = this.contextData.lineCap.bind(this.contextData); + this.ctxLineJoin = this.contextData.lineJoin.bind(this.contextData); + this.ctxMiterLimit = this.contextData.miterLimit.bind(this.contextData); + this.ctxFill = this.contextData.fill.bind(this.contextData); + this.ctxFillRect = this.contextData.fillRect.bind(this.contextData); + this.ctxStroke = this.contextData.stroke.bind(this.contextData); + this.save = this.contextData.save.bind(this.contextData); + } } extendPrototype([CanvasRendererBase], CanvasRenderer); diff --git a/player/js/renderers/CanvasRendererBase.js b/player/js/renderers/CanvasRendererBase.js index 0b2f99cac..dd199f511 100644 --- a/player/js/renderers/CanvasRendererBase.js +++ b/player/js/renderers/CanvasRendererBase.js @@ -6,43 +6,13 @@ import { } from '../utils/helpers/arrays'; import createTag from '../utils/helpers/html_elements'; import SVGRenderer from './SVGRenderer'; -import Matrix from '../3rd_party/transformation-matrix'; import BaseRenderer from './BaseRenderer'; -import CVContextData from '../elements/canvasElements/CVContextData'; import CVShapeElement from '../elements/canvasElements/CVShapeElement'; import CVTextElement from '../elements/canvasElements/CVTextElement'; import CVImageElement from '../elements/canvasElements/CVImageElement'; import CVSolidElement from '../elements/canvasElements/CVSolidElement'; -function CanvasRendererBase(animationItem, config) { - this.animationItem = animationItem; - this.renderConfig = { - clearCanvas: (config && config.clearCanvas !== undefined) ? config.clearCanvas : true, - context: (config && config.context) || null, - progressiveLoad: (config && config.progressiveLoad) || false, - preserveAspectRatio: (config && config.preserveAspectRatio) || 'xMidYMid meet', - imagePreserveAspectRatio: (config && config.imagePreserveAspectRatio) || 'xMidYMid slice', - contentVisibility: (config && config.contentVisibility) || 'visible', - className: (config && config.className) || '', - id: (config && config.id) || '', - }; - this.renderConfig.dpr = (config && config.dpr) || 1; - if (this.animationItem.wrapper) { - this.renderConfig.dpr = (config && config.dpr) || window.devicePixelRatio || 1; - } - this.renderedFrame = -1; - this.globalData = { - frameNum: -1, - _mdf: false, - renderConfig: this.renderConfig, - currentGlobalAlpha: -1, - }; - this.contextData = new CVContextData(); - this.elements = []; - this.pendingElements = []; - this.transformMat = new Matrix(); - this.completeLayers = false; - this.rendererType = 'canvas'; +function CanvasRendererBase() { } extendPrototype([BaseRenderer], CanvasRendererBase); @@ -68,94 +38,47 @@ CanvasRendererBase.prototype.ctxTransform = function (props) { if (props[0] === 1 && props[1] === 0 && props[4] === 0 && props[5] === 1 && props[12] === 0 && props[13] === 0) { return; } - if (!this.renderConfig.clearCanvas) { - this.canvasContext.transform(props[0], props[1], props[4], props[5], props[12], props[13]); - return; - } - this.contextData.transform(props); + this.canvasContext.transform(props[0], props[1], props[4], props[5], props[12], props[13]); }; CanvasRendererBase.prototype.ctxOpacity = function (op) { - /* if(op === 1){ - return; - } */ - if (!this.renderConfig.clearCanvas) { - this.canvasContext.globalAlpha *= op < 0 ? 0 : op; - return; - } - this.contextData.opacity(op); + this.canvasContext.globalAlpha *= op < 0 ? 0 : op; }; CanvasRendererBase.prototype.ctxFillStyle = function (value) { - if (!this.renderConfig.clearCanvas) { - this.canvasContext.fillStyle = value; - return; - } - this.contextData.fillStyle(value); + this.canvasContext.fillStyle = value; }; CanvasRendererBase.prototype.ctxStrokeStyle = function (value) { - if (!this.renderConfig.clearCanvas) { - this.canvasContext.strokeStyle = value; - return; - } - this.contextData.strokeStyle(value); + this.canvasContext.strokeStyle = value; }; CanvasRendererBase.prototype.ctxLineWidth = function (value) { - if (!this.renderConfig.clearCanvas) { - this.canvasContext.lineWidth = value; - return; - } - this.contextData.lineWidth(value); + this.canvasContext.lineWidth = value; }; CanvasRendererBase.prototype.ctxLineCap = function (value) { - if (!this.renderConfig.clearCanvas) { - this.canvasContext.lineCap = value; - return; - } - this.contextData.lineCap(value); + this.canvasContext.lineCap = value; }; CanvasRendererBase.prototype.ctxLineJoin = function (value) { - if (!this.renderConfig.clearCanvas) { - this.canvasContext.lineJoin = value; - return; - } - this.contextData.lineJoin(value); + this.canvasContext.lineJoin = value; }; CanvasRendererBase.prototype.ctxMiterLimit = function (value) { - if (!this.renderConfig.clearCanvas) { - this.canvasContext.miterLimit = value; - return; - } - this.contextData.miterLimit(value); + this.canvasContext.miterLimit = value; }; CanvasRendererBase.prototype.ctxFill = function (rule) { - if (!this.renderConfig.clearCanvas) { - this.canvasContext.fill(rule); - return; - } - this.contextData.fill(rule); + this.canvasContext.fill(rule); }; CanvasRendererBase.prototype.ctxFillRect = function (x, y, w, h) { - if (!this.renderConfig.clearCanvas) { - this.canvasContext.fillRect(x, y, w, h); - return; - } - this.contextData.fillRect(x, y, w, h); + this.canvasContext.fillRect(x, y, w, h); }; CanvasRendererBase.prototype.ctxStroke = function () { - if (!this.renderConfig.clearCanvas) { - this.canvasContext.stroke(); - return; - } - this.contextData.stroke(); + this.canvasContext.stroke(); }; CanvasRendererBase.prototype.reset = function () { @@ -166,15 +89,8 @@ CanvasRendererBase.prototype.reset = function () { this.contextData.reset(); }; -CanvasRendererBase.prototype.save = function (actionFlag) { - if (!this.renderConfig.clearCanvas) { - this.canvasContext.save(); - return; - } - if (actionFlag) { - this.canvasContext.save(); - } - this.contextData.save(); +CanvasRendererBase.prototype.save = function () { + this.canvasContext.save(); }; CanvasRendererBase.prototype.restore = function (actionFlag) { @@ -183,7 +99,6 @@ CanvasRendererBase.prototype.restore = function (actionFlag) { return; } if (actionFlag) { - this.canvasContext.restore(); this.globalData.blendMode = 'source-over'; } this.contextData.restore(actionFlag); From 2e5504d8b96f2d62a4683d9231f8f6e4a0d32885 Mon Sep 17 00:00:00 2001 From: airnan Date: Sat, 3 Jun 2023 19:31:26 -0400 Subject: [PATCH 4/8] test --- test/index.js | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/test/index.js b/test/index.js index dab4308cf..d0dc4cf25 100644 --- a/test/index.js +++ b/test/index.js @@ -28,6 +28,14 @@ function createDirectoryPath(path) { } const animations = [ + { + fileName: 'pigeon.json', + renderer: 'svg', + }, + { + fileName: 'banner.json', + renderer: 'svg', + }, { fileName: 'adrock.json', renderer: 'canvas', @@ -48,14 +56,6 @@ const animations = [ fileName: 'dalek.json', renderer: 'svg', }, - { - fileName: 'pigeon.json', - renderer: 'svg', - }, - { - fileName: 'banner.json', - renderer: 'svg', - }, { fileName: 'navidad.json', renderer: 'svg', @@ -229,6 +229,7 @@ const compareFiles = (folderName, fileName) => { const result = pixelmatch(img1.data, img2.data, diff.data, width, height, {threshold: 0}); if (result !== 0) { + console.log('RESULT NOT ZERO: ', result); throw new Error(`Animaiton failed: ${folderName} at frame: ${fileName}`) } } From 69a93259498cbec4c6e82bf1f466f7175ba6159a Mon Sep 17 00:00:00 2001 From: airnan Date: Sat, 3 Jun 2023 19:52:45 -0400 Subject: [PATCH 5/8] logging failed frame --- test/index.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/index.js b/test/index.js index 986bc43aa..9c132b5a0 100644 --- a/test/index.js +++ b/test/index.js @@ -259,7 +259,12 @@ const createIndividualAssets = async (page, folderName, settings) => { fullPage: false, }); if (settings.step === 'compare') { + try { compareFiles(folderName, fileName); + } catch (err) { + console.log('FAILED AT FRAME: ', message.currentFrame); + throw err; + } } isLastFrame = message.isLast; } From 31a9b54cbdc4cac4833af86b44a69a838796dbc0 Mon Sep 17 00:00:00 2001 From: airnan Date: Sat, 3 Jun 2023 20:23:20 -0400 Subject: [PATCH 6/8] updated threshold --- test/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/index.js b/test/index.js index 9c132b5a0..11d68d305 100644 --- a/test/index.js +++ b/test/index.js @@ -227,7 +227,7 @@ const compareFiles = (folderName, fileName) => { const {width, height} = img1; const diff = new PNG({width, height}); - const result = pixelmatch(img1.data, img2.data, diff.data, width, height, {threshold: 0}); + const result = pixelmatch(img1.data, img2.data, diff.data, width, height, {threshold: 0.1}); if (result !== 0) { console.log('RESULT NOT ZERO: ', result); throw new Error(`Animation failed: ${folderName} at frame: ${fileName}`) From a9f916ea4aeebb8fc800a9479079c16f9bffed95 Mon Sep 17 00:00:00 2001 From: airnan Date: Sat, 3 Jun 2023 22:22:17 -0400 Subject: [PATCH 7/8] adding threshold to comparison --- test/index.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/index.js b/test/index.js index 11d68d305..940d0285a 100644 --- a/test/index.js +++ b/test/index.js @@ -228,7 +228,9 @@ const compareFiles = (folderName, fileName) => { const diff = new PNG({width, height}); const result = pixelmatch(img1.data, img2.data, diff.data, width, height, {threshold: 0.1}); - if (result !== 0) { + // Using 50 as threshold because it should be an acceptable difference + // that doesn't raise false positives + if (result > 50) { console.log('RESULT NOT ZERO: ', result); throw new Error(`Animation failed: ${folderName} at frame: ${fileName}`) } From 51c2ce7e18e9d9d176826f7afb900c777a4ba758 Mon Sep 17 00:00:00 2001 From: airnan Date: Thu, 8 Jun 2023 07:29:19 -0400 Subject: [PATCH 8/8] change test threshold --- test/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/index.js b/test/index.js index 940d0285a..3c2263e7b 100644 --- a/test/index.js +++ b/test/index.js @@ -230,7 +230,7 @@ const compareFiles = (folderName, fileName) => { const result = pixelmatch(img1.data, img2.data, diff.data, width, height, {threshold: 0.1}); // Using 50 as threshold because it should be an acceptable difference // that doesn't raise false positives - if (result > 50) { + if (result > 200) { console.log('RESULT NOT ZERO: ', result); throw new Error(`Animation failed: ${folderName} at frame: ${fileName}`) }