@remotion/web-renderer 4.0.394 → 4.0.396

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.
Files changed (72) hide show
  1. package/dist/border-radius.d.ts +31 -0
  2. package/dist/border-radius.js +152 -0
  3. package/dist/calculate-transforms.d.ts +2 -0
  4. package/dist/calculate-transforms.js +17 -0
  5. package/dist/composable.d.ts +2 -8
  6. package/dist/compose-canvas.js +28 -4
  7. package/dist/compose.d.ts +6 -3
  8. package/dist/compose.js +41 -12
  9. package/dist/drawing/border-radius.d.ts +3 -5
  10. package/dist/drawing/border-radius.js +12 -11
  11. package/dist/drawing/calculate-transforms.d.ts +6 -2
  12. package/dist/drawing/calculate-transforms.js +19 -22
  13. package/dist/drawing/canvas-offset-from-rect.d.ts +8 -0
  14. package/dist/drawing/canvas-offset-from-rect.js +12 -0
  15. package/dist/drawing/clamp-rect-to-parent-bounds.d.ts +4 -0
  16. package/dist/drawing/clamp-rect-to-parent-bounds.js +7 -0
  17. package/dist/drawing/compose-canvas.d.ts +1 -0
  18. package/dist/drawing/compose-canvas.js +36 -0
  19. package/dist/drawing/compose-svg.d.ts +1 -0
  20. package/dist/drawing/compose-svg.js +34 -0
  21. package/dist/drawing/compose.d.ts +5 -0
  22. package/dist/drawing/compose.js +6 -0
  23. package/dist/drawing/draw-border.d.ts +2 -5
  24. package/dist/drawing/draw-border.js +307 -55
  25. package/dist/drawing/draw-element-to-canvas.d.ts +4 -1
  26. package/dist/drawing/draw-element-to-canvas.js +9 -26
  27. package/dist/drawing/draw-element.d.ts +5 -3
  28. package/dist/drawing/draw-element.js +29 -14
  29. package/dist/drawing/draw-outline.d.ts +9 -0
  30. package/dist/drawing/draw-outline.js +116 -0
  31. package/dist/drawing/get-bounding-box-including-shadow.d.ts +1 -0
  32. package/dist/drawing/get-bounding-box-including-shadow.js +6 -0
  33. package/dist/drawing/get-computed-style-cache.d.ts +0 -0
  34. package/dist/drawing/get-computed-style-cache.js +1 -0
  35. package/dist/drawing/get-pretransform-rect.d.ts +1 -0
  36. package/dist/drawing/get-pretransform-rect.js +31 -0
  37. package/dist/drawing/handle-3d-transform.d.ts +10 -0
  38. package/dist/drawing/handle-3d-transform.js +39 -0
  39. package/dist/drawing/has-transform.d.ts +4 -0
  40. package/dist/drawing/has-transform.js +14 -0
  41. package/dist/drawing/overflow.d.ts +7 -0
  42. package/dist/drawing/overflow.js +12 -0
  43. package/dist/drawing/process-node.d.ts +17 -0
  44. package/dist/drawing/process-node.js +41 -0
  45. package/dist/drawing/text/draw-text.js +6 -8
  46. package/dist/drawing/text/handle-text-node.d.ts +8 -5
  47. package/dist/drawing/text/handle-text-node.js +6 -5
  48. package/dist/drawing/transform-in-3d.d.ts +7 -7
  49. package/dist/drawing/transform-in-3d.js +27 -13
  50. package/dist/drawing/transform-rect-with-matrix.d.ts +4 -0
  51. package/dist/drawing/transform-rect-with-matrix.js +19 -0
  52. package/dist/drawing/turn-svg-into-drawable.js +7 -0
  53. package/dist/esm/index.mjs +897 -205
  54. package/dist/find-canvas-elements.d.ts +1 -0
  55. package/dist/find-canvas-elements.js +13 -0
  56. package/dist/find-capturable-elements.d.ts +1 -1
  57. package/dist/find-capturable-elements.js +20 -22
  58. package/dist/get-biggest-bounding-client-rect.js +18 -5
  59. package/dist/internal-state.d.ts +9 -0
  60. package/dist/internal-state.js +12 -0
  61. package/dist/opacity.d.ts +4 -0
  62. package/dist/opacity.js +7 -0
  63. package/dist/render-media-on-web.d.ts +3 -0
  64. package/dist/render-media-on-web.js +38 -15
  65. package/dist/render-still-on-web.d.ts +6 -1
  66. package/dist/render-still-on-web.js +29 -8
  67. package/dist/send-telemetry-event.js +1 -1
  68. package/dist/take-screenshot.d.ts +8 -2
  69. package/dist/take-screenshot.js +16 -4
  70. package/dist/transform.d.ts +4 -0
  71. package/dist/transform.js +6 -0
  72. package/package.json +7 -6
@@ -0,0 +1,116 @@
1
+ export const parseOutlineWidth = (value) => {
2
+ return parseFloat(value) || 0;
3
+ };
4
+ export const parseOutlineOffset = (value) => {
5
+ return parseFloat(value) || 0;
6
+ };
7
+ const getLineDashPattern = (style, width) => {
8
+ if (style === 'dashed') {
9
+ return [width * 2, width];
10
+ }
11
+ if (style === 'dotted') {
12
+ return [width, width];
13
+ }
14
+ return [];
15
+ };
16
+ export const drawOutline = ({ ctx, rect, borderRadius, computedStyle, }) => {
17
+ const outlineWidth = parseOutlineWidth(computedStyle.outlineWidth);
18
+ const { outlineStyle } = computedStyle;
19
+ const outlineColor = computedStyle.outlineColor || 'black';
20
+ const outlineOffset = parseOutlineOffset(computedStyle.outlineOffset);
21
+ // Check if we have a visible outline
22
+ if (outlineWidth <= 0 ||
23
+ outlineStyle === 'none' ||
24
+ outlineStyle === 'hidden') {
25
+ return;
26
+ }
27
+ // Save original canvas state
28
+ const originalStrokeStyle = ctx.strokeStyle;
29
+ const originalLineWidth = ctx.lineWidth;
30
+ const originalLineDash = ctx.getLineDash();
31
+ ctx.beginPath();
32
+ ctx.strokeStyle = outlineColor;
33
+ ctx.lineWidth = outlineWidth;
34
+ ctx.setLineDash(getLineDashPattern(outlineStyle, outlineWidth));
35
+ // Calculate outline position
36
+ // Outline is drawn outside the border edge, offset by outlineOffset
37
+ const halfWidth = outlineWidth / 2;
38
+ const offset = outlineOffset + halfWidth;
39
+ const outlineX = rect.left - offset;
40
+ const outlineY = rect.top - offset;
41
+ const outlineW = rect.width + offset * 2;
42
+ const outlineH = rect.height + offset * 2;
43
+ // Adjust border radius for the outline offset
44
+ // When outline-offset is positive, we need to expand the radius
45
+ // When outline-offset is negative, we need to shrink the radius
46
+ const adjustedBorderRadius = {
47
+ topLeft: {
48
+ horizontal: borderRadius.topLeft.horizontal === 0
49
+ ? 0
50
+ : Math.max(0, borderRadius.topLeft.horizontal + offset),
51
+ vertical: borderRadius.topLeft.vertical === 0
52
+ ? 0
53
+ : Math.max(0, borderRadius.topLeft.vertical + offset),
54
+ },
55
+ topRight: {
56
+ horizontal: borderRadius.topRight.horizontal === 0
57
+ ? 0
58
+ : Math.max(0, borderRadius.topRight.horizontal + offset),
59
+ vertical: borderRadius.topRight.vertical === 0
60
+ ? 0
61
+ : Math.max(0, borderRadius.topRight.vertical + offset),
62
+ },
63
+ bottomRight: {
64
+ horizontal: borderRadius.bottomRight.horizontal === 0
65
+ ? 0
66
+ : Math.max(0, borderRadius.bottomRight.horizontal + offset),
67
+ vertical: borderRadius.bottomRight.vertical === 0
68
+ ? 0
69
+ : Math.max(0, borderRadius.bottomRight.vertical + offset),
70
+ },
71
+ bottomLeft: {
72
+ horizontal: borderRadius.bottomLeft.horizontal === 0
73
+ ? 0
74
+ : Math.max(0, borderRadius.bottomLeft.horizontal + offset),
75
+ vertical: borderRadius.bottomLeft.vertical === 0
76
+ ? 0
77
+ : Math.max(0, borderRadius.bottomLeft.vertical + offset),
78
+ },
79
+ };
80
+ // Draw continuous path with border radius
81
+ ctx.moveTo(outlineX + adjustedBorderRadius.topLeft.horizontal, outlineY);
82
+ // Top edge
83
+ ctx.lineTo(outlineX + outlineW - adjustedBorderRadius.topRight.horizontal, outlineY);
84
+ // Top-right corner
85
+ if (adjustedBorderRadius.topRight.horizontal > 0 ||
86
+ adjustedBorderRadius.topRight.vertical > 0) {
87
+ ctx.ellipse(outlineX + outlineW - adjustedBorderRadius.topRight.horizontal, outlineY + adjustedBorderRadius.topRight.vertical, adjustedBorderRadius.topRight.horizontal, adjustedBorderRadius.topRight.vertical, 0, -Math.PI / 2, 0);
88
+ }
89
+ // Right edge
90
+ ctx.lineTo(outlineX + outlineW, outlineY + outlineH - adjustedBorderRadius.bottomRight.vertical);
91
+ // Bottom-right corner
92
+ if (adjustedBorderRadius.bottomRight.horizontal > 0 ||
93
+ adjustedBorderRadius.bottomRight.vertical > 0) {
94
+ ctx.ellipse(outlineX + outlineW - adjustedBorderRadius.bottomRight.horizontal, outlineY + outlineH - adjustedBorderRadius.bottomRight.vertical, adjustedBorderRadius.bottomRight.horizontal, adjustedBorderRadius.bottomRight.vertical, 0, 0, Math.PI / 2);
95
+ }
96
+ // Bottom edge
97
+ ctx.lineTo(outlineX + adjustedBorderRadius.bottomLeft.horizontal, outlineY + outlineH);
98
+ // Bottom-left corner
99
+ if (adjustedBorderRadius.bottomLeft.horizontal > 0 ||
100
+ adjustedBorderRadius.bottomLeft.vertical > 0) {
101
+ ctx.ellipse(outlineX + adjustedBorderRadius.bottomLeft.horizontal, outlineY + outlineH - adjustedBorderRadius.bottomLeft.vertical, adjustedBorderRadius.bottomLeft.horizontal, adjustedBorderRadius.bottomLeft.vertical, 0, Math.PI / 2, Math.PI);
102
+ }
103
+ // Left edge
104
+ ctx.lineTo(outlineX, outlineY + adjustedBorderRadius.topLeft.vertical);
105
+ // Top-left corner
106
+ if (adjustedBorderRadius.topLeft.horizontal > 0 ||
107
+ adjustedBorderRadius.topLeft.vertical > 0) {
108
+ ctx.ellipse(outlineX + adjustedBorderRadius.topLeft.horizontal, outlineY + adjustedBorderRadius.topLeft.vertical, adjustedBorderRadius.topLeft.horizontal, adjustedBorderRadius.topLeft.vertical, 0, Math.PI, (Math.PI * 3) / 2);
109
+ }
110
+ ctx.closePath();
111
+ ctx.stroke();
112
+ // Restore original canvas state
113
+ ctx.strokeStyle = originalStrokeStyle;
114
+ ctx.lineWidth = originalLineWidth;
115
+ ctx.setLineDash(originalLineDash);
116
+ };
@@ -0,0 +1 @@
1
+ export declare const getBoundingBoxIncludingOutline: (element: Element) => DOMRect;
@@ -0,0 +1,6 @@
1
+ export const getBoundingBoxIncludingOutline = (element) => {
2
+ var _a, _b, _c, _d, _e;
3
+ const rect = element.getBoundingClientRect();
4
+ const shadow = (_a = element.shadowRoot) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect();
5
+ return new DOMRect(Math.min(rect.left, (_b = shadow === null || shadow === void 0 ? void 0 : shadow.left) !== null && _b !== void 0 ? _b : 0), Math.min(rect.top, (_c = shadow === null || shadow === void 0 ? void 0 : shadow.top) !== null && _c !== void 0 ? _c : 0), Math.max(rect.right, (_d = shadow === null || shadow === void 0 ? void 0 : shadow.right) !== null && _d !== void 0 ? _d : 0), Math.max(rect.bottom, (_e = shadow === null || shadow === void 0 ? void 0 : shadow.bottom) !== null && _e !== void 0 ? _e : 0));
6
+ };
File without changes
@@ -0,0 +1 @@
1
+ "use strict";
@@ -0,0 +1 @@
1
+ export declare function getPreTransformRect(targetRect: DOMRect, matrix: DOMMatrix): DOMRect;
@@ -0,0 +1,31 @@
1
+ export function getPreTransformRect(targetRect, matrix) {
2
+ // 1. Determine the effective 2D transformation by transforming basis vectors
3
+ const origin = new DOMPoint(0, 0).matrixTransform(matrix);
4
+ const unitX = new DOMPoint(1, 0).matrixTransform(matrix);
5
+ const unitY = new DOMPoint(0, 1).matrixTransform(matrix);
6
+ // 2. Compute the 2D basis vectors after transformation
7
+ const basisX = { x: unitX.x - origin.x, y: unitX.y - origin.y };
8
+ const basisY = { x: unitY.x - origin.x, y: unitY.y - origin.y };
9
+ // 3. Build the effective 2D matrix and invert it
10
+ const effective2D = new DOMMatrix([
11
+ basisX.x,
12
+ basisX.y, // a, b (first column)
13
+ basisY.x,
14
+ basisY.y, // c, d (second column)
15
+ origin.x,
16
+ origin.y, // e, f (translation)
17
+ ]);
18
+ const inverse2D = effective2D.inverse();
19
+ // 4. Transform target rect corners using the 2D inverse
20
+ const corners = [
21
+ new DOMPoint(targetRect.x, targetRect.y),
22
+ new DOMPoint(targetRect.x + targetRect.width, targetRect.y),
23
+ new DOMPoint(targetRect.x + targetRect.width, targetRect.y + targetRect.height),
24
+ new DOMPoint(targetRect.x, targetRect.y + targetRect.height),
25
+ ];
26
+ const transformedCorners = corners.map((c) => c.matrixTransform(inverse2D));
27
+ // 5. Compute bounding box
28
+ const xs = transformedCorners.map((p) => p.x);
29
+ const ys = transformedCorners.map((p) => p.y);
30
+ return new DOMRect(Math.min(...xs), Math.min(...ys), Math.max(...xs) - Math.min(...xs), Math.max(...ys) - Math.min(...ys));
31
+ }
@@ -0,0 +1,10 @@
1
+ import type { LogLevel } from 'remotion';
2
+ import type { InternalState } from '../internal-state';
3
+ export declare const handle3dTransform: ({ element, matrix, parentRect, context, logLevel, internalState, }: {
4
+ element: HTMLElement | SVGElement;
5
+ matrix: DOMMatrix;
6
+ parentRect: DOMRect;
7
+ context: OffscreenCanvasRenderingContext2D;
8
+ logLevel: LogLevel;
9
+ internalState: InternalState;
10
+ }) => Promise<void>;
@@ -0,0 +1,39 @@
1
+ import { Internals } from 'remotion';
2
+ import { compose } from '../compose';
3
+ import { getBiggestBoundingClientRect } from '../get-biggest-bounding-client-rect';
4
+ import { getNarrowerRect } from './clamp-rect-to-parent-bounds';
5
+ import { getPreTransformRect } from './get-pretransform-rect';
6
+ import { transformIn3d } from './transform-in-3d';
7
+ export const handle3dTransform = async ({ element, matrix, parentRect, context, logLevel, internalState, }) => {
8
+ const unclampedBiggestBoundingClientRect = getBiggestBoundingClientRect(element);
9
+ const biggestPossiblePretransformRect = getPreTransformRect(parentRect, matrix);
10
+ const preTransformRect = getNarrowerRect({
11
+ firstRect: unclampedBiggestBoundingClientRect,
12
+ secondRect: biggestPossiblePretransformRect,
13
+ });
14
+ const start = Date.now();
15
+ const tempCanvas = new OffscreenCanvas(Math.ceil(preTransformRect.width), Math.ceil(preTransformRect.height));
16
+ await compose({
17
+ element,
18
+ context: tempCanvas.getContext('2d'),
19
+ logLevel,
20
+ parentRect: preTransformRect,
21
+ internalState,
22
+ });
23
+ const afterCompose = Date.now();
24
+ const { canvas: transformed, rect: transformedRect } = transformIn3d({
25
+ untransformedRect: preTransformRect,
26
+ matrix,
27
+ sourceCanvas: tempCanvas,
28
+ });
29
+ context.drawImage(transformed, transformedRect.x, transformedRect.y);
30
+ const afterDraw = Date.now();
31
+ Internals.Log.trace({
32
+ logLevel,
33
+ tag: '@remotion/web-renderer',
34
+ }, `Transforming element in 3D - canvas size: ${transformedRect.width}x${transformedRect.height} - compose: ${afterCompose - start}ms - draw: ${afterDraw - afterCompose}ms`);
35
+ internalState.add3DTransform({
36
+ canvasWidth: Math.ceil(transformedRect.width),
37
+ canvasHeight: Math.ceil(transformedRect.height),
38
+ });
39
+ };
@@ -0,0 +1,4 @@
1
+ export declare const hasTransformCssValue: (style: CSSStyleDeclaration) => boolean;
2
+ export declare const hasRotateCssValue: (style: CSSStyleDeclaration) => boolean;
3
+ export declare const hasScaleCssValue: (style: CSSStyleDeclaration) => boolean;
4
+ export declare const hasAnyTransformCssValue: (style: CSSStyleDeclaration) => boolean;
@@ -0,0 +1,14 @@
1
+ export const hasTransformCssValue = (style) => {
2
+ return style.transform !== 'none' && style.transform !== '';
3
+ };
4
+ export const hasRotateCssValue = (style) => {
5
+ return style.rotate !== 'none' && style.rotate !== '';
6
+ };
7
+ export const hasScaleCssValue = (style) => {
8
+ return style.scale !== 'none' && style.scale !== '';
9
+ };
10
+ export const hasAnyTransformCssValue = (style) => {
11
+ return (hasTransformCssValue(style) ||
12
+ hasRotateCssValue(style) ||
13
+ hasScaleCssValue(style));
14
+ };
@@ -0,0 +1,7 @@
1
+ import type { BorderRadiusCorners } from './border-radius';
2
+ export declare const setOverflowHidden: ({ ctx, rect, borderRadius, overflowHidden, }: {
3
+ ctx: OffscreenCanvasRenderingContext2D;
4
+ rect: DOMRect;
5
+ borderRadius: BorderRadiusCorners;
6
+ overflowHidden: boolean;
7
+ }) => () => void;
@@ -0,0 +1,12 @@
1
+ import { setBorderRadius } from './border-radius';
2
+ export const setOverflowHidden = ({ ctx, rect, borderRadius, overflowHidden, }) => {
3
+ if (!overflowHidden) {
4
+ return () => { };
5
+ }
6
+ return setBorderRadius({
7
+ ctx,
8
+ rect,
9
+ borderRadius,
10
+ forceClipEvenWhenZero: true,
11
+ });
12
+ };
@@ -0,0 +1,17 @@
1
+ import type { LogLevel } from 'remotion';
2
+ import type { InternalState } from '../internal-state';
3
+ import type { DrawFn } from './drawn-fn';
4
+ export type ProcessNodeReturnValue = {
5
+ type: 'continue';
6
+ cleanupAfterChildren: () => void;
7
+ } | {
8
+ type: 'skip-children';
9
+ };
10
+ export declare const processNode: ({ element, context, draw, logLevel, parentRect, internalState, }: {
11
+ element: HTMLElement | SVGElement;
12
+ context: OffscreenCanvasRenderingContext2D;
13
+ draw: DrawFn;
14
+ logLevel: LogLevel;
15
+ parentRect: DOMRect;
16
+ internalState: InternalState;
17
+ }) => Promise<ProcessNodeReturnValue>;
@@ -0,0 +1,41 @@
1
+ import { calculateTransforms } from './calculate-transforms';
2
+ import { drawElement } from './draw-element';
3
+ import { handle3dTransform } from './handle-3d-transform';
4
+ export const processNode = async ({ element, context, draw, logLevel, parentRect, internalState, }) => {
5
+ const transforms = calculateTransforms({
6
+ element,
7
+ offsetLeft: parentRect.x,
8
+ offsetTop: parentRect.y,
9
+ });
10
+ const { totalMatrix, reset, dimensions, opacity, computedStyle } = transforms;
11
+ if (opacity === 0) {
12
+ reset();
13
+ return { type: 'continue', cleanupAfterChildren: () => { } };
14
+ }
15
+ if (dimensions.width <= 0 || dimensions.height <= 0) {
16
+ reset();
17
+ return { type: 'continue', cleanupAfterChildren: () => { } };
18
+ }
19
+ if (!totalMatrix.is2D) {
20
+ await handle3dTransform({
21
+ element,
22
+ matrix: totalMatrix,
23
+ parentRect,
24
+ context,
25
+ logLevel,
26
+ internalState,
27
+ });
28
+ reset();
29
+ return { type: 'skip-children' };
30
+ }
31
+ const { cleanupAfterChildren } = await drawElement({
32
+ rect: new DOMRect(dimensions.left - parentRect.x, dimensions.top - parentRect.y, dimensions.width, dimensions.height),
33
+ computedStyle,
34
+ context,
35
+ draw,
36
+ opacity,
37
+ totalMatrix,
38
+ });
39
+ reset();
40
+ return { type: 'continue', cleanupAfterChildren };
41
+ };
@@ -4,7 +4,7 @@ import { findLineBreaks } from './find-line-breaks.text';
4
4
  import { getCollapsedText } from './get-collapsed-text';
5
5
  export const drawText = (span) => {
6
6
  const drawFn = ({ dimensions: rect, computedStyle, contextToDraw }) => {
7
- const { fontFamily, fontSize, fontWeight, color, lineHeight, direction, writingMode, letterSpacing, textTransform, } = computedStyle;
7
+ const { fontFamily, fontSize, fontWeight, color, direction, writingMode, letterSpacing, textTransform, } = computedStyle;
8
8
  const isVertical = writingMode !== 'horizontal-tb';
9
9
  if (isVertical) {
10
10
  // TODO: Only warn once per render.
@@ -15,16 +15,13 @@ export const drawText = (span) => {
15
15
  return;
16
16
  }
17
17
  contextToDraw.save();
18
- contextToDraw.font = `${fontWeight} ${fontSize} ${fontFamily}`;
18
+ const fontSizePx = parseFloat(fontSize);
19
+ contextToDraw.font = `${fontWeight} ${fontSizePx}px ${fontFamily}`;
19
20
  contextToDraw.fillStyle = color;
20
21
  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
22
  const isRTL = direction === 'rtl';
26
23
  contextToDraw.textAlign = isRTL ? 'right' : 'left';
27
- contextToDraw.textBaseline = 'top';
24
+ contextToDraw.textBaseline = 'alphabetic';
28
25
  const originalText = span.textContent;
29
26
  const collapsedText = getCollapsedText(span);
30
27
  const transformedText = applyTextTransform(collapsedText, textTransform);
@@ -33,8 +30,9 @@ export const drawText = (span) => {
33
30
  const xPosition = isRTL ? rect.right : rect.left;
34
31
  const lines = findLineBreaks(span, isRTL);
35
32
  let offsetTop = 0;
33
+ const { fontBoundingBoxAscent } = contextToDraw.measureText(lines[0].text);
36
34
  for (const line of lines) {
37
- contextToDraw.fillText(line.text, xPosition + line.offsetHorizontal, rect.top + baselineOffset + offsetTop);
35
+ contextToDraw.fillText(line.text, xPosition + line.offsetHorizontal, rect.top + offsetTop + fontBoundingBoxAscent);
38
36
  offsetTop += line.offsetTop;
39
37
  }
40
38
  span.textContent = originalText;
@@ -1,7 +1,10 @@
1
- import type { DrawElementToCanvasReturnValue } from '../draw-element-to-canvas';
2
- export declare const handleTextNode: ({ node, context, offsetLeft, offsetTop, }: {
1
+ import type { LogLevel } from 'remotion';
2
+ import type { InternalState } from '../../internal-state';
3
+ import type { ProcessNodeReturnValue } from '../process-node';
4
+ export declare const handleTextNode: ({ node, context, logLevel, parentRect, internalState, }: {
3
5
  node: Text;
4
6
  context: OffscreenCanvasRenderingContext2D;
5
- offsetLeft: number;
6
- offsetTop: number;
7
- }) => Promise<DrawElementToCanvasReturnValue>;
7
+ logLevel: LogLevel;
8
+ parentRect: DOMRect;
9
+ internalState: InternalState;
10
+ }) => Promise<ProcessNodeReturnValue>;
@@ -1,6 +1,6 @@
1
- import { drawElementToCanvas } from '../draw-element-to-canvas';
1
+ import { processNode } from '../process-node';
2
2
  import { drawText } from './draw-text';
3
- export const handleTextNode = async ({ node, context, offsetLeft, offsetTop, }) => {
3
+ export const handleTextNode = async ({ node, context, logLevel, parentRect, internalState, }) => {
4
4
  const span = document.createElement('span');
5
5
  const parent = node.parentNode;
6
6
  if (!parent) {
@@ -8,12 +8,13 @@ export const handleTextNode = async ({ node, context, offsetLeft, offsetTop, })
8
8
  }
9
9
  parent.insertBefore(span, node);
10
10
  span.appendChild(node);
11
- const value = await drawElementToCanvas({
11
+ const value = await processNode({
12
12
  context,
13
13
  element: span,
14
14
  draw: drawText(span),
15
- offsetLeft,
16
- offsetTop,
15
+ logLevel,
16
+ parentRect,
17
+ internalState,
17
18
  });
18
19
  // Undo the layout manipulation
19
20
  parent.insertBefore(node, span);
@@ -1,8 +1,8 @@
1
- export declare const transformIn3d: ({ canvasWidth, canvasHeight, matrix, sourceCanvas, offsetLeft, offsetTop, }: {
2
- canvasWidth: number;
3
- canvasHeight: number;
4
- offsetLeft: number;
5
- offsetTop: number;
1
+ export declare const transformIn3d: ({ matrix, sourceCanvas, untransformedRect, }: {
2
+ untransformedRect: DOMRect;
6
3
  matrix: DOMMatrix;
7
- sourceCanvas: HTMLCanvasElement | OffscreenCanvas;
8
- }) => OffscreenCanvas;
4
+ sourceCanvas: OffscreenCanvas;
5
+ }) => {
6
+ canvas: OffscreenCanvas;
7
+ rect: DOMRect;
8
+ };
@@ -1,3 +1,4 @@
1
+ import { transformDOMRect } from './transform-rect-with-matrix';
1
2
  function compileShader(shaderGl, source, type) {
2
3
  const shader = shaderGl.createShader(type);
3
4
  if (!shader) {
@@ -22,6 +23,12 @@ const createHelperCanvas = ({ canvasWidth, canvasHeight, }) => {
22
23
  helperCanvas.gl.clear(helperCanvas.gl.COLOR_BUFFER_BIT);
23
24
  return helperCanvas;
24
25
  }
26
+ if (helperCanvas) {
27
+ helperCanvas.gl.deleteProgram(helperCanvas.program);
28
+ helperCanvas.gl.deleteShader(helperCanvas.vertexShader);
29
+ helperCanvas.gl.deleteShader(helperCanvas.fragmentShader);
30
+ helperCanvas = null;
31
+ }
25
32
  const canvas = new OffscreenCanvas(canvasWidth, canvasHeight);
26
33
  const gl = canvas.getContext('webgl');
27
34
  if (!gl) {
@@ -67,11 +74,18 @@ const createHelperCanvas = ({ canvasWidth, canvasHeight, }) => {
67
74
  // Enable blending for transparency
68
75
  gl.enable(gl.BLEND);
69
76
  gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
70
- helperCanvas = { canvas, gl, program };
77
+ helperCanvas = { canvas, gl, program, vertexShader, fragmentShader };
71
78
  return helperCanvas;
72
79
  };
73
- export const transformIn3d = ({ canvasWidth, canvasHeight, matrix, sourceCanvas, offsetLeft, offsetTop, }) => {
74
- const { canvas, gl, program } = createHelperCanvas({ canvasWidth, canvasHeight });
80
+ export const transformIn3d = ({ matrix, sourceCanvas, untransformedRect, }) => {
81
+ const rectAfterTransforms = transformDOMRect({
82
+ rect: untransformedRect,
83
+ matrix,
84
+ });
85
+ const { canvas, gl, program } = createHelperCanvas({
86
+ canvasWidth: Math.ceil(rectAfterTransforms.width),
87
+ canvasHeight: Math.ceil(rectAfterTransforms.height),
88
+ });
75
89
  const vertexBuffer = gl.createBuffer();
76
90
  gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
77
91
  // Create a quad (two triangles) with texture coordinates
@@ -79,13 +93,13 @@ export const transformIn3d = ({ canvasWidth, canvasHeight, matrix, sourceCanvas,
79
93
  const vertices = new Float32Array([
80
94
  // Position (x, y) + TexCoord (u, v)
81
95
  // First:
82
- offsetLeft, offsetTop, 0, 0, // bottom-left
83
- canvasWidth + offsetLeft, offsetTop, 1, 0, // bottom-right
84
- offsetLeft, canvasHeight + offsetTop, 0, 1, // top-left
96
+ untransformedRect.x, untransformedRect.y, 0, 0, // bottom-left
97
+ untransformedRect.x + untransformedRect.width, untransformedRect.y, 1, 0, // bottom-right
98
+ untransformedRect.x, untransformedRect.y + untransformedRect.height, 0, 1, // top-left
85
99
  // Second:
86
- offsetLeft, canvasHeight + offsetTop, 0, 1, // top-left
87
- canvasWidth + offsetLeft, offsetTop, 1, 0, // bottom-right
88
- canvasWidth + offsetLeft, canvasHeight + offsetTop, 1, 1, // top-right
100
+ untransformedRect.x, untransformedRect.y + untransformedRect.height, 0, 1, // top-left
101
+ untransformedRect.x + untransformedRect.width, untransformedRect.y, 1, 0, // bottom-right
102
+ untransformedRect.x + untransformedRect.width, untransformedRect.y + untransformedRect.height, 1, 1, // top-right
89
103
  ]);
90
104
  gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
91
105
  const aPosition = gl.getAttribLocation(program, 'aPosition');
@@ -109,7 +123,7 @@ export const transformIn3d = ({ canvasWidth, canvasHeight, matrix, sourceCanvas,
109
123
  // The transform matrix
110
124
  const transformMatrix = matrix.toFloat32Array();
111
125
  const zScale = 1000000000; // By default infinite in chrome
112
- // Create orthographic projection matrix for pixel coordinates
126
+ // Create orthographic projection matrix for pixel coordinates with offset
113
127
  const projectionMatrix = new Float32Array([
114
128
  2 / canvas.width,
115
129
  0,
@@ -123,8 +137,8 @@ export const transformIn3d = ({ canvasWidth, canvasHeight, matrix, sourceCanvas,
123
137
  0,
124
138
  -2 / zScale,
125
139
  0,
126
- -1,
127
- 1,
140
+ -1 + (2 * -rectAfterTransforms.x) / canvas.width,
141
+ 1 - (2 * -rectAfterTransforms.y) / canvas.height,
128
142
  0,
129
143
  1,
130
144
  ]);
@@ -138,5 +152,5 @@ export const transformIn3d = ({ canvasWidth, canvasHeight, matrix, sourceCanvas,
138
152
  // Clean up resources to prevent leaks and ensure clean state for reuse
139
153
  gl.deleteTexture(texture);
140
154
  gl.deleteBuffer(vertexBuffer);
141
- return canvas;
155
+ return { canvas, rect: rectAfterTransforms };
142
156
  };
@@ -0,0 +1,4 @@
1
+ export declare function transformDOMRect({ rect, matrix, }: {
2
+ rect: DOMRect;
3
+ matrix: DOMMatrix;
4
+ }): DOMRect;
@@ -0,0 +1,19 @@
1
+ export function transformDOMRect({ rect, matrix, }) {
2
+ // Get all four corners of the rectangle
3
+ const topLeft = new DOMPointReadOnly(rect.left, rect.top);
4
+ const topRight = new DOMPointReadOnly(rect.right, rect.top);
5
+ const bottomLeft = new DOMPointReadOnly(rect.left, rect.bottom);
6
+ const bottomRight = new DOMPointReadOnly(rect.right, rect.bottom);
7
+ // Transform all corners
8
+ const transformedTopLeft = topLeft.matrixTransform(matrix);
9
+ const transformedTopRight = topRight.matrixTransform(matrix);
10
+ const transformedBottomLeft = bottomLeft.matrixTransform(matrix);
11
+ const transformedBottomRight = bottomRight.matrixTransform(matrix);
12
+ // Find the bounding box of the transformed points
13
+ const minX = Math.min(transformedTopLeft.x / transformedTopLeft.w, transformedTopRight.x / transformedTopRight.w, transformedBottomLeft.x / transformedBottomLeft.w, transformedBottomRight.x / transformedBottomRight.w);
14
+ const maxX = Math.max(transformedTopLeft.x / transformedTopLeft.w, transformedTopRight.x / transformedTopRight.w, transformedBottomLeft.x / transformedBottomLeft.w, transformedBottomRight.x / transformedBottomRight.w);
15
+ const minY = Math.min(transformedTopLeft.y / transformedTopLeft.w, transformedTopRight.y / transformedTopRight.w, transformedBottomLeft.y / transformedBottomLeft.w, transformedBottomRight.y / transformedBottomRight.w);
16
+ const maxY = Math.max(transformedTopLeft.y / transformedTopLeft.w, transformedTopRight.y / transformedTopRight.w, transformedBottomLeft.y / transformedBottomLeft.w, transformedBottomRight.y / transformedBottomRight.w);
17
+ // Create a new DOMRect from the bounding box
18
+ return new DOMRect(minX, minY, maxX - minX, maxY - minY);
19
+ }
@@ -1,10 +1,13 @@
1
1
  export const turnSvgIntoDrawable = (svg) => {
2
+ const { fill, color } = getComputedStyle(svg);
2
3
  const originalTransform = svg.style.transform;
3
4
  const originalTransformOrigin = svg.style.transformOrigin;
4
5
  const originalMarginLeft = svg.style.marginLeft;
5
6
  const originalMarginRight = svg.style.marginRight;
6
7
  const originalMarginTop = svg.style.marginTop;
7
8
  const originalMarginBottom = svg.style.marginBottom;
9
+ const originalFill = svg.style.fill;
10
+ const originalColor = svg.style.color;
8
11
  svg.style.transform = 'none';
9
12
  svg.style.transformOrigin = '';
10
13
  // Margins were already included in the positioning calculation,
@@ -13,6 +16,8 @@ export const turnSvgIntoDrawable = (svg) => {
13
16
  svg.style.marginRight = '0';
14
17
  svg.style.marginTop = '0';
15
18
  svg.style.marginBottom = '0';
19
+ svg.style.fill = fill;
20
+ svg.style.color = color;
16
21
  const svgData = new XMLSerializer().serializeToString(svg);
17
22
  svg.style.marginLeft = originalMarginLeft;
18
23
  svg.style.marginRight = originalMarginRight;
@@ -20,6 +25,8 @@ export const turnSvgIntoDrawable = (svg) => {
20
25
  svg.style.marginBottom = originalMarginBottom;
21
26
  svg.style.transform = originalTransform;
22
27
  svg.style.transformOrigin = originalTransformOrigin;
28
+ svg.style.fill = originalFill;
29
+ svg.style.color = originalColor;
23
30
  return new Promise((resolve, reject) => {
24
31
  const image = new Image();
25
32
  const url = `data:image/svg+xml;base64,${btoa(svgData)}`;