Browse Source

refactoring

stateless
Mikael Finstad 6 years ago
parent
commit
7a13d7599f
  1. 2
      index.js
  2. 87
      sources/fabric.js
  3. 88
      sources/fabric/fabricFrameSources.js
  4. 43
      sources/frameSource.js

2
index.js

@ -7,7 +7,7 @@ const JSON5 = require('json5');
const fs = require('fs-extra'); const fs = require('fs-extra');
const { parseFps, readVideoFileInfo, readAudioFileInfo, multipleOf2 } = require('./util'); const { parseFps, readVideoFileInfo, readAudioFileInfo, multipleOf2 } = require('./util');
const { registerFont } = require('./sources/fabricFrameSource');
const { registerFont } = require('./sources/fabric');
const { createFrameSource } = require('./sources/frameSource'); const { createFrameSource } = require('./sources/frameSource');
const { calcTransition } = require('./transitions'); const { calcTransition } = require('./transitions');

87
sources/fabric.js

@ -0,0 +1,87 @@
const { fabric } = require('fabric');
const nodeCanvas = require('canvas');
const { canvasToRgba } = require('./shared');
// Fabric is used as a fundament for compositing layers in editly
function fabricCanvasToRgba(canvas) {
// https://github.com/fabricjs/fabric.js/blob/26e1a5b55cbeeffb59845337ced3f3f91d533d7d/src/static_canvas.class.js
// https://github.com/fabricjs/fabric.js/issues/3885
const internalCanvas = fabric.util.getNodeCanvas(canvas.lowerCanvasEl);
const ctx = internalCanvas.getContext('2d');
// require('fs').writeFileSync(`${Math.floor(Math.random() * 1e12)}.png`, internalCanvas.toBuffer('image/png'));
// throw new Error('abort');
return canvasToRgba(ctx);
}
function createFabricCanvas({ width, height }) {
return new fabric.StaticCanvas(null, { width, height });
}
async function renderFabricCanvas(canvas) {
canvas.renderAll();
const rgba = fabricCanvasToRgba(canvas);
canvas.clear();
canvas.dispose();
return rgba;
}
async function rgbaToFabricImage({ width, height, rgba }) {
const canvas = nodeCanvas.createCanvas(width, height);
const ctx = canvas.getContext('2d');
// https://developer.mozilla.org/en-US/docs/Web/API/ImageData/ImageData
// https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/putImageData
ctx.putImageData(new nodeCanvas.ImageData(Uint8ClampedArray.from(rgba), width, height), 0, 0);
// https://stackoverflow.com/questions/58209996/unable-to-render-tiff-images-and-add-it-as-a-fabric-object
return new fabric.Image(canvas);
}
async function createFabricFrameSource(func, { width, height, ...rest }) {
const onInit = async () => func(({ width, height, fabric, ...rest }));
const { onRender = () => {}, onClose = () => {} } = await onInit() || {};
return {
readNextFrame: onRender,
close: onClose,
};
}
async function createCustomCanvasFrameSource({ width, height, params }) {
const canvas = nodeCanvas.createCanvas(width, height);
const context = canvas.getContext('2d');
const { onClose, onRender } = await params.func(({ width, height, canvas }));
async function readNextFrame(progress) {
context.clearRect(0, 0, canvas.width, canvas.height);
await onRender(progress);
// require('fs').writeFileSync(`${new Date().getTime()}.png`, canvas.toBuffer('image/png'));
// I don't know any way to draw a node-canvas as a layer on a fabric.js canvas, other than converting to rgba first:
return canvasToRgba(context);
}
return {
readNextFrame,
// Node canvas needs no cleanup https://github.com/Automattic/node-canvas/issues/1216#issuecomment-412390668
close: onClose,
};
}
function registerFont(...args) {
fabric.nodeCanvas.registerFont(...args);
}
module.exports = {
registerFont,
createFabricFrameSource,
createCustomCanvasFrameSource,
createFabricCanvas,
renderFabricCanvas,
rgbaToFabricImage,
};

88
sources/fabricFrameSource.js → sources/fabric/fabricFrameSources.js

@ -1,62 +1,13 @@
const { fabric } = require('fabric'); const { fabric } = require('fabric');
const fileUrl = require('file-url'); const fileUrl = require('file-url');
const nodeCanvas = require('canvas');
const { createCanvas } = nodeCanvas;
const { canvasToRgba } = require('./shared');
const { getRandomGradient, getRandomColors } = require('../colors');
const { easeOutExpo } = require('../transitions');
const { getPositionProps } = require('../util');
const { getRandomGradient, getRandomColors } = require('../../colors');
const { easeOutExpo } = require('../../transitions');
const { getPositionProps } = require('../../util');
// http://fabricjs.com/kitchensink // http://fabricjs.com/kitchensink
function fabricCanvasToRgba(canvas) {
// https://github.com/fabricjs/fabric.js/blob/26e1a5b55cbeeffb59845337ced3f3f91d533d7d/src/static_canvas.class.js
// https://github.com/fabricjs/fabric.js/issues/3885
const internalCanvas = fabric.util.getNodeCanvas(canvas.lowerCanvasEl);
const ctx = internalCanvas.getContext('2d');
// require('fs').writeFileSync(`${Math.floor(Math.random() * 1e12)}.png`, internalCanvas.toBuffer('image/png'));
// throw new Error('abort');
return canvasToRgba(ctx);
}
function createFabricCanvas({ width, height }) {
return new fabric.StaticCanvas(null, { width, height });
}
async function renderFabricCanvas(canvas) {
canvas.renderAll();
const rgba = fabricCanvasToRgba(canvas);
canvas.clear();
canvas.dispose();
return rgba;
}
async function rgbaToFabricImage({ width, height, rgba }) {
const canvas = createCanvas(width, height);
const ctx = canvas.getContext('2d');
// https://developer.mozilla.org/en-US/docs/Web/API/ImageData/ImageData
// https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/putImageData
ctx.putImageData(new nodeCanvas.ImageData(Uint8ClampedArray.from(rgba), width, height), 0, 0);
// https://stackoverflow.com/questions/58209996/unable-to-render-tiff-images-and-add-it-as-a-fabric-object
return new fabric.Image(canvas);
}
async function createFabricFrameSource(func, { width, height, ...rest }) {
const onInit = async () => func(({ width, height, fabric, ...rest }));
const { onRender = () => {}, onClose = () => {} } = await onInit() || {};
return {
readNextFrame: onRender,
close: onClose,
};
}
const loadImage = async (path) => new Promise((resolve) => fabric.util.loadImage(fileUrl(path), resolve)); const loadImage = async (path) => new Promise((resolve) => fabric.util.loadImage(fileUrl(path), resolve));
function getZoomParams({ progress, zoomDirection, zoomAmount }) { function getZoomParams({ progress, zoomDirection, zoomAmount }) {
@ -357,40 +308,11 @@ async function newsTitleFrameSource({ width, height, params }) {
return { onRender }; return { onRender };
} }
async function createCustomCanvasFrameSource({ width, height, params }) {
const canvas = createCanvas(width, height);
const context = canvas.getContext('2d');
const { onClose, onRender } = await params.func(({ width, height, canvas }));
async function readNextFrame(progress) {
context.clearRect(0, 0, canvas.width, canvas.height);
await onRender(progress);
// require('fs').writeFileSync(`${new Date().getTime()}.png`, canvas.toBuffer('image/png'));
// I don't know any way to draw a node-canvas as a layer on a fabric.js canvas, other than converting to rgba first:
return canvasToRgba(context);
}
return {
readNextFrame,
// Node canvas needs no cleanup https://github.com/Automattic/node-canvas/issues/1216#issuecomment-412390668
close: onClose,
};
}
async function customFabricFrameSource({ canvas, width, height, params }) { async function customFabricFrameSource({ canvas, width, height, params }) {
return params.func(({ width, height, fabric, canvas })); return params.func(({ width, height, fabric, canvas }));
} }
function registerFont(...args) {
fabric.nodeCanvas.registerFont(...args);
}
module.exports = { module.exports = {
registerFont,
createFabricFrameSource,
createCustomCanvasFrameSource,
customFabricFrameSource, customFabricFrameSource,
subtitleFrameSource, subtitleFrameSource,
titleFrameSource, titleFrameSource,
@ -400,8 +322,4 @@ module.exports = {
linearGradientFrameSource, linearGradientFrameSource,
imageFrameSource, imageFrameSource,
imageOverlayFrameSource, imageOverlayFrameSource,
createFabricCanvas,
renderFabricCanvas,
rgbaToFabricImage,
}; };

43
sources/frameSource.js

@ -1,10 +1,25 @@
const assert = require('assert'); const assert = require('assert');
const pMap = require('p-map'); const pMap = require('p-map');
const { rgbaToFabricImage, customFabricFrameSource, createCustomCanvasFrameSource, titleFrameSource, subtitleFrameSource, imageFrameSource, imageOverlayFrameSource, linearGradientFrameSource, radialGradientFrameSource, fillColorFrameSource, createFabricFrameSource, newsTitleFrameSource, createFabricCanvas, renderFabricCanvas } = require('./fabricFrameSource');
const { rgbaToFabricImage, createCustomCanvasFrameSource, createFabricFrameSource, createFabricCanvas, renderFabricCanvas } = require('./fabric');
const { customFabricFrameSource, subtitleFrameSource, titleFrameSource, newsTitleFrameSource, fillColorFrameSource, radialGradientFrameSource, linearGradientFrameSource, imageFrameSource, imageOverlayFrameSource } = require('./fabric/fabricFrameSources');
const createVideoFrameSource = require('./videoFrameSource'); const createVideoFrameSource = require('./videoFrameSource');
const { createGlFrameSource } = require('./glFrameSource'); const { createGlFrameSource } = require('./glFrameSource');
const fabricFrameSources = {
fabric: customFabricFrameSource,
image: imageFrameSource,
'image-overlay': imageOverlayFrameSource,
title: titleFrameSource,
subtitle: subtitleFrameSource,
'linear-gradient': linearGradientFrameSource,
'radial-gradient': radialGradientFrameSource,
'fill-color': fillColorFrameSource,
'news-title': newsTitleFrameSource,
};
async function createFrameSource({ clip, clipIndex, width, height, channels, verbose, ffmpegPath, ffprobePath, enableFfmpegLog, framerateStr }) { async function createFrameSource({ clip, clipIndex, width, height, channels, verbose, ffmpegPath, ffprobePath, enableFfmpegLog, framerateStr }) {
const { layers, duration } = clip; const { layers, duration } = clip;
@ -14,21 +29,17 @@ async function createFrameSource({ clip, clipIndex, width, height, channels, ver
const { type, ...params } = layer; const { type, ...params } = layer;
console.log('createFrameSource', type, 'clip', clipIndex, 'layer', layerIndex); console.log('createFrameSource', type, 'clip', clipIndex, 'layer', layerIndex);
const frameSourceFuncs = {
video: createVideoFrameSource,
gl: createGlFrameSource,
canvas: createCustomCanvasFrameSource,
fabric: async (opts) => createFabricFrameSource(customFabricFrameSource, opts),
image: async (opts) => createFabricFrameSource(imageFrameSource, opts),
'image-overlay': async (opts) => createFabricFrameSource(imageOverlayFrameSource, opts),
title: async (opts) => createFabricFrameSource(titleFrameSource, opts),
subtitle: async (opts) => createFabricFrameSource(subtitleFrameSource, opts),
'linear-gradient': async (opts) => createFabricFrameSource(linearGradientFrameSource, opts),
'radial-gradient': async (opts) => createFabricFrameSource(radialGradientFrameSource, opts),
'fill-color': async (opts) => createFabricFrameSource(fillColorFrameSource, opts),
'news-title': async (opts) => createFabricFrameSource(newsTitleFrameSource, opts),
};
const createFrameSourceFunc = frameSourceFuncs[type];
let createFrameSourceFunc;
if (fabricFrameSources[type]) {
createFrameSourceFunc = async (opts) => createFabricFrameSource(fabricFrameSources[type], opts);
} else {
createFrameSourceFunc = {
video: createVideoFrameSource,
gl: createGlFrameSource,
canvas: createCustomCanvasFrameSource,
}[type];
}
assert(createFrameSourceFunc, `Invalid type ${type}`); assert(createFrameSourceFunc, `Invalid type ${type}`);
const frameSource = await createFrameSourceFunc({ ffmpegPath, ffprobePath, width, height, duration, channels, verbose, enableFfmpegLog, framerateStr, params }); const frameSource = await createFrameSourceFunc({ ffmpegPath, ffprobePath, width, height, duration, channels, verbose, enableFfmpegLog, framerateStr, params });

Loading…
Cancel
Save