@remotion/web-renderer 4.0.392 → 4.0.393
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/can-use-webfs-target.d.ts +1 -0
- package/dist/can-use-webfs-target.js +19 -0
- package/dist/compose.d.ts +6 -1
- package/dist/compose.js +31 -23
- package/dist/drawing/calculate-transforms.js +2 -2
- package/dist/drawing/draw-dom-element.d.ts +2 -0
- package/dist/drawing/draw-dom-element.js +17 -0
- package/dist/drawing/draw-element-to-canvas.d.ts +7 -3
- package/dist/drawing/draw-element-to-canvas.js +23 -24
- package/dist/drawing/draw-element.d.ts +2 -1
- package/dist/drawing/draw-element.js +1 -1
- package/dist/drawing/drawn-fn.d.ts +5 -0
- package/dist/drawing/text/apply-text-transform.d.ts +1 -0
- package/dist/drawing/text/apply-text-transform.js +12 -0
- package/dist/drawing/text/draw-text.d.ts +2 -1
- package/dist/drawing/text/draw-text.js +41 -54
- package/dist/drawing/text/handle-text-node.d.ts +7 -1
- package/dist/drawing/text/handle-text-node.js +7 -54
- package/dist/drawing/transform-in-3d.js +28 -11
- package/dist/esm/index.mjs +365 -8420
- package/dist/index.d.ts +2 -1
- package/dist/mediabunny-mappings.d.ts +1 -0
- package/dist/mediabunny-mappings.js +10 -0
- package/dist/output-target.d.ts +1 -0
- package/dist/output-target.js +1 -0
- package/dist/render-media-on-web.d.ts +6 -1
- package/dist/render-media-on-web.js +64 -24
- package/dist/render-operations-queue.d.ts +3 -0
- package/dist/render-operations-queue.js +3 -0
- package/dist/render-still-on-web.js +15 -9
- package/dist/take-screenshot.js +1 -1
- package/dist/walk-tree.d.ts +1 -0
- package/dist/walk-tree.js +14 -0
- package/dist/web-fs-target.d.ts +7 -0
- package/dist/web-fs-target.js +41 -0
- package/package.json +6 -6
- package/dist/border-radius.d.ts +0 -31
- package/dist/border-radius.js +0 -152
- package/dist/calculate-transforms.d.ts +0 -11
- package/dist/calculate-transforms.js +0 -91
- package/dist/composable.d.ts +0 -4
- package/dist/compose-canvas.d.ts +0 -1
- package/dist/compose-canvas.js +0 -36
- package/dist/compose-svg.d.ts +0 -1
- package/dist/compose-svg.js +0 -34
- package/dist/drawing/compose-canvas.d.ts +0 -1
- package/dist/drawing/compose-canvas.js +0 -36
- package/dist/drawing/compose-svg.d.ts +0 -1
- package/dist/drawing/compose-svg.js +0 -34
- package/dist/drawing/compose.d.ts +0 -5
- package/dist/drawing/compose.js +0 -6
- package/dist/drawing/get-computed-style-cache.d.ts +0 -0
- package/dist/drawing/get-computed-style-cache.js +0 -1
- package/dist/find-canvas-elements.d.ts +0 -1
- package/dist/find-canvas-elements.js +0 -13
- package/dist/find-capturable-elements.d.ts +0 -2
- package/dist/find-capturable-elements.js +0 -26
- package/dist/opacity.d.ts +0 -4
- package/dist/opacity.js +0 -7
- package/dist/parse-transform-origin.d.ts +0 -4
- package/dist/parse-transform-origin.js +0 -7
- package/dist/transform.d.ts +0 -4
- package/dist/transform.js +0 -6
- /package/dist/{composable.js → drawing/drawn-fn.js} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const canUseWebFsWriter: () => Promise<boolean>;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export const canUseWebFsWriter = async () => {
|
|
2
|
+
if (!('storage' in navigator)) {
|
|
3
|
+
return false;
|
|
4
|
+
}
|
|
5
|
+
if (!('getDirectory' in navigator.storage)) {
|
|
6
|
+
return false;
|
|
7
|
+
}
|
|
8
|
+
try {
|
|
9
|
+
const directoryHandle = await navigator.storage.getDirectory();
|
|
10
|
+
const fileHandle = await directoryHandle.getFileHandle('remotion-probe-web-fs-support', {
|
|
11
|
+
create: true,
|
|
12
|
+
});
|
|
13
|
+
const canUse = fileHandle.createWritable !== undefined;
|
|
14
|
+
return canUse;
|
|
15
|
+
}
|
|
16
|
+
catch (_a) {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
};
|
package/dist/compose.d.ts
CHANGED
|
@@ -1 +1,6 @@
|
|
|
1
|
-
export declare const compose: (element
|
|
1
|
+
export declare const compose: ({ element, context, offsetLeft, offsetTop, }: {
|
|
2
|
+
element: HTMLElement | SVGElement;
|
|
3
|
+
context: OffscreenCanvasRenderingContext2D;
|
|
4
|
+
offsetLeft: number;
|
|
5
|
+
offsetTop: number;
|
|
6
|
+
}) => Promise<void>;
|
package/dist/compose.js
CHANGED
|
@@ -1,7 +1,23 @@
|
|
|
1
|
+
import { drawDomElement } from './drawing/draw-dom-element';
|
|
1
2
|
import { drawElementToCanvas } from './drawing/draw-element-to-canvas';
|
|
2
3
|
import { handleTextNode } from './drawing/text/handle-text-node';
|
|
3
|
-
import {
|
|
4
|
-
|
|
4
|
+
import { skipToNextNonDescendant } from './walk-tree';
|
|
5
|
+
const walkOverNode = ({ node, context, offsetLeft, offsetTop, }) => {
|
|
6
|
+
if (node instanceof HTMLElement || node instanceof SVGElement) {
|
|
7
|
+
return drawElementToCanvas({
|
|
8
|
+
element: node,
|
|
9
|
+
context,
|
|
10
|
+
draw: drawDomElement(node),
|
|
11
|
+
offsetLeft,
|
|
12
|
+
offsetTop,
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
if (node instanceof Text) {
|
|
16
|
+
return handleTextNode({ node, context, offsetLeft, offsetTop });
|
|
17
|
+
}
|
|
18
|
+
throw new Error('Unknown node type');
|
|
19
|
+
};
|
|
20
|
+
export const compose = async ({ element, context, offsetLeft, offsetTop, }) => {
|
|
5
21
|
const treeWalker = document.createTreeWalker(element, NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_TEXT, (node) => {
|
|
6
22
|
if (node instanceof Element) {
|
|
7
23
|
// SVG does have children, but we process SVG elements in its
|
|
@@ -16,28 +32,20 @@ export const compose = async (element, context) => {
|
|
|
16
32
|
}
|
|
17
33
|
return NodeFilter.FILTER_ACCEPT;
|
|
18
34
|
});
|
|
19
|
-
while (
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
: node instanceof HTMLCanvasElement
|
|
31
|
-
? node
|
|
32
|
-
: null);
|
|
33
|
-
if (drawable) {
|
|
34
|
-
context.drawImage(drawable, dimensions.left, dimensions.top, dimensions.width, dimensions.height);
|
|
35
|
-
}
|
|
36
|
-
},
|
|
37
|
-
});
|
|
35
|
+
while (true) {
|
|
36
|
+
const val = await walkOverNode({
|
|
37
|
+
node: treeWalker.currentNode,
|
|
38
|
+
context,
|
|
39
|
+
offsetLeft,
|
|
40
|
+
offsetTop,
|
|
41
|
+
});
|
|
42
|
+
if (val === 'skip-children') {
|
|
43
|
+
if (!skipToNextNonDescendant(treeWalker)) {
|
|
44
|
+
break;
|
|
45
|
+
}
|
|
38
46
|
}
|
|
39
|
-
else if (
|
|
40
|
-
|
|
47
|
+
else if (!treeWalker.nextNode()) {
|
|
48
|
+
break;
|
|
41
49
|
}
|
|
42
50
|
}
|
|
43
51
|
};
|
|
@@ -46,10 +46,10 @@ export const calculateTransforms = (element) => {
|
|
|
46
46
|
// 2. Rotate
|
|
47
47
|
// 3. Scale
|
|
48
48
|
// 4. CSS "transform"
|
|
49
|
-
if (rotate !== '') {
|
|
49
|
+
if (rotate !== '' && rotate !== 'none') {
|
|
50
50
|
additionalMatrices.push(new DOMMatrix(`rotate(${rotate})`));
|
|
51
51
|
}
|
|
52
|
-
if (scale !== '') {
|
|
52
|
+
if (scale !== '' && scale !== 'none') {
|
|
53
53
|
additionalMatrices.push(new DOMMatrix(`scale(${scale})`));
|
|
54
54
|
}
|
|
55
55
|
additionalMatrices.push(matrix);
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { turnSvgIntoDrawable } from './turn-svg-into-drawable';
|
|
2
|
+
export const drawDomElement = (node) => {
|
|
3
|
+
const domDrawFn = async ({ dimensions, contextToDraw }) => {
|
|
4
|
+
const drawable = await (node instanceof SVGSVGElement
|
|
5
|
+
? turnSvgIntoDrawable(node)
|
|
6
|
+
: node instanceof HTMLImageElement
|
|
7
|
+
? node
|
|
8
|
+
: node instanceof HTMLCanvasElement
|
|
9
|
+
? node
|
|
10
|
+
: null);
|
|
11
|
+
if (!drawable) {
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
contextToDraw.drawImage(drawable, dimensions.left, dimensions.top, dimensions.width, dimensions.height);
|
|
15
|
+
};
|
|
16
|
+
return domDrawFn;
|
|
17
|
+
};
|
|
@@ -1,5 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
import type { DrawFn } from './drawn-fn';
|
|
2
|
+
export type DrawElementToCanvasReturnValue = 'continue' | 'skip-children';
|
|
3
|
+
export declare const drawElementToCanvas: ({ element, context, draw, offsetLeft, offsetTop, }: {
|
|
2
4
|
element: HTMLElement | SVGElement;
|
|
3
5
|
context: OffscreenCanvasRenderingContext2D;
|
|
4
|
-
draw:
|
|
5
|
-
|
|
6
|
+
draw: DrawFn;
|
|
7
|
+
offsetLeft: number;
|
|
8
|
+
offsetTop: number;
|
|
9
|
+
}) => Promise<DrawElementToCanvasReturnValue>;
|
|
@@ -1,19 +1,20 @@
|
|
|
1
|
+
import { compose } from '../compose';
|
|
1
2
|
import { calculateTransforms } from './calculate-transforms';
|
|
2
3
|
import { drawElement } from './draw-element';
|
|
3
4
|
import { transformIn3d } from './transform-in-3d';
|
|
4
|
-
export const drawElementToCanvas = async ({ element, context, draw, }) => {
|
|
5
|
+
export const drawElementToCanvas = async ({ element, context, draw, offsetLeft, offsetTop, }) => {
|
|
5
6
|
const { totalMatrix, reset, dimensions, opacity, computedStyle } = calculateTransforms(element);
|
|
6
7
|
if (opacity === 0) {
|
|
7
8
|
reset();
|
|
8
|
-
return;
|
|
9
|
+
return 'continue';
|
|
9
10
|
}
|
|
10
11
|
if (dimensions.width <= 0 || dimensions.height <= 0) {
|
|
11
12
|
reset();
|
|
12
|
-
return;
|
|
13
|
+
return 'continue';
|
|
13
14
|
}
|
|
14
15
|
if (!totalMatrix.is2D) {
|
|
15
|
-
const
|
|
16
|
-
const
|
|
16
|
+
const canvasOffsetLeft = Math.min(dimensions.left, 0);
|
|
17
|
+
const canvasOffsetTop = Math.min(dimensions.top, 0);
|
|
17
18
|
const tempCanvasWidth = Math.max(dimensions.width, dimensions.right);
|
|
18
19
|
const tempCanvasHeight = Math.max(dimensions.height, dimensions.bottom);
|
|
19
20
|
const tempCanvas = new OffscreenCanvas(tempCanvasWidth, tempCanvasHeight);
|
|
@@ -21,34 +22,32 @@ export const drawElementToCanvas = async ({ element, context, draw, }) => {
|
|
|
21
22
|
if (!context2) {
|
|
22
23
|
throw new Error('Could not get context');
|
|
23
24
|
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
dimensions: adjustedDimensions,
|
|
27
|
-
computedStyle,
|
|
25
|
+
await compose({
|
|
26
|
+
element,
|
|
28
27
|
context: context2,
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
totalMatrix: new DOMMatrix(),
|
|
28
|
+
offsetLeft: canvasOffsetLeft,
|
|
29
|
+
offsetTop: canvasOffsetTop,
|
|
32
30
|
});
|
|
33
31
|
const transformed = transformIn3d({
|
|
34
32
|
canvasWidth: tempCanvasWidth,
|
|
35
33
|
canvasHeight: tempCanvasHeight,
|
|
36
34
|
matrix: totalMatrix,
|
|
37
35
|
sourceCanvas: tempCanvas,
|
|
38
|
-
offsetLeft,
|
|
39
|
-
offsetTop,
|
|
36
|
+
offsetLeft: canvasOffsetLeft,
|
|
37
|
+
offsetTop: canvasOffsetTop,
|
|
40
38
|
});
|
|
41
39
|
context.drawImage(transformed, 0, 0);
|
|
40
|
+
reset();
|
|
41
|
+
return 'skip-children';
|
|
42
42
|
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
});
|
|
52
|
-
}
|
|
43
|
+
await drawElement({
|
|
44
|
+
dimensions: new DOMRect(dimensions.left - offsetLeft, dimensions.top - offsetTop, dimensions.width, dimensions.height),
|
|
45
|
+
computedStyle,
|
|
46
|
+
context,
|
|
47
|
+
draw,
|
|
48
|
+
opacity,
|
|
49
|
+
totalMatrix,
|
|
50
|
+
});
|
|
53
51
|
reset();
|
|
52
|
+
return 'continue';
|
|
54
53
|
};
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
import type { DrawFn } from './drawn-fn';
|
|
1
2
|
export declare const drawElement: ({ dimensions, computedStyle, context, draw, opacity, totalMatrix, }: {
|
|
2
3
|
dimensions: DOMRect;
|
|
3
4
|
computedStyle: CSSStyleDeclaration;
|
|
4
5
|
context: OffscreenCanvasRenderingContext2D;
|
|
5
6
|
opacity: number;
|
|
6
7
|
totalMatrix: DOMMatrix;
|
|
7
|
-
draw:
|
|
8
|
+
draw: DrawFn;
|
|
8
9
|
}) => Promise<void>;
|
|
@@ -34,7 +34,7 @@ export const drawElement = async ({ dimensions, computedStyle, context, draw, op
|
|
|
34
34
|
context.fillRect(dimensions.left, dimensions.top, dimensions.width, dimensions.height);
|
|
35
35
|
context.fillStyle = originalFillStyle;
|
|
36
36
|
}
|
|
37
|
-
await draw(dimensions, computedStyle);
|
|
37
|
+
await draw({ dimensions, computedStyle, contextToDraw: context });
|
|
38
38
|
drawBorder({
|
|
39
39
|
ctx: context,
|
|
40
40
|
x: dimensions.left,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const applyTextTransform: (text: string, transform: string) => string;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export const applyTextTransform = (text, transform) => {
|
|
2
|
+
if (transform === 'uppercase') {
|
|
3
|
+
return text.toUpperCase();
|
|
4
|
+
}
|
|
5
|
+
if (transform === 'lowercase') {
|
|
6
|
+
return text.toLowerCase();
|
|
7
|
+
}
|
|
8
|
+
if (transform === 'capitalize') {
|
|
9
|
+
return text.replace(/\b\w/g, (char) => char.toUpperCase());
|
|
10
|
+
}
|
|
11
|
+
return text;
|
|
12
|
+
};
|
|
@@ -1 +1,2 @@
|
|
|
1
|
-
|
|
1
|
+
import type { DrawFn } from '../drawn-fn';
|
|
2
|
+
export declare const drawText: (span: HTMLSpanElement) => DrawFn;
|
|
@@ -1,57 +1,44 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
if (
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
continue;
|
|
1
|
+
import { Internals } from 'remotion';
|
|
2
|
+
import { applyTextTransform } from './apply-text-transform';
|
|
3
|
+
import { findLineBreaks } from './find-line-breaks.text';
|
|
4
|
+
import { getCollapsedText } from './get-collapsed-text';
|
|
5
|
+
export const drawText = (span) => {
|
|
6
|
+
const drawFn = ({ dimensions: rect, computedStyle, contextToDraw }) => {
|
|
7
|
+
const { fontFamily, fontSize, fontWeight, color, lineHeight, direction, writingMode, letterSpacing, textTransform, } = computedStyle;
|
|
8
|
+
const isVertical = writingMode !== 'horizontal-tb';
|
|
9
|
+
if (isVertical) {
|
|
10
|
+
// TODO: Only warn once per render.
|
|
11
|
+
Internals.Log.warn({
|
|
12
|
+
logLevel: 'warn',
|
|
13
|
+
tag: '@remotion/web-renderer',
|
|
14
|
+
}, 'Detected "writing-mode" CSS property. Vertical text is not yet supported in @remotion/web-renderer');
|
|
15
|
+
return;
|
|
17
16
|
}
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
const { promise, resolve, reject } = withResolvers();
|
|
44
|
-
// Create image and draw when loaded
|
|
45
|
-
const img = new window.Image();
|
|
46
|
-
img.onload = function () {
|
|
47
|
-
resolve(img);
|
|
48
|
-
};
|
|
49
|
-
img.onerror = function (err) {
|
|
50
|
-
// We may want to add robust error handling here
|
|
51
|
-
reject(err);
|
|
17
|
+
contextToDraw.save();
|
|
18
|
+
contextToDraw.font = `${fontWeight} ${fontSize} ${fontFamily}`;
|
|
19
|
+
contextToDraw.fillStyle = color;
|
|
20
|
+
contextToDraw.letterSpacing = letterSpacing;
|
|
21
|
+
const fontSizePx = parseFloat(fontSize);
|
|
22
|
+
// TODO: This is not necessarily correct, need to create text and measure to know for sure
|
|
23
|
+
const lineHeightPx = lineHeight === 'normal' ? 1.2 * fontSizePx : parseFloat(lineHeight);
|
|
24
|
+
const baselineOffset = (lineHeightPx - fontSizePx) / 2;
|
|
25
|
+
const isRTL = direction === 'rtl';
|
|
26
|
+
contextToDraw.textAlign = isRTL ? 'right' : 'left';
|
|
27
|
+
contextToDraw.textBaseline = 'top';
|
|
28
|
+
const originalText = span.textContent;
|
|
29
|
+
const collapsedText = getCollapsedText(span);
|
|
30
|
+
const transformedText = applyTextTransform(collapsedText, textTransform);
|
|
31
|
+
span.textContent = transformedText;
|
|
32
|
+
// For RTL text, fill from the right edge instead of left
|
|
33
|
+
const xPosition = isRTL ? rect.right : rect.left;
|
|
34
|
+
const lines = findLineBreaks(span, isRTL);
|
|
35
|
+
let offsetTop = 0;
|
|
36
|
+
for (const line of lines) {
|
|
37
|
+
contextToDraw.fillText(line.text, xPosition + line.offsetHorizontal, rect.top + baselineOffset + offsetTop);
|
|
38
|
+
offsetTop += line.offsetTop;
|
|
39
|
+
}
|
|
40
|
+
span.textContent = originalText;
|
|
41
|
+
contextToDraw.restore();
|
|
52
42
|
};
|
|
53
|
-
|
|
54
|
-
await promise;
|
|
55
|
-
console.log(clientRect);
|
|
56
|
-
context.drawImage(img, clientRect.left, clientRect.top, clientRect.width, clientRect.height);
|
|
43
|
+
return drawFn;
|
|
57
44
|
};
|
|
@@ -1 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
import type { DrawElementToCanvasReturnValue } from '../draw-element-to-canvas';
|
|
2
|
+
export declare const handleTextNode: ({ node, context, offsetLeft, offsetTop, }: {
|
|
3
|
+
node: Text;
|
|
4
|
+
context: OffscreenCanvasRenderingContext2D;
|
|
5
|
+
offsetLeft: number;
|
|
6
|
+
offsetTop: number;
|
|
7
|
+
}) => Promise<DrawElementToCanvasReturnValue>;
|
|
@@ -1,20 +1,6 @@
|
|
|
1
|
-
import { Internals } from 'remotion';
|
|
2
1
|
import { drawElementToCanvas } from '../draw-element-to-canvas';
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
const applyTextTransform = (text, transform) => {
|
|
6
|
-
if (transform === 'uppercase') {
|
|
7
|
-
return text.toUpperCase();
|
|
8
|
-
}
|
|
9
|
-
if (transform === 'lowercase') {
|
|
10
|
-
return text.toLowerCase();
|
|
11
|
-
}
|
|
12
|
-
if (transform === 'capitalize') {
|
|
13
|
-
return text.replace(/\b\w/g, (char) => char.toUpperCase());
|
|
14
|
-
}
|
|
15
|
-
return text;
|
|
16
|
-
};
|
|
17
|
-
export const handleTextNode = async (node, context) => {
|
|
2
|
+
import { drawText } from './draw-text';
|
|
3
|
+
export const handleTextNode = async ({ node, context, offsetLeft, offsetTop, }) => {
|
|
18
4
|
const span = document.createElement('span');
|
|
19
5
|
const parent = node.parentNode;
|
|
20
6
|
if (!parent) {
|
|
@@ -22,48 +8,15 @@ export const handleTextNode = async (node, context) => {
|
|
|
22
8
|
}
|
|
23
9
|
parent.insertBefore(span, node);
|
|
24
10
|
span.appendChild(node);
|
|
25
|
-
await drawElementToCanvas({
|
|
11
|
+
const value = await drawElementToCanvas({
|
|
26
12
|
context,
|
|
27
13
|
element: span,
|
|
28
|
-
draw(
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
if (isVertical) {
|
|
32
|
-
// TODO: Only warn once per render.
|
|
33
|
-
Internals.Log.warn({
|
|
34
|
-
logLevel: 'warn',
|
|
35
|
-
tag: '@remotion/web-renderer',
|
|
36
|
-
}, 'Detected "writing-mode" CSS property. Vertical text is not yet supported in @remotion/web-renderer');
|
|
37
|
-
return;
|
|
38
|
-
}
|
|
39
|
-
context.save();
|
|
40
|
-
context.font = `${fontWeight} ${fontSize} ${fontFamily}`;
|
|
41
|
-
context.fillStyle = color;
|
|
42
|
-
context.letterSpacing = letterSpacing;
|
|
43
|
-
const fontSizePx = parseFloat(fontSize);
|
|
44
|
-
// TODO: This is not necessarily correct, need to create text and measure to know for sure
|
|
45
|
-
const lineHeightPx = lineHeight === 'normal' ? 1.2 * fontSizePx : parseFloat(lineHeight);
|
|
46
|
-
const baselineOffset = (lineHeightPx - fontSizePx) / 2;
|
|
47
|
-
const isRTL = direction === 'rtl';
|
|
48
|
-
context.textAlign = isRTL ? 'right' : 'left';
|
|
49
|
-
context.textBaseline = 'top';
|
|
50
|
-
const originalText = span.textContent;
|
|
51
|
-
const collapsedText = getCollapsedText(span);
|
|
52
|
-
const transformedText = applyTextTransform(collapsedText, textTransform);
|
|
53
|
-
span.textContent = transformedText;
|
|
54
|
-
// For RTL text, fill from the right edge instead of left
|
|
55
|
-
const xPosition = isRTL ? rect.right : rect.left;
|
|
56
|
-
const lines = findLineBreaks(span, isRTL);
|
|
57
|
-
let offsetTop = 0;
|
|
58
|
-
for (const line of lines) {
|
|
59
|
-
context.fillText(line.text, xPosition + line.offsetHorizontal, rect.top + baselineOffset + offsetTop);
|
|
60
|
-
offsetTop += line.offsetTop;
|
|
61
|
-
}
|
|
62
|
-
span.textContent = originalText;
|
|
63
|
-
context.restore();
|
|
64
|
-
},
|
|
14
|
+
draw: drawText(span),
|
|
15
|
+
offsetLeft,
|
|
16
|
+
offsetTop,
|
|
65
17
|
});
|
|
66
18
|
// Undo the layout manipulation
|
|
67
19
|
parent.insertBefore(node, span);
|
|
68
20
|
parent.removeChild(span);
|
|
21
|
+
return value;
|
|
69
22
|
};
|
|
@@ -12,7 +12,16 @@ function compileShader(shaderGl, source, type) {
|
|
|
12
12
|
}
|
|
13
13
|
return shader;
|
|
14
14
|
}
|
|
15
|
-
|
|
15
|
+
let helperCanvas = null;
|
|
16
|
+
const createHelperCanvas = ({ canvasWidth, canvasHeight, }) => {
|
|
17
|
+
if (helperCanvas &&
|
|
18
|
+
helperCanvas.canvas.width >= canvasWidth &&
|
|
19
|
+
helperCanvas.canvas.height >= canvasHeight) {
|
|
20
|
+
// Clear and draw
|
|
21
|
+
helperCanvas.gl.clearColor(0, 0, 0, 0); // Transparent background
|
|
22
|
+
helperCanvas.gl.clear(helperCanvas.gl.COLOR_BUFFER_BIT);
|
|
23
|
+
return helperCanvas;
|
|
24
|
+
}
|
|
16
25
|
const canvas = new OffscreenCanvas(canvasWidth, canvasHeight);
|
|
17
26
|
const gl = canvas.getContext('webgl');
|
|
18
27
|
if (!gl) {
|
|
@@ -52,6 +61,19 @@ export const transformIn3d = ({ canvasWidth, canvasHeight, matrix, sourceCanvas,
|
|
|
52
61
|
throw new Error('Program link error: ' + gl.getProgramInfoLog(program));
|
|
53
62
|
}
|
|
54
63
|
gl.useProgram(program);
|
|
64
|
+
// Clear and draw
|
|
65
|
+
gl.clearColor(0, 0, 0, 0); // Transparent background
|
|
66
|
+
gl.clear(gl.COLOR_BUFFER_BIT);
|
|
67
|
+
// Enable blending for transparency
|
|
68
|
+
gl.enable(gl.BLEND);
|
|
69
|
+
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
|
|
70
|
+
helperCanvas = { canvas, gl, program };
|
|
71
|
+
return helperCanvas;
|
|
72
|
+
};
|
|
73
|
+
export const transformIn3d = ({ canvasWidth, canvasHeight, matrix, sourceCanvas, offsetLeft, offsetTop, }) => {
|
|
74
|
+
const { canvas, gl, program } = createHelperCanvas({ canvasWidth, canvasHeight });
|
|
75
|
+
const vertexBuffer = gl.createBuffer();
|
|
76
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
|
|
55
77
|
// Create a quad (two triangles) with texture coordinates
|
|
56
78
|
// prettier-ignore
|
|
57
79
|
const vertices = new Float32Array([
|
|
@@ -65,8 +87,6 @@ export const transformIn3d = ({ canvasWidth, canvasHeight, matrix, sourceCanvas,
|
|
|
65
87
|
canvasWidth + offsetLeft, offsetTop, 1, 0, // bottom-right
|
|
66
88
|
canvasWidth + offsetLeft, canvasHeight + offsetTop, 1, 1, // top-right
|
|
67
89
|
]);
|
|
68
|
-
const vertexBuffer = gl.createBuffer();
|
|
69
|
-
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
|
|
70
90
|
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
|
|
71
91
|
const aPosition = gl.getAttribLocation(program, 'aPosition');
|
|
72
92
|
const aTexCoord = gl.getAttribLocation(program, 'aTexCoord');
|
|
@@ -91,12 +111,12 @@ export const transformIn3d = ({ canvasWidth, canvasHeight, matrix, sourceCanvas,
|
|
|
91
111
|
const zScale = 1000000000; // By default infinite in chrome
|
|
92
112
|
// Create orthographic projection matrix for pixel coordinates
|
|
93
113
|
const projectionMatrix = new Float32Array([
|
|
94
|
-
2 /
|
|
114
|
+
2 / canvas.width,
|
|
95
115
|
0,
|
|
96
116
|
0,
|
|
97
117
|
0,
|
|
98
118
|
0,
|
|
99
|
-
-2 /
|
|
119
|
+
-2 / canvas.height,
|
|
100
120
|
0,
|
|
101
121
|
0,
|
|
102
122
|
0,
|
|
@@ -114,12 +134,9 @@ export const transformIn3d = ({ canvasWidth, canvasHeight, matrix, sourceCanvas,
|
|
|
114
134
|
gl.uniformMatrix4fv(uTransform, false, transformMatrix);
|
|
115
135
|
gl.uniformMatrix4fv(uProjection, false, projectionMatrix);
|
|
116
136
|
gl.uniform1i(uTexture, 0); // Use texture unit 0
|
|
117
|
-
// Clear and draw
|
|
118
|
-
gl.clearColor(0, 0, 0, 0); // Transparent background
|
|
119
|
-
gl.clear(gl.COLOR_BUFFER_BIT);
|
|
120
|
-
// Enable blending for transparency
|
|
121
|
-
gl.enable(gl.BLEND);
|
|
122
|
-
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
|
|
123
137
|
gl.drawArrays(gl.TRIANGLES, 0, 6);
|
|
138
|
+
// Clean up resources to prevent leaks and ensure clean state for reuse
|
|
139
|
+
gl.deleteTexture(texture);
|
|
140
|
+
gl.deleteBuffer(vertexBuffer);
|
|
124
141
|
return canvas;
|
|
125
142
|
};
|