@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.
- package/dist/border-radius.d.ts +31 -0
- package/dist/border-radius.js +152 -0
- package/dist/calculate-transforms.d.ts +2 -0
- package/dist/calculate-transforms.js +17 -0
- package/dist/composable.d.ts +2 -8
- package/dist/compose-canvas.js +28 -4
- package/dist/compose.d.ts +4 -3
- package/dist/compose.js +45 -12
- package/dist/create-scaffold.js +10 -3
- package/dist/drawing/border-radius.d.ts +3 -5
- package/dist/drawing/border-radius.js +12 -34
- package/dist/drawing/calculate-transforms.d.ts +12 -2
- package/dist/drawing/calculate-transforms.js +38 -19
- package/dist/drawing/canvas-offset-from-rect.d.ts +8 -0
- package/dist/drawing/canvas-offset-from-rect.js +12 -0
- package/dist/drawing/clamp-rect-to-parent-bounds.d.ts +8 -0
- package/dist/drawing/clamp-rect-to-parent-bounds.js +18 -0
- package/dist/drawing/compose-canvas.d.ts +1 -0
- package/dist/drawing/compose-canvas.js +36 -0
- package/dist/drawing/compose-svg.d.ts +1 -0
- package/dist/drawing/compose-svg.js +34 -0
- package/dist/drawing/compose.d.ts +5 -0
- package/dist/drawing/compose.js +6 -0
- package/dist/drawing/do-rects-intersect.d.ts +1 -0
- package/dist/drawing/do-rects-intersect.js +6 -0
- package/dist/drawing/draw-border.d.ts +2 -5
- package/dist/drawing/draw-border.js +37 -37
- package/dist/drawing/draw-box-shadow.d.ts +18 -0
- package/dist/drawing/draw-box-shadow.js +103 -0
- package/dist/drawing/draw-element-to-canvas.d.ts +2 -1
- package/dist/drawing/draw-element-to-canvas.js +8 -36
- package/dist/drawing/draw-element.d.ts +8 -3
- package/dist/drawing/draw-element.js +64 -18
- package/dist/drawing/draw-outline.d.ts +9 -0
- package/dist/drawing/draw-outline.js +93 -0
- package/dist/drawing/draw-rounded.d.ts +9 -0
- package/dist/drawing/draw-rounded.js +34 -0
- package/dist/drawing/get-bounding-box-including-shadow.d.ts +1 -0
- package/dist/drawing/get-bounding-box-including-shadow.js +6 -0
- package/dist/drawing/get-computed-style-cache.d.ts +0 -0
- package/dist/drawing/get-computed-style-cache.js +1 -0
- package/dist/drawing/get-pretransform-rect.d.ts +1 -0
- package/dist/drawing/get-pretransform-rect.js +36 -0
- package/dist/drawing/handle-3d-transform.d.ts +11 -0
- package/dist/drawing/handle-3d-transform.js +25 -0
- package/dist/drawing/handle-mask.d.ts +8 -0
- package/dist/drawing/handle-mask.js +19 -0
- package/dist/drawing/has-transform.d.ts +4 -0
- package/dist/drawing/has-transform.js +14 -0
- package/dist/drawing/mask-image.d.ts +3 -0
- package/dist/drawing/mask-image.js +14 -0
- package/dist/drawing/overflow.d.ts +7 -0
- package/dist/drawing/overflow.js +12 -0
- package/dist/drawing/parse-linear-gradient.d.ts +14 -0
- package/dist/drawing/parse-linear-gradient.js +260 -0
- package/dist/drawing/precompose.d.ts +11 -0
- package/dist/drawing/precompose.js +13 -0
- package/dist/drawing/process-node.d.ts +18 -0
- package/dist/drawing/process-node.js +116 -0
- package/dist/drawing/round-to-expand-rect.d.ts +1 -0
- package/dist/drawing/round-to-expand-rect.js +7 -0
- package/dist/drawing/text/draw-text.d.ts +5 -1
- package/dist/drawing/text/draw-text.js +10 -5
- package/dist/drawing/text/find-line-breaks.text.d.ts +1 -1
- package/dist/drawing/text/find-line-breaks.text.js +2 -2
- package/dist/drawing/text/handle-text-node.d.ts +7 -5
- package/dist/drawing/text/handle-text-node.js +7 -6
- package/dist/drawing/transform-in-3d.d.ts +9 -7
- package/dist/drawing/transform-in-3d.js +41 -31
- package/dist/drawing/transform-rect-with-matrix.d.ts +4 -0
- package/dist/drawing/transform-rect-with-matrix.js +19 -0
- package/dist/drawing/transform.d.ts +2 -1
- package/dist/drawing/transform.js +6 -2
- package/dist/esm/index.mjs +1138 -278
- package/dist/find-canvas-elements.d.ts +1 -0
- package/dist/find-canvas-elements.js +13 -0
- package/dist/find-capturable-elements.d.ts +1 -1
- package/dist/find-capturable-elements.js +20 -22
- package/dist/get-biggest-bounding-client-rect.js +29 -4
- package/dist/internal-state.d.ts +9 -0
- package/dist/internal-state.js +12 -0
- package/dist/opacity.d.ts +4 -0
- package/dist/opacity.js +7 -0
- package/dist/render-media-on-web.d.ts +3 -0
- package/dist/render-media-on-web.js +27 -14
- package/dist/render-still-on-web.d.ts +5 -1
- package/dist/render-still-on-web.js +4 -1
- package/dist/take-screenshot.d.ts +5 -2
- package/dist/take-screenshot.js +16 -4
- package/dist/transform.d.ts +4 -0
- package/dist/transform.js +6 -0
- package/package.json +6 -6
- package/dist/drawing/text/get-base-height.d.ts +0 -1
- 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;
|
|
@@ -1,2 +1,6 @@
|
|
|
1
|
+
import type { LogLevel } from 'remotion';
|
|
1
2
|
import type { DrawFn } from '../drawn-fn';
|
|
2
|
-
export declare const drawText: (span
|
|
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
|
|
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
|
|
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
|
-
|
|
36
|
-
|
|
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();
|
|
@@ -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
|
-
|
|
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
|
-
|
|
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 {
|
|
3
|
-
|
|
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
|
-
|
|
8
|
+
parentRect: DOMRect;
|
|
9
|
+
internalState: InternalState;
|
|
10
|
+
rootElement: HTMLElement | SVGElement;
|
|
11
|
+
}) => Promise<ProcessNodeReturnValue>;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { processNode } from '../process-node';
|
|
2
2
|
import { drawText } from './draw-text';
|
|
3
|
-
export const handleTextNode = async ({ node, context,
|
|
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
|
|
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: ({
|
|
2
|
-
|
|
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:
|
|
8
|
-
|
|
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 -
|
|
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
|
-
|
|
77
|
-
return helperCanvas;
|
|
63
|
+
return { canvas, gl, program, vertexShader, fragmentShader };
|
|
78
64
|
};
|
|
79
|
-
export const transformIn3d = ({
|
|
80
|
-
const { canvas, gl, program } = createHelperCanvas({
|
|
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
|
-
|
|
89
|
-
|
|
90
|
-
|
|
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
|
-
|
|
93
|
-
|
|
94
|
-
|
|
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
|
-
|
|
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,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,5 +1,9 @@
|
|
|
1
|
-
export const setTransform = ({ ctx, transform, }) => {
|
|
2
|
-
|
|
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
|
};
|