Skip to content

Commit

Permalink
Merge pull request #2985 from airbnb/91_canvas_paint_operations_optim…
Browse files Browse the repository at this point in the history
…ization

91 canvas paint operations optimization
  • Loading branch information
bodymovin authored Jun 3, 2023
2 parents 3403e17 + c1b5232 commit b8c66ba
Show file tree
Hide file tree
Showing 6 changed files with 293 additions and 144 deletions.
227 changes: 188 additions & 39 deletions player/js/elements/canvasElements/CVContextData.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,88 +3,237 @@ 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.appliedFillStyle = '';
//
this.currentStrokeStyle = '';
this.appliedStrokeStyle = '';
//
this.currentLineWidth = '';
this.appliedLineWidth = '';
//
this.currentLineCap = '';
this.appliedLineCap = '';
//
this.currentLineJoin = '';
this.appliedLineJoin = '';
//
this.appliedMiterLimit = '';
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;
};

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];
}
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,
};
if (forceRestore) {
this.nativeContext.restore();
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;
}
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.push = function () {
CVContextData.prototype.save = function (saveOnNativeFlag) {
if (saveOnNativeFlag) {
this.nativeContext.save();
}
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) {
this.currentFillStyle = value;
this.stack[this.cArrPos].fillStyle = value;
}
};

CVContextData.prototype.strokeStyle = function (value) {
if (this.stack[this.cArrPos].strokeStyle !== value) {
this.currentStrokeStyle = value;
this.stack[this.cArrPos].strokeStyle = value;
}
};

CVContextData.prototype.lineWidth = function (value) {
if (this.stack[this.cArrPos].lineWidth !== value) {
this.currentLineWidth = value;
this.stack[this.cArrPos].lineWidth = value;
}
};

CVContextData.prototype.lineCap = function (value) {
if (this.stack[this.cArrPos].lineCap !== value) {
this.currentLineCap = value;
this.stack[this.cArrPos].lineCap = value;
}
};

CVContextData.prototype.lineJoin = function (value) {
if (this.stack[this.cArrPos].lineJoin !== value) {
this.currentLineJoin = value;
this.stack[this.cArrPos].lineJoin = value;
}
};

CVContextData.prototype.miterLimit = function (value) {
if (this.stack[this.cArrPos].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;
}
};

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;
24 changes: 16 additions & 8 deletions player/js/elements/canvasElements/CVShapeElement.js
Original file line number Diff line number Diff line change
Expand Up @@ -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') {
Expand Down Expand Up @@ -330,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();
}
Expand Down
8 changes: 5 additions & 3 deletions player/js/elements/canvasElements/CVSolidElement.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,11 @@ CVSolidElement.prototype.initElement = SVGShapeElement.prototype.initElement;
CVSolidElement.prototype.prepareFrame = IImageElement.prototype.prepareFrame;

CVSolidElement.prototype.renderInnerContent = function () {
var ctx = this.canvasContext;
ctx.fillStyle = this.data.sc;
ctx.fillRect(0, 0, this.data.sw, this.data.sh);
// var ctx = this.canvasContext;
this.globalData.renderer.ctxFillStyle(this.data.sc);
// ctx.fillStyle = this.data.sc;
this.globalData.renderer.ctxFillRect(0, 0, this.data.sw, this.data.sh);
// ctx.fillRect(0, 0, this.data.sw, this.data.sh);
//
};

Expand Down
Loading

0 comments on commit b8c66ba

Please sign in to comment.