@remotion/web-renderer 4.0.395 → 4.0.397

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 (94) 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 +4 -3
  8. package/dist/compose.js +45 -12
  9. package/dist/create-scaffold.js +10 -3
  10. package/dist/drawing/border-radius.d.ts +3 -5
  11. package/dist/drawing/border-radius.js +12 -34
  12. package/dist/drawing/calculate-transforms.d.ts +12 -2
  13. package/dist/drawing/calculate-transforms.js +38 -19
  14. package/dist/drawing/canvas-offset-from-rect.d.ts +8 -0
  15. package/dist/drawing/canvas-offset-from-rect.js +12 -0
  16. package/dist/drawing/clamp-rect-to-parent-bounds.d.ts +8 -0
  17. package/dist/drawing/clamp-rect-to-parent-bounds.js +18 -0
  18. package/dist/drawing/compose-canvas.d.ts +1 -0
  19. package/dist/drawing/compose-canvas.js +36 -0
  20. package/dist/drawing/compose-svg.d.ts +1 -0
  21. package/dist/drawing/compose-svg.js +34 -0
  22. package/dist/drawing/compose.d.ts +5 -0
  23. package/dist/drawing/compose.js +6 -0
  24. package/dist/drawing/do-rects-intersect.d.ts +1 -0
  25. package/dist/drawing/do-rects-intersect.js +6 -0
  26. package/dist/drawing/draw-border.d.ts +2 -5
  27. package/dist/drawing/draw-border.js +37 -37
  28. package/dist/drawing/draw-box-shadow.d.ts +18 -0
  29. package/dist/drawing/draw-box-shadow.js +103 -0
  30. package/dist/drawing/draw-element-to-canvas.d.ts +2 -1
  31. package/dist/drawing/draw-element-to-canvas.js +8 -36
  32. package/dist/drawing/draw-element.d.ts +8 -3
  33. package/dist/drawing/draw-element.js +64 -18
  34. package/dist/drawing/draw-outline.d.ts +9 -0
  35. package/dist/drawing/draw-outline.js +93 -0
  36. package/dist/drawing/draw-rounded.d.ts +9 -0
  37. package/dist/drawing/draw-rounded.js +34 -0
  38. package/dist/drawing/get-bounding-box-including-shadow.d.ts +1 -0
  39. package/dist/drawing/get-bounding-box-including-shadow.js +6 -0
  40. package/dist/drawing/get-computed-style-cache.d.ts +0 -0
  41. package/dist/drawing/get-computed-style-cache.js +1 -0
  42. package/dist/drawing/get-pretransform-rect.d.ts +1 -0
  43. package/dist/drawing/get-pretransform-rect.js +36 -0
  44. package/dist/drawing/handle-3d-transform.d.ts +11 -0
  45. package/dist/drawing/handle-3d-transform.js +25 -0
  46. package/dist/drawing/handle-mask.d.ts +8 -0
  47. package/dist/drawing/handle-mask.js +19 -0
  48. package/dist/drawing/has-transform.d.ts +4 -0
  49. package/dist/drawing/has-transform.js +14 -0
  50. package/dist/drawing/mask-image.d.ts +3 -0
  51. package/dist/drawing/mask-image.js +14 -0
  52. package/dist/drawing/overflow.d.ts +7 -0
  53. package/dist/drawing/overflow.js +12 -0
  54. package/dist/drawing/parse-linear-gradient.d.ts +14 -0
  55. package/dist/drawing/parse-linear-gradient.js +260 -0
  56. package/dist/drawing/precompose.d.ts +11 -0
  57. package/dist/drawing/precompose.js +13 -0
  58. package/dist/drawing/process-node.d.ts +18 -0
  59. package/dist/drawing/process-node.js +116 -0
  60. package/dist/drawing/round-to-expand-rect.d.ts +1 -0
  61. package/dist/drawing/round-to-expand-rect.js +7 -0
  62. package/dist/drawing/text/draw-text.d.ts +5 -1
  63. package/dist/drawing/text/draw-text.js +10 -5
  64. package/dist/drawing/text/find-line-breaks.text.d.ts +1 -1
  65. package/dist/drawing/text/find-line-breaks.text.js +2 -2
  66. package/dist/drawing/text/handle-text-node.d.ts +7 -5
  67. package/dist/drawing/text/handle-text-node.js +7 -6
  68. package/dist/drawing/transform-in-3d.d.ts +9 -7
  69. package/dist/drawing/transform-in-3d.js +41 -31
  70. package/dist/drawing/transform-rect-with-matrix.d.ts +4 -0
  71. package/dist/drawing/transform-rect-with-matrix.js +19 -0
  72. package/dist/drawing/transform.d.ts +2 -1
  73. package/dist/drawing/transform.js +6 -2
  74. package/dist/esm/index.mjs +1138 -278
  75. package/dist/find-canvas-elements.d.ts +1 -0
  76. package/dist/find-canvas-elements.js +13 -0
  77. package/dist/find-capturable-elements.d.ts +1 -1
  78. package/dist/find-capturable-elements.js +20 -22
  79. package/dist/get-biggest-bounding-client-rect.js +29 -4
  80. package/dist/internal-state.d.ts +9 -0
  81. package/dist/internal-state.js +12 -0
  82. package/dist/opacity.d.ts +4 -0
  83. package/dist/opacity.js +7 -0
  84. package/dist/render-media-on-web.d.ts +3 -0
  85. package/dist/render-media-on-web.js +27 -14
  86. package/dist/render-still-on-web.d.ts +5 -1
  87. package/dist/render-still-on-web.js +4 -1
  88. package/dist/take-screenshot.d.ts +5 -2
  89. package/dist/take-screenshot.js +16 -4
  90. package/dist/transform.d.ts +4 -0
  91. package/dist/transform.js +6 -0
  92. package/package.json +6 -6
  93. package/dist/drawing/text/get-base-height.d.ts +0 -1
  94. package/dist/drawing/text/get-base-height.js +0 -13
@@ -0,0 +1,116 @@
1
+ import { Internals } from 'remotion';
2
+ import { calculateTransforms } from './calculate-transforms';
3
+ import { getWiderRectAndExpand } from './clamp-rect-to-parent-bounds';
4
+ import { doRectsIntersect } from './do-rects-intersect';
5
+ import { drawElement } from './draw-element';
6
+ import { getPrecomposeRectFor3DTransform, handle3dTransform, } from './handle-3d-transform';
7
+ import { getPrecomposeRectForMask, handleMask } from './handle-mask';
8
+ import { precomposeDOMElement } from './precompose';
9
+ import { roundToExpandRect } from './round-to-expand-rect';
10
+ import { transformDOMRect } from './transform-rect-with-matrix';
11
+ export const processNode = async ({ element, context, draw, logLevel, parentRect, internalState, rootElement, }) => {
12
+ const { totalMatrix, reset, dimensions, opacity, computedStyle, precompositing, } = calculateTransforms({
13
+ element,
14
+ rootElement,
15
+ });
16
+ if (opacity === 0) {
17
+ reset();
18
+ return { type: 'skip-children' };
19
+ }
20
+ if (dimensions.width <= 0 || dimensions.height <= 0) {
21
+ reset();
22
+ return { type: 'continue', cleanupAfterChildren: null };
23
+ }
24
+ const rect = new DOMRect(dimensions.left - parentRect.x, dimensions.top - parentRect.y, dimensions.width, dimensions.height);
25
+ if (precompositing.needsPrecompositing) {
26
+ const start = Date.now();
27
+ let precomposeRect = null;
28
+ if (precompositing.needsMaskImage) {
29
+ precomposeRect = getWiderRectAndExpand({
30
+ firstRect: precomposeRect,
31
+ secondRect: getPrecomposeRectForMask(element),
32
+ });
33
+ }
34
+ if (precompositing.needs3DTransformViaWebGL) {
35
+ precomposeRect = getWiderRectAndExpand({
36
+ firstRect: precomposeRect,
37
+ secondRect: getPrecomposeRectFor3DTransform({
38
+ element,
39
+ parentRect,
40
+ matrix: totalMatrix,
41
+ }),
42
+ });
43
+ }
44
+ if (!precomposeRect) {
45
+ throw new Error('Precompose rect not found');
46
+ }
47
+ if (precomposeRect.width <= 0 || precomposeRect.height <= 0) {
48
+ return { type: 'continue', cleanupAfterChildren: null };
49
+ }
50
+ if (!doRectsIntersect(precomposeRect, parentRect)) {
51
+ return { type: 'continue', cleanupAfterChildren: null };
52
+ }
53
+ const { tempCanvas, tempContext } = await precomposeDOMElement({
54
+ boundingRect: precomposeRect,
55
+ element,
56
+ logLevel,
57
+ internalState,
58
+ });
59
+ let drawable = tempCanvas;
60
+ let cleanupWebGL = () => { };
61
+ const rectAfterTransforms = roundToExpandRect(transformDOMRect({
62
+ rect: precomposeRect,
63
+ matrix: totalMatrix,
64
+ }));
65
+ if (precompositing.needsMaskImage) {
66
+ handleMask({
67
+ gradientInfo: precompositing.needsMaskImage,
68
+ rect,
69
+ precomposeRect,
70
+ tempContext,
71
+ });
72
+ }
73
+ if (precompositing.needs3DTransformViaWebGL) {
74
+ const t = handle3dTransform({
75
+ matrix: totalMatrix,
76
+ precomposeRect,
77
+ tempCanvas: drawable,
78
+ rectAfterTransforms,
79
+ });
80
+ if (t) {
81
+ const [transformed, cleanup] = t;
82
+ drawable = transformed;
83
+ cleanupWebGL = cleanup;
84
+ }
85
+ }
86
+ const previousTransform = context.getTransform();
87
+ if (drawable) {
88
+ context.setTransform(new DOMMatrix());
89
+ context.drawImage(drawable, rectAfterTransforms.left - parentRect.x, rectAfterTransforms.top - parentRect.y, rectAfterTransforms.width, rectAfterTransforms.height);
90
+ context.setTransform(previousTransform);
91
+ Internals.Log.trace({
92
+ logLevel,
93
+ tag: '@remotion/web-renderer',
94
+ }, `Transforming element in 3D - canvas size: ${precomposeRect.width}x${precomposeRect.height} - compose: ${Date.now() - start}ms`);
95
+ internalState.addPrecompose({
96
+ canvasWidth: precomposeRect.width,
97
+ canvasHeight: precomposeRect.height,
98
+ });
99
+ }
100
+ reset();
101
+ cleanupWebGL();
102
+ return { type: 'skip-children' };
103
+ }
104
+ const { cleanupAfterChildren } = await drawElement({
105
+ rect,
106
+ computedStyle,
107
+ context,
108
+ draw,
109
+ opacity,
110
+ totalMatrix,
111
+ parentRect,
112
+ logLevel,
113
+ });
114
+ reset();
115
+ return { type: 'continue', cleanupAfterChildren };
116
+ };
@@ -0,0 +1 @@
1
+ export declare const roundToExpandRect: (rect: DOMRect) => DOMRect;
@@ -0,0 +1,7 @@
1
+ export const roundToExpandRect = (rect) => {
2
+ const left = Math.floor(rect.left);
3
+ const top = Math.floor(rect.top);
4
+ const right = Math.ceil(rect.right);
5
+ const bottom = Math.ceil(rect.bottom);
6
+ return new DOMRect(left, top, right - left, bottom - top);
7
+ };
@@ -1,2 +1,6 @@
1
+ import type { LogLevel } from 'remotion';
1
2
  import type { DrawFn } from '../drawn-fn';
2
- export declare const drawText: (span: HTMLSpanElement) => DrawFn;
3
+ export declare const drawText: ({ span, logLevel, }: {
4
+ span: HTMLSpanElement;
5
+ logLevel: LogLevel;
6
+ }) => DrawFn;
@@ -2,14 +2,14 @@ import { Internals } from 'remotion';
2
2
  import { applyTextTransform } from './apply-text-transform';
3
3
  import { findLineBreaks } from './find-line-breaks.text';
4
4
  import { getCollapsedText } from './get-collapsed-text';
5
- export const drawText = (span) => {
5
+ export const drawText = ({ span, logLevel, }) => {
6
6
  const drawFn = ({ dimensions: rect, computedStyle, contextToDraw }) => {
7
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.
11
11
  Internals.Log.warn({
12
- logLevel: 'warn',
12
+ logLevel,
13
13
  tag: '@remotion/web-renderer',
14
14
  }, 'Detected "writing-mode" CSS property. Vertical text is not yet supported in @remotion/web-renderer');
15
15
  return;
@@ -30,10 +30,15 @@ export const drawText = (span) => {
30
30
  const xPosition = isRTL ? rect.right : rect.left;
31
31
  const lines = findLineBreaks(span, isRTL);
32
32
  let offsetTop = 0;
33
- const { fontBoundingBoxAscent } = contextToDraw.measureText(lines[0].text);
33
+ const measurements = contextToDraw.measureText(lines[0].text);
34
+ const { fontBoundingBoxDescent, fontBoundingBoxAscent } = measurements;
35
+ const fontHeight = fontBoundingBoxAscent + fontBoundingBoxDescent;
34
36
  for (const line of lines) {
35
- contextToDraw.fillText(line.text, xPosition + line.offsetHorizontal, rect.top + offsetTop + fontBoundingBoxAscent);
36
- offsetTop += line.offsetTop;
37
+ // Calculate leading
38
+ const leading = line.height - fontHeight;
39
+ const halfLeading = leading / 2;
40
+ contextToDraw.fillText(line.text, xPosition + line.offsetHorizontal, rect.top + halfLeading + fontBoundingBoxAscent + offsetTop);
41
+ offsetTop += line.height;
37
42
  }
38
43
  span.textContent = originalText;
39
44
  contextToDraw.restore();
@@ -1,5 +1,5 @@
1
1
  export declare function findLineBreaks(span: HTMLSpanElement, rtl: boolean): Array<{
2
2
  text: string;
3
- offsetTop: number;
3
+ height: number;
4
4
  offsetHorizontal: number;
5
5
  }>;
@@ -37,7 +37,7 @@ export function findLineBreaks(span, rtl) {
37
37
  const shouldCollapse = !computedStyle.whiteSpaceCollapse.includes('preserve');
38
38
  lines.push({
39
39
  text: shouldCollapse ? currentLine.trim() : currentLine,
40
- offsetTop: currentHeight - previousRect.height,
40
+ height: currentHeight - previousRect.height,
41
41
  offsetHorizontal,
42
42
  });
43
43
  currentLine = wordsToAdd;
@@ -57,7 +57,7 @@ export function findLineBreaks(span, rtl) {
57
57
  : lastRect.left - originalRect.left;
58
58
  lines.push({
59
59
  text: currentLine,
60
- offsetTop: rect.height - previousRect.height,
60
+ height: rect.height - lines.reduce((acc, curr) => acc + curr.height, 0),
61
61
  offsetHorizontal,
62
62
  });
63
63
  }
@@ -1,9 +1,11 @@
1
1
  import type { LogLevel } from 'remotion';
2
- import type { DrawElementToCanvasReturnValue } from '../draw-element-to-canvas';
3
- export declare const handleTextNode: ({ node, context, offsetLeft, offsetTop, logLevel, }: {
2
+ import type { InternalState } from '../../internal-state';
3
+ import type { ProcessNodeReturnValue } from '../process-node';
4
+ export declare const handleTextNode: ({ node, context, logLevel, parentRect, internalState, rootElement, }: {
4
5
  node: Text;
5
6
  context: OffscreenCanvasRenderingContext2D;
6
- offsetLeft: number;
7
- offsetTop: number;
8
7
  logLevel: LogLevel;
9
- }) => Promise<DrawElementToCanvasReturnValue>;
8
+ parentRect: DOMRect;
9
+ internalState: InternalState;
10
+ rootElement: HTMLElement | SVGElement;
11
+ }) => 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, logLevel, }) => {
3
+ export const handleTextNode = async ({ node, context, logLevel, parentRect, internalState, rootElement, }) => {
4
4
  const span = document.createElement('span');
5
5
  const parent = node.parentNode;
6
6
  if (!parent) {
@@ -8,13 +8,14 @@ export const handleTextNode = async ({ node, context, offsetLeft, offsetTop, log
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
- draw: drawText(span),
15
- offsetLeft,
16
- offsetTop,
14
+ draw: drawText({ span, logLevel }),
17
15
  logLevel,
16
+ parentRect,
17
+ internalState,
18
+ rootElement,
18
19
  });
19
20
  // Undo the layout manipulation
20
21
  parent.insertBefore(node, span);
@@ -1,8 +1,10 @@
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, rectAfterTransforms, }: {
2
+ untransformedRect: DOMRect;
6
3
  matrix: DOMMatrix;
7
- sourceCanvas: HTMLCanvasElement | OffscreenCanvas;
8
- }) => OffscreenCanvas;
4
+ sourceCanvas: OffscreenCanvas;
5
+ rectAfterTransforms: DOMRect;
6
+ }) => {
7
+ canvas: OffscreenCanvas;
8
+ rect: DOMRect;
9
+ cleanup: () => void;
10
+ };
@@ -12,24 +12,11 @@ function compileShader(shaderGl, source, type) {
12
12
  }
13
13
  return shader;
14
14
  }
15
- let helperCanvas = null;
16
15
  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
- }
25
- if (helperCanvas) {
26
- helperCanvas.gl.deleteProgram(helperCanvas.program);
27
- helperCanvas.gl.deleteShader(helperCanvas.vertexShader);
28
- helperCanvas.gl.deleteShader(helperCanvas.fragmentShader);
29
- helperCanvas = null;
30
- }
31
16
  const canvas = new OffscreenCanvas(canvasWidth, canvasHeight);
32
- const gl = canvas.getContext('webgl');
17
+ const gl = canvas.getContext('webgl', {
18
+ premultipliedAlpha: true,
19
+ });
33
20
  if (!gl) {
34
21
  throw new Error('WebGL not supported');
35
22
  }
@@ -46,7 +33,7 @@ const createHelperCanvas = ({ canvasWidth, canvasHeight, }) => {
46
33
  vTexCoord = aTexCoord;
47
34
  }
48
35
  `;
49
- // Fragment shader - now samples from texture
36
+ // Fragment shader - samples from texture and unpremultiplies alpha
50
37
  const fsSource = `
51
38
  precision mediump float;
52
39
  uniform sampler2D uTexture;
@@ -73,11 +60,13 @@ const createHelperCanvas = ({ canvasWidth, canvasHeight, }) => {
73
60
  // Enable blending for transparency
74
61
  gl.enable(gl.BLEND);
75
62
  gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
76
- helperCanvas = { canvas, gl, program, vertexShader, fragmentShader };
77
- return helperCanvas;
63
+ return { canvas, gl, program, vertexShader, fragmentShader };
78
64
  };
79
- export const transformIn3d = ({ canvasWidth, canvasHeight, matrix, sourceCanvas, offsetLeft, offsetTop, }) => {
80
- const { canvas, gl, program } = createHelperCanvas({ canvasWidth, canvasHeight });
65
+ export const transformIn3d = ({ matrix, sourceCanvas, untransformedRect, rectAfterTransforms, }) => {
66
+ const { canvas, gl, program } = createHelperCanvas({
67
+ canvasWidth: rectAfterTransforms.width,
68
+ canvasHeight: rectAfterTransforms.height,
69
+ });
81
70
  const vertexBuffer = gl.createBuffer();
82
71
  gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
83
72
  // Create a quad (two triangles) with texture coordinates
@@ -85,13 +74,13 @@ export const transformIn3d = ({ canvasWidth, canvasHeight, matrix, sourceCanvas,
85
74
  const vertices = new Float32Array([
86
75
  // Position (x, y) + TexCoord (u, v)
87
76
  // First:
88
- offsetLeft, offsetTop, 0, 0, // bottom-left
89
- canvasWidth + offsetLeft, offsetTop, 1, 0, // bottom-right
90
- offsetLeft, canvasHeight + offsetTop, 0, 1, // top-left
77
+ untransformedRect.x, untransformedRect.y, 0, 0, // bottom-left
78
+ untransformedRect.x + untransformedRect.width, untransformedRect.y, 1, 0, // bottom-right
79
+ untransformedRect.x, untransformedRect.y + untransformedRect.height, 0, 1, // top-left
91
80
  // Second:
92
- offsetLeft, canvasHeight + offsetTop, 0, 1, // top-left
93
- canvasWidth + offsetLeft, offsetTop, 1, 0, // bottom-right
94
- canvasWidth + offsetLeft, canvasHeight + offsetTop, 1, 1, // top-right
81
+ untransformedRect.x, untransformedRect.y + untransformedRect.height, 0, 1, // top-left
82
+ untransformedRect.x + untransformedRect.width, untransformedRect.y, 1, 0, // bottom-right
83
+ untransformedRect.x + untransformedRect.width, untransformedRect.y + untransformedRect.height, 1, 1, // top-right
95
84
  ]);
96
85
  gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
97
86
  const aPosition = gl.getAttribLocation(program, 'aPosition');
@@ -110,12 +99,15 @@ export const transformIn3d = ({ canvasWidth, canvasHeight, matrix, sourceCanvas,
110
99
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
111
100
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
112
101
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
102
+ gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true);
113
103
  // Upload the source canvas as a texture
114
104
  gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, sourceCanvas);
105
+ gl.enable(gl.BLEND);
106
+ gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
115
107
  // The transform matrix
116
108
  const transformMatrix = matrix.toFloat32Array();
117
109
  const zScale = 1000000000; // By default infinite in chrome
118
- // Create orthographic projection matrix for pixel coordinates
110
+ // Create orthographic projection matrix for pixel coordinates with offset
119
111
  const projectionMatrix = new Float32Array([
120
112
  2 / canvas.width,
121
113
  0,
@@ -129,8 +121,8 @@ export const transformIn3d = ({ canvasWidth, canvasHeight, matrix, sourceCanvas,
129
121
  0,
130
122
  -2 / zScale,
131
123
  0,
132
- -1,
133
- 1,
124
+ -1 + (2 * -rectAfterTransforms.x) / canvas.width,
125
+ 1 - (2 * -rectAfterTransforms.y) / canvas.height,
134
126
  0,
135
127
  1,
136
128
  ]);
@@ -142,7 +134,25 @@ export const transformIn3d = ({ canvasWidth, canvasHeight, matrix, sourceCanvas,
142
134
  gl.uniform1i(uTexture, 0); // Use texture unit 0
143
135
  gl.drawArrays(gl.TRIANGLES, 0, 6);
144
136
  // Clean up resources to prevent leaks and ensure clean state for reuse
137
+ gl.disableVertexAttribArray(aPosition);
138
+ gl.disableVertexAttribArray(aTexCoord);
139
+ // Clean up resources to prevent leaks and ensure clean state for reuse
145
140
  gl.deleteTexture(texture);
146
141
  gl.deleteBuffer(vertexBuffer);
147
- return canvas;
142
+ gl.bindTexture(gl.TEXTURE_2D, null);
143
+ gl.deleteTexture(texture);
144
+ // Reset pixel store state
145
+ gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);
146
+ // Reset blend function to the initial state
147
+ gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
148
+ return {
149
+ canvas,
150
+ rect: rectAfterTransforms,
151
+ cleanup: () => {
152
+ const loseContext = gl.getExtension('WEBGL_lose_context');
153
+ if (loseContext) {
154
+ loseContext.loseContext();
155
+ }
156
+ },
157
+ };
148
158
  };
@@ -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,4 +1,5 @@
1
- export declare const setTransform: ({ ctx, transform, }: {
1
+ export declare const setTransform: ({ ctx, transform, parentRect, }: {
2
2
  ctx: OffscreenCanvasRenderingContext2D;
3
3
  transform: DOMMatrix;
4
+ parentRect: DOMRect;
4
5
  }) => () => void;
@@ -1,5 +1,9 @@
1
- export const setTransform = ({ ctx, transform, }) => {
2
- ctx.setTransform(transform);
1
+ export const setTransform = ({ ctx, transform, parentRect, }) => {
2
+ const offsetMatrix = new DOMMatrix()
3
+ .translate(-parentRect.x, -parentRect.y)
4
+ .multiply(transform)
5
+ .translate(parentRect.x, parentRect.y);
6
+ ctx.setTransform(offsetMatrix);
3
7
  return () => {
4
8
  ctx.setTransform(new DOMMatrix());
5
9
  };