@remotion/effects 4.0.541 → 4.0.543
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/THIRD_PARTY_LICENSES.md +10 -0
- package/dist/esm/lightning.mjs +603 -0
- package/dist/esm/magnify.mjs +403 -0
- package/dist/esm/preview/barrel-distortion.mjs +303 -0
- package/dist/esm/preview/bevel-alpha.mjs +525 -0
- package/dist/esm/preview/blur.mjs +435 -0
- package/dist/esm/preview/brightness.mjs +95 -0
- package/dist/esm/preview/burlap.mjs +481 -0
- package/dist/esm/preview/checkerboard.mjs +8 -0
- package/dist/esm/preview/chromatic-aberration.mjs +308 -0
- package/dist/esm/preview/color-correction.mjs +522 -0
- package/dist/esm/preview/color-key.mjs +440 -0
- package/dist/esm/preview/contour-lines.mjs +608 -0
- package/dist/esm/preview/contrast.mjs +95 -0
- package/dist/esm/preview/corner-pin.mjs +6 -0
- package/dist/esm/preview/curves.mjs +357 -0
- package/dist/esm/preview/cylinder.mjs +273 -0
- package/dist/esm/preview/dot-grid.mjs +333 -0
- package/dist/esm/preview/drop-shadow.mjs +607 -0
- package/dist/esm/preview/duotone.mjs +344 -0
- package/dist/esm/preview/emboss.mjs +479 -0
- package/dist/esm/preview/evolve.mjs +485 -0
- package/dist/esm/preview/exposure.mjs +264 -0
- package/dist/esm/preview/film-burn.mjs +298 -0
- package/dist/esm/preview/fisheye.mjs +422 -0
- package/dist/esm/preview/flannel.mjs +401 -0
- package/dist/esm/preview/fractal-noise.mjs +642 -0
- package/dist/esm/preview/glow.mjs +598 -0
- package/dist/esm/preview/grayscale.mjs +86 -0
- package/dist/esm/preview/gridlines.mjs +8 -0
- package/dist/esm/preview/halftone-linear-gradient.mjs +523 -0
- package/dist/esm/preview/halftone.mjs +552 -0
- package/dist/esm/preview/hue.mjs +83 -0
- package/dist/esm/preview/index-00v3hr66.mjs +299 -0
- package/dist/esm/preview/index-0w4eqht8.mjs +351 -0
- package/dist/esm/preview/index-18y0zcy2.mjs +440 -0
- package/dist/esm/preview/index-1m1b1ad7.mjs +520 -0
- package/dist/esm/preview/index-34fxm3ax.mjs +518 -0
- package/dist/esm/preview/index-4fvp93ps.mjs +32 -0
- package/dist/esm/preview/index-4scpn12x.mjs +224 -0
- package/dist/esm/preview/index-4z6pt3ka.mjs +104 -0
- package/dist/esm/preview/index-618ky75w.mjs +51 -0
- package/dist/esm/preview/index-6agxdrvr.mjs +411 -0
- package/dist/esm/preview/index-6jkps947.mjs +120 -0
- package/dist/esm/preview/index-8agtsbw4.mjs +617 -0
- package/dist/esm/preview/index-8jx11jyn.mjs +29 -0
- package/dist/esm/preview/index-8p04sssx.mjs +123 -0
- package/dist/esm/preview/index-afdtmrh2.mjs +596 -0
- package/dist/esm/preview/index-b86qstcs.mjs +18 -0
- package/dist/esm/preview/index-cb77qg3g.mjs +587 -0
- package/dist/esm/preview/index-cdp7tr8x.mjs +122 -0
- package/dist/esm/preview/index-evy8hyah.mjs +55 -0
- package/dist/esm/preview/index-gnsce796.mjs +6 -0
- package/dist/esm/preview/index-kkvzt8b7.mjs +199 -0
- package/dist/esm/preview/index-r0v18b2n.mjs +425 -0
- package/dist/esm/preview/index-rswpzmm5.mjs +926 -0
- package/dist/esm/preview/index-s9mrx750.mjs +97 -0
- package/dist/esm/preview/index-tvfexb7s.mjs +164 -0
- package/dist/esm/preview/index-vp56k1gj.mjs +215 -0
- package/dist/esm/preview/index-wxhvgv2s.mjs +79 -0
- package/dist/esm/preview/index-wy95a41r.mjs +407 -0
- package/dist/esm/preview/index-ysnwfc4t.mjs +306 -0
- package/dist/esm/preview/index.mjs +51 -0
- package/dist/esm/preview/invert.mjs +84 -0
- package/dist/esm/preview/iris-wipe.mjs +315 -0
- package/dist/esm/preview/lens-blur.mjs +563 -0
- package/dist/esm/preview/levels.mjs +400 -0
- package/dist/esm/preview/light-leak.mjs +8 -0
- package/dist/esm/preview/light-trail.mjs +516 -0
- package/dist/esm/preview/lightning.mjs +603 -0
- package/dist/esm/preview/linear-gradient-tint.mjs +6 -0
- package/dist/esm/preview/linear-gradient.mjs +6 -0
- package/dist/esm/preview/linear-progressive-blur.mjs +516 -0
- package/dist/esm/preview/linear-progressive-pixelate.mjs +176 -0
- package/dist/esm/preview/lines.mjs +536 -0
- package/dist/esm/preview/liquid-contours.mjs +512 -0
- package/dist/esm/preview/long-shadows.mjs +437 -0
- package/dist/esm/preview/lut.mjs +601 -0
- package/dist/esm/preview/magnify.mjs +403 -0
- package/dist/esm/preview/mirror.mjs +341 -0
- package/dist/esm/preview/noise-displacement.mjs +584 -0
- package/dist/esm/preview/noise.mjs +318 -0
- package/dist/esm/preview/outline.mjs +725 -0
- package/dist/esm/preview/page-turn.mjs +302 -0
- package/dist/esm/preview/paper.mjs +1249 -0
- package/dist/esm/preview/pattern.mjs +6 -0
- package/dist/esm/preview/pixel-dissolve.mjs +360 -0
- package/dist/esm/preview/pixelate.mjs +249 -0
- package/dist/esm/preview/radial-progressive-blur.mjs +582 -0
- package/dist/esm/preview/radial-progressive-pixelate.mjs +220 -0
- package/dist/esm/preview/radial-wipe.mjs +224 -0
- package/dist/esm/preview/rain-fall.mjs +114 -0
- package/dist/esm/preview/region-blur.mjs +500 -0
- package/dist/esm/preview/rings.mjs +8 -0
- package/dist/esm/preview/roughen-edges.mjs +590 -0
- package/dist/esm/preview/saturation.mjs +86 -0
- package/dist/esm/preview/scale.mjs +109 -0
- package/dist/esm/preview/scanlines.mjs +362 -0
- package/dist/esm/preview/shadows-highlights.mjs +284 -0
- package/dist/esm/preview/shatter.mjs +868 -0
- package/dist/esm/preview/shine.mjs +368 -0
- package/dist/esm/preview/shrinkwrap.mjs +769 -0
- package/dist/esm/preview/skew.mjs +300 -0
- package/dist/esm/preview/snow-fall.mjs +170 -0
- package/dist/esm/preview/speckle.mjs +356 -0
- package/dist/esm/preview/sphere.mjs +252 -0
- package/dist/esm/preview/starburst.mjs +8 -0
- package/dist/esm/preview/tear.mjs +375 -0
- package/dist/esm/preview/thermal-vision.mjs +387 -0
- package/dist/esm/preview/tile.mjs +7 -0
- package/dist/esm/preview/tint.mjs +125 -0
- package/dist/esm/preview/translate.mjs +151 -0
- package/dist/esm/preview/turbulent-displace.mjs +613 -0
- package/dist/esm/preview/tv-signal-off.mjs +308 -0
- package/dist/esm/preview/venetian-blinds.mjs +341 -0
- package/dist/esm/preview/vibrance.mjs +254 -0
- package/dist/esm/preview/vignette.mjs +458 -0
- package/dist/esm/preview/wave.mjs +384 -0
- package/dist/esm/preview/waves.mjs +599 -0
- package/dist/esm/preview/white-balance.mjs +280 -0
- package/dist/esm/preview/white-noise.mjs +244 -0
- package/dist/esm/preview/zigzag.mjs +8 -0
- package/dist/esm/preview/zoom-blur.mjs +389 -0
- package/dist/lightning/geometry.d.ts +10 -0
- package/dist/lightning/index.d.ts +4 -0
- package/dist/lightning/mask.d.ts +2 -0
- package/dist/lightning/params.d.ts +259 -0
- package/dist/lightning/shaders.d.ts +2 -0
- package/dist/lightning.d.ts +2 -0
- package/dist/magnify/shader.d.ts +1 -0
- package/dist/magnify/webgpu-shader.d.ts +1 -0
- package/dist/magnify.d.ts +28 -0
- package/package.json +30 -6
|
@@ -0,0 +1,725 @@
|
|
|
1
|
+
import {
|
|
2
|
+
WEBGPU_MULTIPASS_BINDINGS,
|
|
3
|
+
setupWebGpuMultipass,
|
|
4
|
+
cleanupWebGpuMultipass,
|
|
5
|
+
applyWebGpuMultipass
|
|
6
|
+
} from "./index-kkvzt8b7.mjs";
|
|
7
|
+
import {
|
|
8
|
+
assertOptionalFiniteNumber,
|
|
9
|
+
validateUnitInterval,
|
|
10
|
+
validateNonNegative,
|
|
11
|
+
parseColorRgba
|
|
12
|
+
} from "./index-wxhvgv2s.mjs";
|
|
13
|
+
import {
|
|
14
|
+
assertEffectParamsObject,
|
|
15
|
+
assertOptionalColor,
|
|
16
|
+
assertOptionalBoolean
|
|
17
|
+
} from "./index-4fvp93ps.mjs";
|
|
18
|
+
|
|
19
|
+
// src/outline.ts
|
|
20
|
+
import { Internals } from "remotion";
|
|
21
|
+
|
|
22
|
+
// src/outline/polygonize-alpha.ts
|
|
23
|
+
var pointKey = ([x, y]) => `${x},${y}`;
|
|
24
|
+
var edgeKey = (a, b) => a < b ? `${a}|${b}` : `${b}|${a}`;
|
|
25
|
+
var squaredDistance = (a, b) => {
|
|
26
|
+
const dx = a[0] - b[0];
|
|
27
|
+
const dy = a[1] - b[1];
|
|
28
|
+
return dx * dx + dy * dy;
|
|
29
|
+
};
|
|
30
|
+
var squaredSegmentDistance = (point, start, end) => {
|
|
31
|
+
let x = start[0];
|
|
32
|
+
let y = start[1];
|
|
33
|
+
let dx = end[0] - x;
|
|
34
|
+
let dy = end[1] - y;
|
|
35
|
+
if (dx !== 0 || dy !== 0) {
|
|
36
|
+
const progress = ((point[0] - x) * dx + (point[1] - y) * dy) / (dx * dx + dy * dy);
|
|
37
|
+
if (progress > 1) {
|
|
38
|
+
x = end[0];
|
|
39
|
+
y = end[1];
|
|
40
|
+
} else if (progress > 0) {
|
|
41
|
+
x += dx * progress;
|
|
42
|
+
y += dy * progress;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
dx = point[0] - x;
|
|
46
|
+
dy = point[1] - y;
|
|
47
|
+
return dx * dx + dy * dy;
|
|
48
|
+
};
|
|
49
|
+
var simplifyOpenContour = (points, toleranceSquared) => {
|
|
50
|
+
if (points.length <= 2) {
|
|
51
|
+
return [...points];
|
|
52
|
+
}
|
|
53
|
+
const keep = new Uint8Array(points.length);
|
|
54
|
+
keep[0] = 1;
|
|
55
|
+
keep[points.length - 1] = 1;
|
|
56
|
+
const stack = [[0, points.length - 1]];
|
|
57
|
+
while (stack.length > 0) {
|
|
58
|
+
const range = stack.pop();
|
|
59
|
+
if (!range) {
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
const [startIndex, endIndex] = range;
|
|
63
|
+
let furthestIndex = -1;
|
|
64
|
+
let furthestDistance = toleranceSquared;
|
|
65
|
+
for (let index = startIndex + 1;index < endIndex; index++) {
|
|
66
|
+
const distance = squaredSegmentDistance(points[index], points[startIndex], points[endIndex]);
|
|
67
|
+
if (distance > furthestDistance) {
|
|
68
|
+
furthestDistance = distance;
|
|
69
|
+
furthestIndex = index;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
if (furthestIndex !== -1) {
|
|
73
|
+
keep[furthestIndex] = 1;
|
|
74
|
+
stack.push([startIndex, furthestIndex], [furthestIndex, endIndex]);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return points.filter((_, index) => keep[index] === 1);
|
|
78
|
+
};
|
|
79
|
+
var simplifyClosedContour = (points, tolerance) => {
|
|
80
|
+
if (points.length <= 3 || tolerance <= 0) {
|
|
81
|
+
return [...points];
|
|
82
|
+
}
|
|
83
|
+
let oppositeIndex = 1;
|
|
84
|
+
let oppositeDistance = 0;
|
|
85
|
+
for (let index = 1;index < points.length; index++) {
|
|
86
|
+
const distance = squaredDistance(points[0], points[index]);
|
|
87
|
+
if (distance > oppositeDistance) {
|
|
88
|
+
oppositeDistance = distance;
|
|
89
|
+
oppositeIndex = index;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
const firstHalf = points.slice(0, oppositeIndex + 1);
|
|
93
|
+
const secondHalf = [...points.slice(oppositeIndex), points[0]];
|
|
94
|
+
const toleranceSquared = tolerance * tolerance;
|
|
95
|
+
const simplified = [
|
|
96
|
+
...simplifyOpenContour(firstHalf, toleranceSquared).slice(0, -1),
|
|
97
|
+
...simplifyOpenContour(secondHalf, toleranceSquared).slice(0, -1)
|
|
98
|
+
];
|
|
99
|
+
return simplified.length >= 3 ? simplified : [...points];
|
|
100
|
+
};
|
|
101
|
+
var polygonizeAlpha = ({
|
|
102
|
+
data,
|
|
103
|
+
width,
|
|
104
|
+
height,
|
|
105
|
+
simplification
|
|
106
|
+
}) => {
|
|
107
|
+
const adjacency = new Map;
|
|
108
|
+
const points = new Map;
|
|
109
|
+
const addSegment = (a, b) => {
|
|
110
|
+
const aKey = pointKey(a);
|
|
111
|
+
const bKey = pointKey(b);
|
|
112
|
+
points.set(aKey, a);
|
|
113
|
+
points.set(bKey, b);
|
|
114
|
+
const aNeighbors = adjacency.get(aKey);
|
|
115
|
+
if (aNeighbors) {
|
|
116
|
+
aNeighbors.push(bKey);
|
|
117
|
+
} else {
|
|
118
|
+
adjacency.set(aKey, [bKey]);
|
|
119
|
+
}
|
|
120
|
+
const bNeighbors = adjacency.get(bKey);
|
|
121
|
+
if (bNeighbors) {
|
|
122
|
+
bNeighbors.push(aKey);
|
|
123
|
+
} else {
|
|
124
|
+
adjacency.set(bKey, [aKey]);
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
const isFilled = (x, y) => x >= 0 && y >= 0 && x < width && y < height && data[(y * width + x) * 4 + 3] > 0;
|
|
128
|
+
for (let y = -1;y < height; y++) {
|
|
129
|
+
for (let x = -1;x < width; x++) {
|
|
130
|
+
const configuration = (isFilled(x, y) ? 1 : 0) | (isFilled(x + 1, y) ? 2 : 0) | (isFilled(x + 1, y + 1) ? 4 : 0) | (isFilled(x, y + 1) ? 8 : 0);
|
|
131
|
+
if (configuration === 0 || configuration === 15) {
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
const top = [x * 2 + 2, y * 2 + 1];
|
|
135
|
+
const right = [x * 2 + 3, y * 2 + 2];
|
|
136
|
+
const bottom = [x * 2 + 2, y * 2 + 3];
|
|
137
|
+
const left = [x * 2 + 1, y * 2 + 2];
|
|
138
|
+
switch (configuration) {
|
|
139
|
+
case 1:
|
|
140
|
+
case 14:
|
|
141
|
+
addSegment(top, left);
|
|
142
|
+
break;
|
|
143
|
+
case 2:
|
|
144
|
+
case 13:
|
|
145
|
+
addSegment(top, right);
|
|
146
|
+
break;
|
|
147
|
+
case 3:
|
|
148
|
+
case 12:
|
|
149
|
+
addSegment(left, right);
|
|
150
|
+
break;
|
|
151
|
+
case 4:
|
|
152
|
+
case 11:
|
|
153
|
+
addSegment(right, bottom);
|
|
154
|
+
break;
|
|
155
|
+
case 5:
|
|
156
|
+
addSegment(top, left);
|
|
157
|
+
addSegment(right, bottom);
|
|
158
|
+
break;
|
|
159
|
+
case 6:
|
|
160
|
+
case 9:
|
|
161
|
+
addSegment(top, bottom);
|
|
162
|
+
break;
|
|
163
|
+
case 7:
|
|
164
|
+
case 8:
|
|
165
|
+
addSegment(bottom, left);
|
|
166
|
+
break;
|
|
167
|
+
case 10:
|
|
168
|
+
addSegment(top, right);
|
|
169
|
+
addSegment(bottom, left);
|
|
170
|
+
break;
|
|
171
|
+
default:
|
|
172
|
+
throw new Error(`Unexpected marching squares case: ${configuration}`);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
const visitedEdges = new Set;
|
|
177
|
+
const contours = [];
|
|
178
|
+
for (const [start, neighbors] of adjacency) {
|
|
179
|
+
for (const firstNeighbor of neighbors) {
|
|
180
|
+
if (visitedEdges.has(edgeKey(start, firstNeighbor))) {
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
const contour = [start];
|
|
184
|
+
let previous = start;
|
|
185
|
+
let current = firstNeighbor;
|
|
186
|
+
visitedEdges.add(edgeKey(previous, current));
|
|
187
|
+
while (current !== start) {
|
|
188
|
+
contour.push(current);
|
|
189
|
+
const candidates = adjacency.get(current) ?? [];
|
|
190
|
+
const next = candidates.find((candidate) => candidate !== previous && !visitedEdges.has(edgeKey(current, candidate)));
|
|
191
|
+
if (!next) {
|
|
192
|
+
break;
|
|
193
|
+
}
|
|
194
|
+
previous = current;
|
|
195
|
+
current = next;
|
|
196
|
+
visitedEdges.add(edgeKey(previous, current));
|
|
197
|
+
}
|
|
198
|
+
if (current !== start || contour.length < 3) {
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
const decoded = contour.map((key) => {
|
|
202
|
+
const point = points.get(key);
|
|
203
|
+
if (!point) {
|
|
204
|
+
throw new Error(`Missing outline point: ${key}`);
|
|
205
|
+
}
|
|
206
|
+
return [point[0] / 2, point[1] / 2];
|
|
207
|
+
});
|
|
208
|
+
contours.push(simplifyClosedContour(decoded, simplification));
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
return contours;
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
// src/outline/webgpu-shader.ts
|
|
215
|
+
var OUTLINE_WEBGPU_SHADER = `
|
|
216
|
+
${WEBGPU_MULTIPASS_BINDINGS}
|
|
217
|
+
struct Params {color: vec4f, width: f32, opacity: f32, outlineOnly: f32, polygon: f32,}
|
|
218
|
+
const MIN_ALPHA=0.5/255.0;
|
|
219
|
+
@fragment fn fragment(input: VertexOutput) -> @location(0) vec4f {
|
|
220
|
+
let color=textureSampleLevel(source,sourceSampler,input.uv,0);
|
|
221
|
+
let only=params.outlineOnly!=0.0;
|
|
222
|
+
if (params.opacity<=0.0 || params.color.a<=0.0) {return select(color,vec4f(0),only);}
|
|
223
|
+
if (params.width<=0.0 && !only) {return color;}
|
|
224
|
+
var mask=0.0;
|
|
225
|
+
if (params.polygon!=0.0) {
|
|
226
|
+
mask=step(MIN_ALPHA,textureSampleLevel(secondary,sourceSampler,input.uv,0).a);
|
|
227
|
+
} else if (params.width>0.0) {
|
|
228
|
+
let size=vec2i(textureDimensions(source));
|
|
229
|
+
let position=clamp(vec2i(input.uv*vec2f(size)),vec2i(0),size-1);
|
|
230
|
+
for (var ring=1;ring<=3;ring++) {
|
|
231
|
+
let distance=params.width*f32(ring)/3.0;
|
|
232
|
+
for (var direction=0;direction<32;direction++) {
|
|
233
|
+
let angle=6.283185307179586*f32(direction)/32.0;
|
|
234
|
+
let offset=vec2i(round(vec2f(cos(angle),-sin(angle))*distance));
|
|
235
|
+
let samplePosition=clamp(position+offset,vec2i(0),size-1);
|
|
236
|
+
mask=max(mask,step(MIN_ALPHA,textureLoad(source,samplePosition,0).a));
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
if (only) {
|
|
241
|
+
let alpha=select(max(step(MIN_ALPHA,color.a),mask),mask,params.polygon!=0.0)*params.color.a*params.opacity;
|
|
242
|
+
return vec4f(params.color.rgb*alpha,alpha);
|
|
243
|
+
}
|
|
244
|
+
let alpha=mask*params.color.a*params.opacity*(1.0-color.a);
|
|
245
|
+
return vec4f(color.rgb+params.color.rgb*alpha,color.a+alpha);
|
|
246
|
+
}
|
|
247
|
+
`;
|
|
248
|
+
|
|
249
|
+
// src/outline.ts
|
|
250
|
+
var { createEffect, createWebGL2ContextError } = Internals;
|
|
251
|
+
var DEFAULT_WIDTH = 8;
|
|
252
|
+
var DEFAULT_EDGE_SIMPLIFICATION = 0;
|
|
253
|
+
var DEFAULT_COLOR = "#ffffff";
|
|
254
|
+
var DEFAULT_OPACITY = 1;
|
|
255
|
+
var DEFAULT_OUTLINE_ONLY = false;
|
|
256
|
+
var outlineSchema = {
|
|
257
|
+
width: {
|
|
258
|
+
type: "number",
|
|
259
|
+
min: 0,
|
|
260
|
+
max: 100,
|
|
261
|
+
step: 1,
|
|
262
|
+
default: DEFAULT_WIDTH,
|
|
263
|
+
description: "Width",
|
|
264
|
+
hiddenFromList: false
|
|
265
|
+
},
|
|
266
|
+
edgeSimplification: {
|
|
267
|
+
type: "number",
|
|
268
|
+
min: 0,
|
|
269
|
+
max: 100,
|
|
270
|
+
step: 1,
|
|
271
|
+
default: DEFAULT_EDGE_SIMPLIFICATION,
|
|
272
|
+
description: "Edge simplification",
|
|
273
|
+
hiddenFromList: false
|
|
274
|
+
},
|
|
275
|
+
color: {
|
|
276
|
+
type: "color",
|
|
277
|
+
default: DEFAULT_COLOR,
|
|
278
|
+
description: "Color"
|
|
279
|
+
},
|
|
280
|
+
opacity: {
|
|
281
|
+
type: "number",
|
|
282
|
+
min: 0,
|
|
283
|
+
max: 1,
|
|
284
|
+
step: 0.01,
|
|
285
|
+
default: DEFAULT_OPACITY,
|
|
286
|
+
description: "Opacity",
|
|
287
|
+
hiddenFromList: false
|
|
288
|
+
},
|
|
289
|
+
outlineOnly: {
|
|
290
|
+
type: "boolean",
|
|
291
|
+
default: DEFAULT_OUTLINE_ONLY,
|
|
292
|
+
description: "Outline only"
|
|
293
|
+
}
|
|
294
|
+
};
|
|
295
|
+
var resolve = (params) => ({
|
|
296
|
+
width: params.width ?? DEFAULT_WIDTH,
|
|
297
|
+
edgeSimplification: params.edgeSimplification ?? DEFAULT_EDGE_SIMPLIFICATION,
|
|
298
|
+
color: params.color ?? DEFAULT_COLOR,
|
|
299
|
+
opacity: params.opacity ?? DEFAULT_OPACITY,
|
|
300
|
+
outlineOnly: params.outlineOnly ?? DEFAULT_OUTLINE_ONLY
|
|
301
|
+
});
|
|
302
|
+
var validateOutlineParams = (params) => {
|
|
303
|
+
assertEffectParamsObject(params, "Outline");
|
|
304
|
+
assertOptionalFiniteNumber(params.width, "width");
|
|
305
|
+
assertOptionalFiniteNumber(params.edgeSimplification, "edgeSimplification");
|
|
306
|
+
assertOptionalColor(params.color, "color");
|
|
307
|
+
assertOptionalFiniteNumber(params.opacity, "opacity");
|
|
308
|
+
assertOptionalBoolean(params.outlineOnly, "outlineOnly");
|
|
309
|
+
const resolved = resolve(params);
|
|
310
|
+
validateNonNegative(resolved.width, "width");
|
|
311
|
+
validateNonNegative(resolved.edgeSimplification, "edgeSimplification");
|
|
312
|
+
validateUnitInterval(resolved.opacity, "opacity");
|
|
313
|
+
};
|
|
314
|
+
var OUTLINE_VS = `#version 300 es
|
|
315
|
+
in vec2 aPos;
|
|
316
|
+
in vec2 aUv;
|
|
317
|
+
out vec2 vUv;
|
|
318
|
+
|
|
319
|
+
void main() {
|
|
320
|
+
vUv = aUv;
|
|
321
|
+
gl_Position = vec4(aPos, 0.0, 1.0);
|
|
322
|
+
}
|
|
323
|
+
`;
|
|
324
|
+
var OUTLINE_FS = `#version 300 es
|
|
325
|
+
precision highp float;
|
|
326
|
+
|
|
327
|
+
in vec2 vUv;
|
|
328
|
+
out vec4 fragColor;
|
|
329
|
+
|
|
330
|
+
uniform sampler2D uSource;
|
|
331
|
+
uniform sampler2D uPolygonMask;
|
|
332
|
+
uniform bool uUsePolygonMask;
|
|
333
|
+
uniform float uWidth;
|
|
334
|
+
uniform vec4 uColor;
|
|
335
|
+
uniform float uOpacity;
|
|
336
|
+
uniform bool uOutlineOnly;
|
|
337
|
+
|
|
338
|
+
const float TAU = 6.283185307179586;
|
|
339
|
+
const float MIN_ALPHA = 0.5 / 255.0;
|
|
340
|
+
const int SAMPLE_DIRECTIONS = 32;
|
|
341
|
+
const int SAMPLE_RINGS = 3;
|
|
342
|
+
|
|
343
|
+
void main() {
|
|
344
|
+
vec4 source = texture(uSource, vUv);
|
|
345
|
+
|
|
346
|
+
if (uOpacity <= 0.0 || uColor.a <= 0.0) {
|
|
347
|
+
fragColor = uOutlineOnly ? vec4(0.0) : source;
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
if (uWidth <= 0.0 && !uOutlineOnly) {
|
|
352
|
+
fragColor = source;
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
float outlineMaskAlpha = 0.0;
|
|
357
|
+
if (uUsePolygonMask) {
|
|
358
|
+
outlineMaskAlpha = step(MIN_ALPHA, texture(uPolygonMask, vUv).a);
|
|
359
|
+
} else if (uWidth > 0.0) {
|
|
360
|
+
ivec2 sourceSize = textureSize(uSource, 0);
|
|
361
|
+
ivec2 sourcePosition = clamp(
|
|
362
|
+
ivec2(vUv * vec2(sourceSize)),
|
|
363
|
+
ivec2(0),
|
|
364
|
+
sourceSize - ivec2(1)
|
|
365
|
+
);
|
|
366
|
+
for (int ring = 1; ring <= SAMPLE_RINGS; ring++) {
|
|
367
|
+
float distancePx = uWidth * float(ring) / float(SAMPLE_RINGS);
|
|
368
|
+
for (int direction = 0; direction < SAMPLE_DIRECTIONS; direction++) {
|
|
369
|
+
float angle = TAU * float(direction) / float(SAMPLE_DIRECTIONS);
|
|
370
|
+
ivec2 offset = ivec2(round(
|
|
371
|
+
vec2(cos(angle), sin(angle)) * distancePx
|
|
372
|
+
));
|
|
373
|
+
ivec2 samplePosition = clamp(
|
|
374
|
+
sourcePosition + offset,
|
|
375
|
+
ivec2(0),
|
|
376
|
+
sourceSize - ivec2(1)
|
|
377
|
+
);
|
|
378
|
+
outlineMaskAlpha = max(
|
|
379
|
+
outlineMaskAlpha,
|
|
380
|
+
step(MIN_ALPHA, texelFetch(uSource, samplePosition, 0).a)
|
|
381
|
+
);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
if (uOutlineOnly) {
|
|
387
|
+
float filledAlpha = (
|
|
388
|
+
uUsePolygonMask
|
|
389
|
+
? outlineMaskAlpha
|
|
390
|
+
: max(step(MIN_ALPHA, source.a), outlineMaskAlpha)
|
|
391
|
+
) * uColor.a * uOpacity;
|
|
392
|
+
fragColor = vec4(uColor.rgb * filledAlpha, filledAlpha);
|
|
393
|
+
return;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
float outlineAlpha = outlineMaskAlpha * uColor.a * uOpacity * (1.0 - source.a);
|
|
397
|
+
vec3 outlineRgb = uColor.rgb * outlineAlpha;
|
|
398
|
+
fragColor = vec4(source.rgb + outlineRgb, source.a + outlineAlpha);
|
|
399
|
+
}
|
|
400
|
+
`;
|
|
401
|
+
var compileShader = (gl, type, source) => {
|
|
402
|
+
const shader = gl.createShader(type);
|
|
403
|
+
if (!shader) {
|
|
404
|
+
throw new Error("Failed to create WebGL shader");
|
|
405
|
+
}
|
|
406
|
+
gl.shaderSource(shader, source);
|
|
407
|
+
gl.compileShader(shader);
|
|
408
|
+
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
|
|
409
|
+
const log = gl.getShaderInfoLog(shader);
|
|
410
|
+
gl.deleteShader(shader);
|
|
411
|
+
throw new Error(`Outline shader compile failed: ${log ?? "(no log)"}`);
|
|
412
|
+
}
|
|
413
|
+
return shader;
|
|
414
|
+
};
|
|
415
|
+
var createProgram = (gl) => {
|
|
416
|
+
const vertexShader = compileShader(gl, gl.VERTEX_SHADER, OUTLINE_VS);
|
|
417
|
+
const fragmentShader = compileShader(gl, gl.FRAGMENT_SHADER, OUTLINE_FS);
|
|
418
|
+
const program = gl.createProgram();
|
|
419
|
+
if (!program) {
|
|
420
|
+
throw new Error("Failed to create WebGL program");
|
|
421
|
+
}
|
|
422
|
+
gl.attachShader(program, vertexShader);
|
|
423
|
+
gl.attachShader(program, fragmentShader);
|
|
424
|
+
gl.linkProgram(program);
|
|
425
|
+
gl.deleteShader(vertexShader);
|
|
426
|
+
gl.deleteShader(fragmentShader);
|
|
427
|
+
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
|
|
428
|
+
const log = gl.getProgramInfoLog(program);
|
|
429
|
+
gl.deleteProgram(program);
|
|
430
|
+
throw new Error(`Outline program link failed: ${log ?? "(no log)"}`);
|
|
431
|
+
}
|
|
432
|
+
return program;
|
|
433
|
+
};
|
|
434
|
+
var createTexture = (gl) => {
|
|
435
|
+
const texture = gl.createTexture();
|
|
436
|
+
if (!texture) {
|
|
437
|
+
throw new Error("Failed to create WebGL texture");
|
|
438
|
+
}
|
|
439
|
+
gl.bindTexture(gl.TEXTURE_2D, texture);
|
|
440
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
|
|
441
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
|
|
442
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
|
443
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
|
444
|
+
gl.bindTexture(gl.TEXTURE_2D, null);
|
|
445
|
+
return texture;
|
|
446
|
+
};
|
|
447
|
+
var updatePolygonMask = ({
|
|
448
|
+
source,
|
|
449
|
+
width,
|
|
450
|
+
height,
|
|
451
|
+
simplification,
|
|
452
|
+
outlineWidth,
|
|
453
|
+
state
|
|
454
|
+
}) => {
|
|
455
|
+
if (state.alphaCanvas.width !== width || state.alphaCanvas.height !== height || state.polygonMaskCanvas.width !== width || state.polygonMaskCanvas.height !== height) {
|
|
456
|
+
state.alphaCanvas.width = width;
|
|
457
|
+
state.alphaCanvas.height = height;
|
|
458
|
+
state.polygonMaskCanvas.width = width;
|
|
459
|
+
state.polygonMaskCanvas.height = height;
|
|
460
|
+
}
|
|
461
|
+
state.alphaCtx.clearRect(0, 0, width, height);
|
|
462
|
+
state.alphaCtx.drawImage(source, 0, 0, width, height);
|
|
463
|
+
const imageData = state.alphaCtx.getImageData(0, 0, width, height);
|
|
464
|
+
const contours = polygonizeAlpha({
|
|
465
|
+
data: imageData.data,
|
|
466
|
+
width,
|
|
467
|
+
height,
|
|
468
|
+
simplification
|
|
469
|
+
});
|
|
470
|
+
const { polygonMaskCtx: context } = state;
|
|
471
|
+
context.clearRect(0, 0, width, height);
|
|
472
|
+
context.beginPath();
|
|
473
|
+
for (const contour of contours) {
|
|
474
|
+
if (contour.length < 3) {
|
|
475
|
+
continue;
|
|
476
|
+
}
|
|
477
|
+
context.moveTo(contour[0][0], contour[0][1]);
|
|
478
|
+
for (let index = 1;index < contour.length; index++) {
|
|
479
|
+
context.lineTo(contour[index][0], contour[index][1]);
|
|
480
|
+
}
|
|
481
|
+
context.closePath();
|
|
482
|
+
}
|
|
483
|
+
context.fillStyle = "white";
|
|
484
|
+
context.fill("evenodd");
|
|
485
|
+
if (outlineWidth > 0) {
|
|
486
|
+
context.strokeStyle = "white";
|
|
487
|
+
context.lineWidth = outlineWidth * 2;
|
|
488
|
+
context.lineJoin = "miter";
|
|
489
|
+
context.miterLimit = 4;
|
|
490
|
+
context.stroke();
|
|
491
|
+
}
|
|
492
|
+
};
|
|
493
|
+
var setupOutline = (target) => {
|
|
494
|
+
const gl = target.getContext("webgl2", {
|
|
495
|
+
premultipliedAlpha: true,
|
|
496
|
+
alpha: true,
|
|
497
|
+
preserveDrawingBuffer: true
|
|
498
|
+
});
|
|
499
|
+
if (!gl) {
|
|
500
|
+
throw createWebGL2ContextError("outline effect");
|
|
501
|
+
}
|
|
502
|
+
gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true);
|
|
503
|
+
const program = createProgram(gl);
|
|
504
|
+
const vao = gl.createVertexArray();
|
|
505
|
+
if (!vao) {
|
|
506
|
+
throw new Error("Failed to create WebGL vertex array");
|
|
507
|
+
}
|
|
508
|
+
gl.bindVertexArray(vao);
|
|
509
|
+
const vbo = gl.createBuffer();
|
|
510
|
+
if (!vbo) {
|
|
511
|
+
throw new Error("Failed to create WebGL buffer");
|
|
512
|
+
}
|
|
513
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, vbo);
|
|
514
|
+
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1, -1, 0, 0, 1, -1, 1, 0, -1, 1, 0, 1, 1, 1, 1, 1]), gl.STATIC_DRAW);
|
|
515
|
+
const aPos = gl.getAttribLocation(program, "aPos");
|
|
516
|
+
const aUv = gl.getAttribLocation(program, "aUv");
|
|
517
|
+
gl.enableVertexAttribArray(aPos);
|
|
518
|
+
gl.vertexAttribPointer(aPos, 2, gl.FLOAT, false, 16, 0);
|
|
519
|
+
gl.enableVertexAttribArray(aUv);
|
|
520
|
+
gl.vertexAttribPointer(aUv, 2, gl.FLOAT, false, 16, 8);
|
|
521
|
+
gl.bindVertexArray(null);
|
|
522
|
+
const textureSource = createTexture(gl);
|
|
523
|
+
const texturePolygonMask = createTexture(gl);
|
|
524
|
+
gl.bindTexture(gl.TEXTURE_2D, texturePolygonMask);
|
|
525
|
+
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array([0, 0, 0, 0]));
|
|
526
|
+
gl.bindTexture(gl.TEXTURE_2D, null);
|
|
527
|
+
const colorCanvas = target.ownerDocument.createElement("canvas");
|
|
528
|
+
colorCanvas.width = 1;
|
|
529
|
+
colorCanvas.height = 1;
|
|
530
|
+
const colorCtx = colorCanvas.getContext("2d", { willReadFrequently: true });
|
|
531
|
+
if (!colorCtx) {
|
|
532
|
+
throw new Error("Failed to acquire 2D context for color parsing");
|
|
533
|
+
}
|
|
534
|
+
const alphaCanvas = target.ownerDocument.createElement("canvas");
|
|
535
|
+
const alphaCtx = alphaCanvas.getContext("2d", { willReadFrequently: true });
|
|
536
|
+
if (!alphaCtx) {
|
|
537
|
+
throw new Error("Failed to acquire 2D context for outline extraction");
|
|
538
|
+
}
|
|
539
|
+
const polygonMaskCanvas = target.ownerDocument.createElement("canvas");
|
|
540
|
+
polygonMaskCanvas.width = 1;
|
|
541
|
+
polygonMaskCanvas.height = 1;
|
|
542
|
+
const polygonMaskCtx = polygonMaskCanvas.getContext("2d");
|
|
543
|
+
if (!polygonMaskCtx) {
|
|
544
|
+
throw new Error("Failed to acquire 2D context for outline mask");
|
|
545
|
+
}
|
|
546
|
+
return {
|
|
547
|
+
gl,
|
|
548
|
+
program,
|
|
549
|
+
vao,
|
|
550
|
+
vbo,
|
|
551
|
+
textureSource,
|
|
552
|
+
texturePolygonMask,
|
|
553
|
+
uniforms: {
|
|
554
|
+
uSource: gl.getUniformLocation(program, "uSource"),
|
|
555
|
+
uPolygonMask: gl.getUniformLocation(program, "uPolygonMask"),
|
|
556
|
+
uUsePolygonMask: gl.getUniformLocation(program, "uUsePolygonMask"),
|
|
557
|
+
uWidth: gl.getUniformLocation(program, "uWidth"),
|
|
558
|
+
uColor: gl.getUniformLocation(program, "uColor"),
|
|
559
|
+
uOpacity: gl.getUniformLocation(program, "uOpacity"),
|
|
560
|
+
uOutlineOnly: gl.getUniformLocation(program, "uOutlineOnly")
|
|
561
|
+
},
|
|
562
|
+
alphaCanvas,
|
|
563
|
+
alphaCtx,
|
|
564
|
+
polygonMaskCanvas,
|
|
565
|
+
polygonMaskCtx,
|
|
566
|
+
colorCtx,
|
|
567
|
+
cachedColor: "",
|
|
568
|
+
cachedColorRgba: [255, 255, 255, 255]
|
|
569
|
+
};
|
|
570
|
+
};
|
|
571
|
+
var outline = createEffect({
|
|
572
|
+
type: "remotion/outline",
|
|
573
|
+
label: "outline()",
|
|
574
|
+
documentationLink: "https://www.remotion.dev/docs/effects/outline",
|
|
575
|
+
calculateKey: (params) => {
|
|
576
|
+
const resolved = resolve(params);
|
|
577
|
+
return `outline-${resolved.width}-${resolved.edgeSimplification}-${resolved.color}-${resolved.opacity}-${resolved.outlineOnly}`;
|
|
578
|
+
},
|
|
579
|
+
implementations: {
|
|
580
|
+
webgpu: Internals.createEffectImplementation({
|
|
581
|
+
setup: (target) => {
|
|
582
|
+
const canvas = document.createElement("canvas");
|
|
583
|
+
canvas.width = 1;
|
|
584
|
+
canvas.height = 1;
|
|
585
|
+
const colorCtx = canvas.getContext("2d", { willReadFrequently: true });
|
|
586
|
+
if (!colorCtx)
|
|
587
|
+
throw new Error("Could not create color parser");
|
|
588
|
+
const alphaCanvas = document.createElement("canvas");
|
|
589
|
+
const alphaCtx = alphaCanvas.getContext("2d", {
|
|
590
|
+
willReadFrequently: true
|
|
591
|
+
});
|
|
592
|
+
const polygonMaskCanvas = document.createElement("canvas");
|
|
593
|
+
const polygonMaskCtx = polygonMaskCanvas.getContext("2d");
|
|
594
|
+
if (!alphaCtx || !polygonMaskCtx)
|
|
595
|
+
throw new Error("Could not create outline mask");
|
|
596
|
+
return {
|
|
597
|
+
alphaCanvas,
|
|
598
|
+
alphaCtx,
|
|
599
|
+
polygonMaskCanvas,
|
|
600
|
+
polygonMaskCtx,
|
|
601
|
+
...setupWebGpuMultipass(target, "outline"),
|
|
602
|
+
colorCtx,
|
|
603
|
+
cachedColor: null,
|
|
604
|
+
cachedRgba: [0, 0, 0, 0]
|
|
605
|
+
};
|
|
606
|
+
},
|
|
607
|
+
apply: ({ source, width, height, params, state, gpuDevice }) => {
|
|
608
|
+
if (!gpuDevice)
|
|
609
|
+
throw new Error("A WebGPU device is required for this effect");
|
|
610
|
+
const r = resolve(params);
|
|
611
|
+
if (state.cachedColor !== r.color) {
|
|
612
|
+
state.cachedColor = r.color;
|
|
613
|
+
state.cachedRgba = parseColorRgba(state.colorCtx, r.color);
|
|
614
|
+
}
|
|
615
|
+
const color = state.cachedRgba.map((channel) => channel / 255);
|
|
616
|
+
const usePolygonMask = r.edgeSimplification > 0 && r.opacity > 0 && color[3] > 0 && (r.width > 0 || r.outlineOnly);
|
|
617
|
+
if (usePolygonMask)
|
|
618
|
+
updatePolygonMask({
|
|
619
|
+
source,
|
|
620
|
+
width,
|
|
621
|
+
height,
|
|
622
|
+
simplification: r.edgeSimplification,
|
|
623
|
+
outlineWidth: r.width,
|
|
624
|
+
state
|
|
625
|
+
});
|
|
626
|
+
applyWebGpuMultipass({
|
|
627
|
+
state,
|
|
628
|
+
source,
|
|
629
|
+
width,
|
|
630
|
+
height,
|
|
631
|
+
device: gpuDevice,
|
|
632
|
+
auxiliarySource: usePolygonMask ? state.polygonMaskCanvas : null,
|
|
633
|
+
passes: [
|
|
634
|
+
{
|
|
635
|
+
shader: OUTLINE_WEBGPU_SHADER,
|
|
636
|
+
uniforms: new Float32Array([
|
|
637
|
+
...color,
|
|
638
|
+
r.width,
|
|
639
|
+
r.opacity,
|
|
640
|
+
r.outlineOnly ? 1 : 0,
|
|
641
|
+
usePolygonMask ? 1 : 0
|
|
642
|
+
]),
|
|
643
|
+
source: -1,
|
|
644
|
+
secondary: usePolygonMask ? -2 : -1
|
|
645
|
+
}
|
|
646
|
+
]
|
|
647
|
+
});
|
|
648
|
+
},
|
|
649
|
+
cleanup: cleanupWebGpuMultipass
|
|
650
|
+
}),
|
|
651
|
+
webgl2: Internals.createEffectImplementation({
|
|
652
|
+
setup: setupOutline,
|
|
653
|
+
apply: ({ source, width, height, params, state, flipSourceY }) => {
|
|
654
|
+
const resolved = resolve(params);
|
|
655
|
+
if (state.cachedColor !== resolved.color) {
|
|
656
|
+
state.cachedColor = resolved.color;
|
|
657
|
+
state.cachedColorRgba = parseColorRgba(state.colorCtx, resolved.color);
|
|
658
|
+
}
|
|
659
|
+
const { gl, program, textureSource, texturePolygonMask, uniforms, vao } = state;
|
|
660
|
+
const [red, green, blue, alpha] = state.cachedColorRgba;
|
|
661
|
+
const usePolygonMask = resolved.edgeSimplification > 0 && resolved.opacity > 0 && alpha > 0 && (resolved.width > 0 || resolved.outlineOnly);
|
|
662
|
+
if (usePolygonMask) {
|
|
663
|
+
updatePolygonMask({
|
|
664
|
+
source,
|
|
665
|
+
width,
|
|
666
|
+
height,
|
|
667
|
+
simplification: resolved.edgeSimplification,
|
|
668
|
+
outlineWidth: resolved.width,
|
|
669
|
+
state
|
|
670
|
+
});
|
|
671
|
+
}
|
|
672
|
+
gl.viewport(0, 0, width, height);
|
|
673
|
+
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
|
|
674
|
+
gl.clearColor(0, 0, 0, 0);
|
|
675
|
+
gl.clear(gl.COLOR_BUFFER_BIT);
|
|
676
|
+
gl.activeTexture(gl.TEXTURE0);
|
|
677
|
+
gl.bindTexture(gl.TEXTURE_2D, textureSource);
|
|
678
|
+
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flipSourceY);
|
|
679
|
+
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, source);
|
|
680
|
+
gl.activeTexture(gl.TEXTURE1);
|
|
681
|
+
gl.bindTexture(gl.TEXTURE_2D, texturePolygonMask);
|
|
682
|
+
if (usePolygonMask) {
|
|
683
|
+
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
|
|
684
|
+
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, state.polygonMaskCanvas);
|
|
685
|
+
}
|
|
686
|
+
gl.useProgram(program);
|
|
687
|
+
if (uniforms.uSource)
|
|
688
|
+
gl.uniform1i(uniforms.uSource, 0);
|
|
689
|
+
if (uniforms.uPolygonMask)
|
|
690
|
+
gl.uniform1i(uniforms.uPolygonMask, 1);
|
|
691
|
+
if (uniforms.uUsePolygonMask)
|
|
692
|
+
gl.uniform1i(uniforms.uUsePolygonMask, usePolygonMask ? 1 : 0);
|
|
693
|
+
if (uniforms.uWidth)
|
|
694
|
+
gl.uniform1f(uniforms.uWidth, resolved.width);
|
|
695
|
+
if (uniforms.uColor)
|
|
696
|
+
gl.uniform4f(uniforms.uColor, red / 255, green / 255, blue / 255, alpha / 255);
|
|
697
|
+
if (uniforms.uOpacity)
|
|
698
|
+
gl.uniform1f(uniforms.uOpacity, resolved.opacity);
|
|
699
|
+
if (uniforms.uOutlineOnly)
|
|
700
|
+
gl.uniform1i(uniforms.uOutlineOnly, resolved.outlineOnly ? 1 : 0);
|
|
701
|
+
gl.bindVertexArray(vao);
|
|
702
|
+
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
|
|
703
|
+
gl.bindVertexArray(null);
|
|
704
|
+
gl.activeTexture(gl.TEXTURE1);
|
|
705
|
+
gl.bindTexture(gl.TEXTURE_2D, null);
|
|
706
|
+
gl.activeTexture(gl.TEXTURE0);
|
|
707
|
+
gl.bindTexture(gl.TEXTURE_2D, null);
|
|
708
|
+
gl.useProgram(null);
|
|
709
|
+
},
|
|
710
|
+
cleanup: ({ gl, program, vao, vbo, textureSource, texturePolygonMask }) => {
|
|
711
|
+
gl.deleteTexture(textureSource);
|
|
712
|
+
gl.deleteTexture(texturePolygonMask);
|
|
713
|
+
gl.deleteBuffer(vbo);
|
|
714
|
+
gl.deleteProgram(program);
|
|
715
|
+
gl.deleteVertexArray(vao);
|
|
716
|
+
}
|
|
717
|
+
})
|
|
718
|
+
},
|
|
719
|
+
schema: outlineSchema,
|
|
720
|
+
validateParams: validateOutlineParams
|
|
721
|
+
});
|
|
722
|
+
export {
|
|
723
|
+
outline,
|
|
724
|
+
outlineSchema
|
|
725
|
+
};
|