123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170 |
- import assetManager from '../../utils/helpers/assetManager';
- import getBlendMode from '../../utils/helpers/blendModes';
- import Matrix from '../../3rd_party/transformation-matrix';
- import CVEffects from './CVEffects';
- import CVMaskElement from './CVMaskElement';
- import effectTypes from '../../utils/helpers/effectTypes';
- function CVBaseElement() {
- }
- var operationsMap = {
- 1: 'source-in',
- 2: 'source-out',
- 3: 'source-in',
- 4: 'source-out',
- };
- CVBaseElement.prototype = {
- createElements: function () {},
- initRendererElement: function () {},
- createContainerElements: function () {
- // If the layer is masked we will use two buffers to store each different states of the drawing
- // This solution is not ideal for several reason. But unfortunately, because of the recursive
- // nature of the render tree, it's the only simple way to make sure one inner mask doesn't override an outer mask.
- // TODO: try to reduce the size of these buffers to the size of the composition contaning the layer
- // It might be challenging because the layer most likely is transformed in some way
- if (this.data.tt >= 1) {
- this.buffers = [];
- var canvasContext = this.globalData.canvasContext;
- var bufferCanvas = assetManager.createCanvas(canvasContext.canvas.width, canvasContext.canvas.height);
- this.buffers.push(bufferCanvas);
- var bufferCanvas2 = assetManager.createCanvas(canvasContext.canvas.width, canvasContext.canvas.height);
- this.buffers.push(bufferCanvas2);
- if (this.data.tt >= 3 && !document._isProxy) {
- assetManager.loadLumaCanvas();
- }
- }
- this.canvasContext = this.globalData.canvasContext;
- this.transformCanvas = this.globalData.transformCanvas;
- this.renderableEffectsManager = new CVEffects(this);
- this.searchEffectTransforms();
- },
- createContent: function () {},
- setBlendMode: function () {
- var globalData = this.globalData;
- if (globalData.blendMode !== this.data.bm) {
- globalData.blendMode = this.data.bm;
- var blendModeValue = getBlendMode(this.data.bm);
- globalData.canvasContext.globalCompositeOperation = blendModeValue;
- }
- },
- createRenderableComponents: function () {
- this.maskManager = new CVMaskElement(this.data, this);
- this.transformEffects = this.renderableEffectsManager.getEffects(effectTypes.TRANSFORM_EFFECT);
- },
- hideElement: function () {
- if (!this.hidden && (!this.isInRange || this.isTransparent)) {
- this.hidden = true;
- }
- },
- showElement: function () {
- if (this.isInRange && !this.isTransparent) {
- this.hidden = false;
- this._isFirstFrame = true;
- this.maskManager._isFirstFrame = true;
- }
- },
- clearCanvas: function (canvasContext) {
- canvasContext.clearRect(
- this.transformCanvas.tx,
- this.transformCanvas.ty,
- this.transformCanvas.w * this.transformCanvas.sx,
- this.transformCanvas.h * this.transformCanvas.sy
- );
- },
- prepareLayer: function () {
- if (this.data.tt >= 1) {
- var buffer = this.buffers[0];
- var bufferCtx = buffer.getContext('2d');
- this.clearCanvas(bufferCtx);
- // on the first buffer we store the current state of the global drawing
- bufferCtx.drawImage(this.canvasContext.canvas, 0, 0);
- // The next four lines are to clear the canvas
- // TODO: Check if there is a way to clear the canvas without resetting the transform
- this.currentTransform = this.canvasContext.getTransform();
- this.canvasContext.setTransform(1, 0, 0, 1, 0, 0);
- this.clearCanvas(this.canvasContext);
- this.canvasContext.setTransform(this.currentTransform);
- }
- },
- exitLayer: function () {
- if (this.data.tt >= 1) {
- var buffer = this.buffers[1];
- // On the second buffer we store the current state of the global drawing
- // that only contains the content of this layer
- // (if it is a composition, it also includes the nested layers)
- var bufferCtx = buffer.getContext('2d');
- this.clearCanvas(bufferCtx);
- bufferCtx.drawImage(this.canvasContext.canvas, 0, 0);
- // We clear the canvas again
- this.canvasContext.setTransform(1, 0, 0, 1, 0, 0);
- this.clearCanvas(this.canvasContext);
- this.canvasContext.setTransform(this.currentTransform);
- // We draw the mask
- const mask = this.comp.getElementById('tp' in this.data ? this.data.tp : this.data.ind - 1);
- mask.renderFrame(true);
- // We draw the second buffer (that contains the content of this layer)
- this.canvasContext.setTransform(1, 0, 0, 1, 0, 0);
- // If the mask is a Luma matte, we need to do two extra painting operations
- // the _isProxy check is to avoid drawing a fake canvas in workers that will throw an error
- if (this.data.tt >= 3 && !document._isProxy) {
- // We copy the painted mask to a buffer that has a color matrix filter applied to it
- // that applies the rgb values to the alpha channel
- var lumaBuffer = assetManager.getLumaCanvas(this.canvasContext.canvas);
- var lumaBufferCtx = lumaBuffer.getContext('2d');
- lumaBufferCtx.drawImage(this.canvasContext.canvas, 0, 0);
- this.clearCanvas(this.canvasContext);
- // we repaint the context with the mask applied to it
- this.canvasContext.drawImage(lumaBuffer, 0, 0);
- }
- this.canvasContext.globalCompositeOperation = operationsMap[this.data.tt];
- this.canvasContext.drawImage(buffer, 0, 0);
- // We finally draw the first buffer (that contains the content of the global drawing)
- // We use destination-over to draw the global drawing below the current layer
- this.canvasContext.globalCompositeOperation = 'destination-over';
- this.canvasContext.drawImage(this.buffers[0], 0, 0);
- this.canvasContext.setTransform(this.currentTransform);
- // We reset the globalCompositeOperation to source-over, the standard type of operation
- this.canvasContext.globalCompositeOperation = 'source-over';
- }
- },
- renderFrame: function (forceRender) {
- if (this.hidden || this.data.hd) {
- return;
- }
- if (this.data.td === 1 && !forceRender) {
- return;
- }
- this.renderTransform();
- this.renderRenderable();
- this.renderLocalTransform();
- this.setBlendMode();
- var forceRealStack = this.data.ty === 0;
- this.prepareLayer();
- this.globalData.renderer.save(forceRealStack);
- this.globalData.renderer.ctxTransform(this.finalTransform.localMat.props);
- this.globalData.renderer.ctxOpacity(this.finalTransform.localOpacity);
- this.renderInnerContent();
- this.globalData.renderer.restore(forceRealStack);
- this.exitLayer();
- if (this.maskManager.hasMasks) {
- this.globalData.renderer.restore(true);
- }
- if (this._isFirstFrame) {
- this._isFirstFrame = false;
- }
- },
- destroy: function () {
- this.canvasContext = null;
- this.data = null;
- this.globalData = null;
- this.maskManager.destroy();
- },
- mHelper: new Matrix(),
- };
- CVBaseElement.prototype.hide = CVBaseElement.prototype.hideElement;
- CVBaseElement.prototype.show = CVBaseElement.prototype.showElement;
- export default CVBaseElement;
|