@pexip/media-processor 20.3.5 → 22.0.0

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 (201) hide show
  1. package/CHANGELOG.md +109 -0
  2. package/README.md +17 -10
  3. package/api-docs/README.mdx +69 -23
  4. package/api-docs/functions/assertMediaStreamVideoTrack.mdx +13 -0
  5. package/api-docs/functions/createBenchmark.mdx +17 -5
  6. package/api-docs/functions/createFrameCallbackRequest.mdx +8 -2
  7. package/api-docs/functions/createMediaStreamTrackInputProcessAdaptor.mdx +10 -0
  8. package/api-docs/functions/createReadWritableStreamFallbackInputProcessAdaptor.mdx +17 -0
  9. package/api-docs/functions/createReadWritableStreamInputProcessAdaptor.mdx +10 -0
  10. package/api-docs/functions/createVideoProcessor.mdx +3 -3
  11. package/api-docs/functions/createWindowedStats.mdx +23 -0
  12. package/api-docs/functions/getDefaultInputProcessAdaptor.mdx +17 -0
  13. package/api-docs/functions/iou.mdx +19 -0
  14. package/api-docs/functions/isMediaStreamVideoTrack.mdx +13 -0
  15. package/api-docs/functions/labelComponents.mdx +20 -0
  16. package/api-docs/functions/nearestPowerOfTwo.mdx +21 -0
  17. package/api-docs/functions/playVideo.mdx +16 -0
  18. package/api-docs/functions/scoreComponent.mdx +22 -0
  19. package/api-docs/functions/setVideoElementSrc.mdx +14 -0
  20. package/api-docs/interfaces/Analyzer.mdx +4 -0
  21. package/api-docs/interfaces/Benchmark.mdx +26 -2
  22. package/api-docs/interfaces/FrameTransformStats.mdx +9 -0
  23. package/api-docs/interfaces/Gain.mdx +4 -0
  24. package/api-docs/interfaces/ImageSegmenterOptions.mdx +0 -2
  25. package/api-docs/interfaces/InputProcessAdaptor.mdx +28 -0
  26. package/api-docs/interfaces/Process.mdx +8 -3
  27. package/api-docs/interfaces/ProcessEventMute.mdx +6 -0
  28. package/api-docs/interfaces/ProcessWorkerEventOpened.mdx +7 -0
  29. package/api-docs/interfaces/ProcessWorkerEventStats.mdx +6 -0
  30. package/api-docs/interfaces/ProcessWorkerEventUpdated.mdx +7 -0
  31. package/api-docs/interfaces/ProcessorEvent.mdx +1 -1
  32. package/api-docs/interfaces/ProcessorOptions.mdx +0 -1
  33. package/api-docs/interfaces/ProcessorProcessOptions.mdx +3 -1
  34. package/api-docs/interfaces/ProcessorUpdateOptions.mdx +15 -0
  35. package/api-docs/interfaces/ReadWritableStream.mdx +12 -0
  36. package/api-docs/interfaces/Renderer.mdx +2 -2
  37. package/api-docs/interfaces/RendererOptions.mdx +25 -12
  38. package/api-docs/interfaces/Segmenter.mdx +89 -10
  39. package/api-docs/interfaces/SegmenterOptions.mdx +0 -1
  40. package/api-docs/interfaces/SelectionOptions.mdx +15 -0
  41. package/api-docs/interfaces/Stats.mdx +11 -0
  42. package/api-docs/interfaces/VideoProcessor.mdx +49 -5
  43. package/api-docs/interfaces/Weights.mdx +12 -0
  44. package/api-docs/type-aliases/OptionalKeys.mdx +10 -0
  45. package/api-docs/type-aliases/ProcessEvents.mdx +1 -0
  46. package/api-docs/type-aliases/ProcessInputType.mdx +3 -1
  47. package/api-docs/type-aliases/ProcessWorkerEvents.mdx +6 -4
  48. package/api-docs/type-aliases/ProcessorWorkerEvents.mdx +1 -1
  49. package/api-docs/type-aliases/Struct.mdx +9 -0
  50. package/api-docs/variables/BACKGROUND_THRESHOLD.mdx +3 -0
  51. package/api-docs/variables/DOWN_SAMPLE_FACTOR.mdx +3 -0
  52. package/api-docs/variables/EDGE_BLUR_AMOUNT.mdx +1 -1
  53. package/api-docs/variables/EXCLUDE_BYSTANDERS.mdx +3 -0
  54. package/api-docs/variables/FOREGROUND_THRESHOLD.mdx +1 -1
  55. package/api-docs/variables/FRAME_RATE.mdx +1 -1
  56. package/api-docs/variables/ImageFetchError.mdx +3 -0
  57. package/api-docs/variables/LIGHT_WRAP_BLUR_AMOUNT.mdx +3 -0
  58. package/api-docs/variables/LIGHT_WRAP_EDGE_BAND.mdx +3 -0
  59. package/api-docs/variables/LIGHT_WRAP_INTENSITY.mdx +3 -0
  60. package/api-docs/variables/LIGHT_WRAP_TIGHTNESS.mdx +3 -0
  61. package/api-docs/variables/MASK_COMBINE_RATIO.mdx +1 -1
  62. package/api-docs/variables/MORPH_DILATE_RADIUS_PX.mdx +3 -0
  63. package/api-docs/variables/MORPH_ERODE_RADIUS_PX.mdx +3 -0
  64. package/api-docs/variables/MORPH_PASS.mdx +3 -0
  65. package/api-docs/variables/MediaStreamTrackTransferable.mdx +5 -0
  66. package/api-docs/variables/PERSON_CENTER.mdx +3 -0
  67. package/api-docs/variables/RENDERER_OPTIONS_STRUCT.mdx +3 -0
  68. package/api-docs/variables/SIGMA_RANGE.mdx +3 -0
  69. package/api-docs/variables/SIGMA_SPACE.mdx +3 -0
  70. package/api-docs/variables/STRONG_EDGE_BLUR_AMOUNT.mdx +3 -0
  71. package/api-docs/variables/cloneImageRecord.mdx +19 -0
  72. package/api-docs/variables/compareImageRecords.mdx +18 -0
  73. package/api-docs/variables/createRemoteImageBitmap.mdx +1 -1
  74. package/api-docs/variables/getCanvasRenderingContext2D.mdx +14 -0
  75. package/api-docs/variables/getImageSize.mdx +13 -0
  76. package/api-docs/variables/isRenderOptionsKey.mdx +13 -0
  77. package/api-docs/variables/isValidGpuAPI.mdx +13 -0
  78. package/dist/common/backends/webgl/binaryMask.d.ts +8 -0
  79. package/dist/common/backends/webgl/binaryMask.js +95 -0
  80. package/dist/common/backends/webgl/blender.d.ts +2 -1
  81. package/dist/common/backends/webgl/blender.js +18 -15
  82. package/dist/common/backends/webgl/blur.d.ts +1 -1
  83. package/dist/common/backends/webgl/blur.js +23 -16
  84. package/dist/common/backends/webgl/canvas.js +9 -4
  85. package/dist/common/backends/webgl/diffAbsMask.d.ts +10 -0
  86. package/dist/common/backends/webgl/diffAbsMask.js +103 -0
  87. package/dist/common/backends/webgl/geodesicReconstruction.d.ts +13 -0
  88. package/dist/common/backends/webgl/geodesicReconstruction.js +176 -0
  89. package/dist/common/backends/webgl/jointBilateralFilter.d.ts +12 -0
  90. package/dist/common/backends/webgl/jointBilateralFilter.js +108 -0
  91. package/dist/common/backends/webgl/morphErode.d.ts +13 -0
  92. package/dist/common/backends/webgl/morphErode.js +121 -0
  93. package/dist/common/backends/webgl/renderer.js +194 -44
  94. package/dist/common/backends/webgl/replace.d.ts +17 -0
  95. package/dist/common/backends/webgl/replace.js +126 -0
  96. package/dist/common/backends/webgl/selectComponent.d.ts +15 -0
  97. package/dist/common/backends/webgl/selectComponent.js +102 -0
  98. package/dist/common/backends/webgl/smoothingMask.d.ts +3 -3
  99. package/dist/common/backends/webgl/smoothingMask.js +22 -21
  100. package/dist/common/backends/webgl/tentBlur.d.ts +9 -0
  101. package/dist/common/backends/webgl/tentBlur.js +118 -0
  102. package/dist/common/backends/webgl/texture.d.ts +2 -3
  103. package/dist/common/backends/webgl/texture.js +21 -19
  104. package/dist/common/backends/webgl/textureToTexture.d.ts +1 -1
  105. package/dist/common/backends/webgl/textureToTexture.js +19 -16
  106. package/dist/common/backends/webgl/webglUtils.d.ts +7 -1
  107. package/dist/common/backends/webgl/webglUtils.js +72 -10
  108. package/dist/common/backends/webgpu/blender.d.ts +6 -1
  109. package/dist/common/backends/webgpu/blender.js +96 -51
  110. package/dist/common/backends/webgpu/constants.js +1 -1
  111. package/dist/common/backends/webgpu/dualFilterBlur.d.ts +2 -3
  112. package/dist/common/backends/webgpu/dualFilterBlur.js +146 -140
  113. package/dist/common/backends/webgpu/jointBilateralFilter.d.ts +7 -0
  114. package/dist/common/backends/webgpu/jointBilateralFilter.js +162 -0
  115. package/dist/common/backends/webgpu/renderer.js +205 -43
  116. package/dist/common/backends/webgpu/replace.d.ts +11 -0
  117. package/dist/common/backends/webgpu/replace.js +175 -0
  118. package/dist/common/backends/webgpu/resize.d.ts +4 -0
  119. package/dist/common/backends/webgpu/resize.js +76 -0
  120. package/dist/common/backends/webgpu/smoothingMask.d.ts +2 -2
  121. package/dist/common/backends/webgpu/smoothingMask.js +74 -40
  122. package/dist/common/backends/webgpu/tentBlur.d.ts +5 -0
  123. package/dist/common/backends/webgpu/tentBlur.js +131 -0
  124. package/dist/common/backends/webgpu/texture.d.ts +2 -3
  125. package/dist/common/backends/webgpu/texture.js +29 -20
  126. package/dist/common/backends/webgpu/textureToTexture.d.ts +1 -1
  127. package/dist/common/backends/webgpu/textureToTexture.js +28 -15
  128. package/dist/common/backends/webgpu/types.d.ts +2 -0
  129. package/dist/common/backends/webgpu/webgpuUtils.d.ts +24 -4
  130. package/dist/common/backends/webgpu/webgpuUtils.js +60 -11
  131. package/dist/common/constants.d.ts +22 -5
  132. package/dist/common/constants.js +23 -5
  133. package/dist/common/index.d.ts +2 -0
  134. package/dist/common/index.js +2 -0
  135. package/dist/common/tsconfig.tsbuildinfo +1 -1
  136. package/dist/common/typeGuards.d.ts +2 -0
  137. package/dist/common/typeGuards.js +7 -0
  138. package/dist/common/types/media.d.ts +5 -1
  139. package/dist/common/types/messageEvents.d.ts +31 -4
  140. package/dist/common/types/processor.d.ts +6 -4
  141. package/dist/common/types/render.d.ts +147 -15
  142. package/dist/common/types/render.js +23 -1
  143. package/dist/common/types/segmentation.d.ts +0 -12
  144. package/dist/common/types/utils.d.ts +21 -0
  145. package/dist/common/utils.d.ts +29 -2
  146. package/dist/common/utils.js +206 -10
  147. package/dist/main/audio.js +3 -3
  148. package/dist/main/benchUtils.d.ts +35 -3
  149. package/dist/main/benchUtils.js +149 -15
  150. package/dist/main/index.d.ts +1 -1
  151. package/dist/main/index.js +1 -1
  152. package/dist/main/tsconfig.tsbuildinfo +1 -1
  153. package/dist/main/types.d.ts +0 -6
  154. package/dist/main/utils.d.ts +4 -0
  155. package/dist/main/utils.js +31 -0
  156. package/dist/main/video/constants.d.ts +1 -4
  157. package/dist/main/video/constants.js +1 -5
  158. package/dist/main/video/index.d.ts +3 -4
  159. package/dist/main/video/index.js +3 -4
  160. package/dist/main/video/inputProcessAdaptor.d.ts +30 -0
  161. package/dist/main/video/inputProcessAdaptor.js +198 -0
  162. package/dist/main/video/segmenter.d.ts +35 -0
  163. package/dist/main/video/{segmenters/mediapipe.js → segmenter.js} +93 -53
  164. package/dist/main/video/typeGuards.d.ts +2 -0
  165. package/dist/main/video/typeGuards.js +8 -1
  166. package/dist/main/video/types.d.ts +36 -16
  167. package/dist/main/video/utils.d.ts +5 -4
  168. package/dist/main/video/utils.js +49 -44
  169. package/dist/main/video/video.d.ts +29 -7
  170. package/dist/main/video/video.js +323 -28
  171. package/dist/workers/mediaWorker.js +35 -162
  172. package/dist/workers/tsconfig.tsbuildinfo +1 -1
  173. package/dist/worklets/tsconfig.tsbuildinfo +1 -1
  174. package/package.json +13 -12
  175. package/api-docs/functions/createCanvasTransform.mdx +0 -14
  176. package/api-docs/functions/createVideoTrackProcessor.mdx +0 -9
  177. package/api-docs/functions/createVideoTrackProcessorWithFallback.mdx +0 -15
  178. package/api-docs/interfaces/Detector.mdx +0 -81
  179. package/api-docs/interfaces/SegmentationParams.mdx +0 -18
  180. package/api-docs/interfaces/Transform.mdx +0 -62
  181. package/api-docs/type-aliases/ProcessVideoTrack.mdx +0 -15
  182. package/api-docs/type-aliases/SegmentationTransform.mdx +0 -3
  183. package/dist/common/backends/webgl/shaders/blendFragmentShader.d.ts +0 -1
  184. package/dist/common/backends/webgl/shaders/blendFragmentShader.js +0 -21
  185. package/dist/common/backends/webgl/shaders/dualFilterBlurFragmentShader.d.ts +0 -1
  186. package/dist/common/backends/webgl/shaders/dualFilterBlurFragmentShader.js +0 -45
  187. package/dist/common/backends/webgl/shaders/smoothingMaskFragmentShader.d.ts +0 -1
  188. package/dist/common/backends/webgl/shaders/smoothingMaskFragmentShader.js +0 -57
  189. package/dist/common/backends/webgl/shaders/textureFragmentShader.d.ts +0 -1
  190. package/dist/common/backends/webgl/shaders/textureFragmentShader.js +0 -16
  191. package/dist/common/backends/webgl/shaders/vertexShader.d.ts +0 -1
  192. package/dist/common/backends/webgl/shaders/vertexShader.js +0 -19
  193. package/dist/main/transformer.d.ts +0 -1
  194. package/dist/main/transformer.js +0 -4
  195. package/dist/main/video/canvasTransform.d.ts +0 -17
  196. package/dist/main/video/canvasTransform.js +0 -253
  197. package/dist/main/video/segmenters/mediapipe.d.ts +0 -22
  198. package/dist/main/video/transformer.d.ts +0 -9
  199. package/dist/main/video/transformer.js +0 -68
  200. package/dist/main/video/videoStreamTrackProcessor.d.ts +0 -20
  201. package/dist/main/video/videoStreamTrackProcessor.js +0 -86
@@ -1,23 +1,59 @@
1
1
  import { assert } from '@pexip/utils';
2
- import { PROCESSING_WIDTH, PROCESSING_HEIGHT, EDGE_BLUR_AMOUNT, BACKGROUND_BLUR_AMOUNT, RENDERING_EVENTS, } from '../../constants';
3
- import { createTextureRenderer } from './texture';
4
- import { createBlurRenderer } from './blur';
2
+ import { BACKGROUND_BLUR_AMOUNT, PROCESSING_HEIGHT, PROCESSING_WIDTH, RENDERING_EVENTS, STRONG_EDGE_BLUR_AMOUNT, } from '../../constants';
3
+ import { createLazyProps, handleWebGLContextLoss } from '../../utils';
4
+ import { createBinaryMaskRenderer } from './binaryMask';
5
5
  import { createBlendRenderer } from './blender';
6
+ import { createBlurRenderer } from './blur';
6
7
  import { createCanvasRenderer } from './canvas';
7
- import { createTextureToTextureRenderer } from './textureToTexture';
8
+ import { createComponentSelector } from './selectComponent';
9
+ import { createGeodesicReconstructionRenderer } from './geodesicReconstruction';
10
+ import { createJointBilateralFilterRenderer } from './jointBilateralFilter';
11
+ import { createMorphErodeRenderer } from './morphErode';
12
+ import { createReplaceRenderer } from './replace';
8
13
  import { createSmoothingMaskRenderer } from './smoothingMask';
9
- import { createLazyProps, handleWebGLContextLoss } from '../../utils';
14
+ import { createTentBlurRenderer } from './tentBlur';
15
+ import { createTextureRenderer } from './texture';
16
+ import { createTextureToTextureRenderer } from './textureToTexture';
10
17
  export const createRenderer = (renderEventHandlers, canvas = new OffscreenCanvas(PROCESSING_WIDTH, PROCESSING_HEIGHT)) => {
18
+ const halfWidth = Math.trunc(canvas.width / 2);
19
+ const halfHeight = Math.trunc(canvas.height / 2);
20
+ const quarterWidth = Math.trunc(canvas.width / 4);
21
+ const quarterHeight = Math.trunc(canvas.height / 4);
22
+ // Width and Height used for the Center Component selection
23
+ const cclWidth = 32;
24
+ const cclHeight = 18;
11
25
  const rendererCreator = {
12
- videoFrameRenderer: () => createTextureRenderer(canvas),
13
- maskRenderer: () => createTextureRenderer(canvas),
14
- canvasRenderer: () => createCanvasRenderer(canvas),
15
- backgroundImageRenderer: () => createTextureRenderer(canvas),
26
+ backgroundImageRenderer: () => createTextureRenderer(canvas, false),
16
27
  blendRenderer: () => createBlendRenderer(canvas),
17
28
  blurRenderer: () => createBlurRenderer(canvas, BACKGROUND_BLUR_AMOUNT),
18
- maskBlurRenderer: () => createBlurRenderer(canvas, EDGE_BLUR_AMOUNT),
19
- textureRenderer: () => createTextureToTextureRenderer(canvas),
20
- smoothingMaskRenderer: () => createSmoothingMaskRenderer(canvas),
29
+ canvasRenderer: () => createCanvasRenderer(canvas),
30
+ frameDownsampler2: () => createTextureToTextureRenderer(canvas, false, true, halfWidth, halfHeight),
31
+ frameDownsampler4: () => createTextureToTextureRenderer(canvas, false, true, quarterWidth, quarterHeight),
32
+ geodesicReconstructionRenderer2: () => createGeodesicReconstructionRenderer(canvas, halfWidth, halfHeight),
33
+ geodesicReconstructionRenderer4: () => createGeodesicReconstructionRenderer(canvas, quarterWidth, quarterHeight),
34
+ jointBilateralFilterRenderer2: () => createJointBilateralFilterRenderer(canvas, halfWidth, halfHeight),
35
+ jointBilateralFilterRenderer4: () => createJointBilateralFilterRenderer(canvas, quarterWidth, quarterHeight),
36
+ jointBilateralFilterRenderer: () => createJointBilateralFilterRenderer(canvas, canvas.width, canvas.height),
37
+ lightWrapBlurRenderer: () => createBlurRenderer(canvas, BACKGROUND_BLUR_AMOUNT),
38
+ maskBlurRenderer: () => createTentBlurRenderer(canvas, canvas.width, canvas.height),
39
+ strongMaskBlurRenderer: () => createBlurRenderer(canvas, STRONG_EDGE_BLUR_AMOUNT),
40
+ maskDownsampler2: () => createTextureToTextureRenderer(canvas, true, false, halfWidth, halfHeight),
41
+ maskDownsampler4: () => createTextureToTextureRenderer(canvas, true, false, quarterWidth, quarterHeight),
42
+ maskRenderer: () => createTextureRenderer(canvas, true),
43
+ maskUpsampler2: () => createTextureToTextureRenderer(canvas, true, false, halfWidth, halfHeight),
44
+ maskUpsampler4: () => createTextureToTextureRenderer(canvas, true, false, quarterWidth, quarterHeight),
45
+ maskUpsampler: () => createTextureToTextureRenderer(canvas, true, false, canvas.width, canvas.height),
46
+ morphErodeRenderer: () => createMorphErodeRenderer(canvas, halfWidth, halfHeight),
47
+ morphErodeRenderer4: () => createMorphErodeRenderer(canvas, quarterWidth, quarterHeight),
48
+ prevMaskRenderer2: () => createTextureToTextureRenderer(canvas, true, false, halfWidth, halfHeight),
49
+ prevMaskRenderer4: () => createTextureToTextureRenderer(canvas, true, false, quarterWidth, quarterHeight),
50
+ replaceRenderer: () => createReplaceRenderer(canvas),
51
+ selectCenterComponent: () => createComponentSelector(canvas, cclWidth, cclHeight),
52
+ smoothingMaskRenderer4: () => createSmoothingMaskRenderer(canvas, quarterWidth, quarterHeight),
53
+ smoothingMaskRenderer2: () => createSmoothingMaskRenderer(canvas, halfWidth, halfHeight),
54
+ binaryMaskRenderer2: () => createBinaryMaskRenderer(canvas, halfWidth, halfHeight),
55
+ binaryMaskRenderer4: () => createBinaryMaskRenderer(canvas, quarterWidth, quarterHeight),
56
+ videoFrameRenderer: () => createTextureRenderer(canvas, false),
21
57
  release: (states) => () => {
22
58
  for (const key in states) {
23
59
  if (key !== 'release') {
@@ -35,6 +71,15 @@ export const createRenderer = (renderEventHandlers, canvas = new OffscreenCanvas
35
71
  get videoFrameRenderer() {
36
72
  return lazyProps.videoFrameRenderer;
37
73
  },
74
+ get jointBilateralFilterRenderer() {
75
+ return lazyProps.jointBilateralFilterRenderer;
76
+ },
77
+ get jointBilateralFilterRenderer2() {
78
+ return lazyProps.jointBilateralFilterRenderer2;
79
+ },
80
+ get jointBilateralFilterRenderer4() {
81
+ return lazyProps.jointBilateralFilterRenderer4;
82
+ },
38
83
  get maskRenderer() {
39
84
  return lazyProps.maskRenderer;
40
85
  },
@@ -47,18 +92,74 @@ export const createRenderer = (renderEventHandlers, canvas = new OffscreenCanvas
47
92
  get blendRenderer() {
48
93
  return lazyProps.blendRenderer;
49
94
  },
95
+ get replaceRenderer() {
96
+ return lazyProps.replaceRenderer;
97
+ },
50
98
  get blurRenderer() {
51
99
  return lazyProps.blurRenderer;
52
100
  },
53
- // TODO: Impl edge blur
101
+ get lightWrapBlurRenderer() {
102
+ return lazyProps.lightWrapBlurRenderer;
103
+ },
104
+ get prevMaskRenderer2() {
105
+ return lazyProps.prevMaskRenderer2;
106
+ },
107
+ get prevMaskRenderer4() {
108
+ return lazyProps.prevMaskRenderer4;
109
+ },
110
+ get selectCenterComponent() {
111
+ return lazyProps.selectCenterComponent;
112
+ },
113
+ get smoothingMaskRenderer2() {
114
+ return lazyProps.smoothingMaskRenderer2;
115
+ },
116
+ get smoothingMaskRenderer4() {
117
+ return lazyProps.smoothingMaskRenderer4;
118
+ },
119
+ get binaryMaskRenderer2() {
120
+ return lazyProps.binaryMaskRenderer2;
121
+ },
122
+ get binaryMaskRenderer4() {
123
+ return lazyProps.binaryMaskRenderer4;
124
+ },
125
+ get morphErodeRenderer() {
126
+ return lazyProps.morphErodeRenderer;
127
+ },
128
+ get morphErodeRenderer4() {
129
+ return lazyProps.morphErodeRenderer4;
130
+ },
54
131
  get maskBlurRenderer() {
55
132
  return lazyProps.maskBlurRenderer;
56
133
  },
57
- get textureRenderer() {
58
- return lazyProps.textureRenderer;
134
+ get strongMaskBlurRenderer() {
135
+ return lazyProps.strongMaskBlurRenderer;
136
+ },
137
+ get maskDownsampler2() {
138
+ return lazyProps.maskDownsampler2;
139
+ },
140
+ get maskDownsampler4() {
141
+ return lazyProps.maskDownsampler4;
142
+ },
143
+ get maskUpsampler() {
144
+ return lazyProps.maskUpsampler;
145
+ },
146
+ get maskUpsampler2() {
147
+ return lazyProps.maskUpsampler2;
148
+ },
149
+ get maskUpsampler4() {
150
+ return lazyProps.maskUpsampler4;
151
+ },
152
+ get frameDownsampler2() {
153
+ return lazyProps.frameDownsampler2;
59
154
  },
60
- get smoothingMaskRenderer() {
61
- return lazyProps.smoothingMaskRenderer;
155
+ get frameDownsampler4() {
156
+ return lazyProps.frameDownsampler4;
157
+ },
158
+ get geodesicReconstructionRenderer2() {
159
+ return lazyProps.geodesicReconstructionRenderer2;
160
+ },
161
+ get geodesicReconstructionRenderer4() {
162
+ return lazyProps.geodesicReconstructionRenderer4;
62
163
  },
63
164
  };
64
165
  const init = () => {
@@ -75,43 +176,92 @@ export const createRenderer = (renderEventHandlers, canvas = new OffscreenCanvas
75
176
  };
76
177
  const render = (mask, frame, options) => {
77
178
  assert(!props.gl?.isContextLost(), RENDERING_EVENTS.ContextLost);
78
- const maskTexture = props.maskRenderer.render({
79
- frame: mask,
80
- width: mask.width,
81
- height: mask.height,
82
- timestamp: frame.timestamp,
83
- });
84
- const smoothedMask = props.smoothingMaskRenderer.render({
85
- mask: maskTexture,
86
- prevMask: props.prevMask,
87
- }, options);
179
+ // 1. Upload current frame and raw mask
88
180
  const frameTexture = props.videoFrameRenderer.render(frame);
89
- let background = frameTexture;
181
+ const maskTexture = props.maskRenderer.render(mask);
182
+ // 2. Joint Bilateral Filter
183
+ let smoothedMask;
184
+ let compositeMaskInput;
185
+ switch (options.downSampleFactor) {
186
+ case 4: {
187
+ const maskLowRes = props.maskDownsampler4.render(maskTexture);
188
+ const frameLowRes = props.frameDownsampler4.render(frameTexture);
189
+ const filteredMaskLowRes = props.jointBilateralFilterRenderer4.render({ input: maskLowRes, guide: frameLowRes }, options);
190
+ smoothedMask = props.smoothingMaskRenderer4.render({
191
+ mask: filteredMaskLowRes,
192
+ prevMask: props.prevMask4 ?? filteredMaskLowRes,
193
+ }, options);
194
+ if (options.excludeBystanders) {
195
+ const binary = props.binaryMaskRenderer4.render(smoothedMask, options.foregroundThreshold);
196
+ const eroded = props.morphErodeRenderer4.render({ mask: binary }, options);
197
+ const selected = props.selectCenterComponent.render(eroded, options);
198
+ const filted = props.maskUpsampler4.render(selected);
199
+ smoothedMask = props.geodesicReconstructionRenderer4.render({ mask: filted, maskRef: smoothedMask }, options);
200
+ }
201
+ // Upsample to original size
202
+ const result = props.maskUpsampler.render(smoothedMask);
203
+ compositeMaskInput = props.strongMaskBlurRenderer.render(result, STRONG_EDGE_BLUR_AMOUNT);
204
+ break;
205
+ }
206
+ default: {
207
+ const maskLowRes = props.maskDownsampler2.render(maskTexture);
208
+ const frameLowRes = props.frameDownsampler2.render(frameTexture);
209
+ const filteredMaskLowRes = props.jointBilateralFilterRenderer2.render({ input: maskLowRes, guide: frameLowRes }, options);
210
+ smoothedMask = props.smoothingMaskRenderer2.render({
211
+ mask: filteredMaskLowRes,
212
+ prevMask: props.prevMask2 ?? filteredMaskLowRes,
213
+ }, options);
214
+ if (options.excludeBystanders) {
215
+ const binary = props.binaryMaskRenderer2.render(smoothedMask, options.foregroundThreshold);
216
+ const eroded = props.morphErodeRenderer.render({ mask: binary }, options);
217
+ const selected = props.selectCenterComponent.render(eroded, options);
218
+ const filted = props.maskUpsampler2.render(selected);
219
+ smoothedMask = props.geodesicReconstructionRenderer2.render({ mask: filted, maskRef: smoothedMask }, options);
220
+ }
221
+ // Upsample to original size
222
+ const result = props.maskUpsampler.render(smoothedMask);
223
+ compositeMaskInput = props.maskBlurRenderer.render(result, options.edgeBlurAmount);
224
+ break;
225
+ }
226
+ }
227
+ // 5. Composite final output
228
+ let outputFrameTexture = frameTexture;
90
229
  if (options.effects === 'blur' && options.backgroundBlurAmount) {
91
- background = props.blurRenderer.render(frameTexture, options.backgroundBlurAmount);
230
+ const background = props.blurRenderer.render(frameTexture, options.backgroundBlurAmount);
231
+ outputFrameTexture = props.blendRenderer.render({
232
+ mask: compositeMaskInput,
233
+ foreground: frameTexture,
234
+ background,
235
+ }, options);
92
236
  }
93
237
  else if (options?.effects === 'overlay' && options.backgroundImage) {
94
- background = props.backgroundImageRenderer.render({
95
- frame: options.backgroundImage,
96
- timestamp: performance.now(),
97
- width: options.backgroundImage.width,
98
- height: options.backgroundImage.height,
99
- });
238
+ const background = props.backgroundImageRenderer.render(options.backgroundImage);
239
+ outputFrameTexture = props.replaceRenderer.render({
240
+ mask: compositeMaskInput,
241
+ foreground: frameTexture,
242
+ background,
243
+ blurredBackground: props.lightWrapBlurRenderer.render(background, options.lightWrapBlurAmount),
244
+ timestamp: frame.timestamp,
245
+ }, options);
246
+ }
247
+ // 6. Output to present
248
+ props.canvasRenderer.render(outputFrameTexture);
249
+ // 7. Cloned the current smoothedMask & frame so it can be fed back to the next round
250
+ switch (options.downSampleFactor) {
251
+ case 4:
252
+ props.prevMask4 = props.prevMaskRenderer4.render(smoothedMask);
253
+ break;
254
+ default:
255
+ props.prevMask2 = props.prevMaskRenderer2.render(smoothedMask);
256
+ break;
100
257
  }
101
- const blendedFrameTexture = props.blendRenderer.render({
102
- mask: smoothedMask,
103
- foreground: frameTexture,
104
- background,
105
- });
106
- props.canvasRenderer.render(blendedFrameTexture);
107
- // Cloned the current smoothedMask so it can be fed back to the next round
108
- props.prevMask = props.textureRenderer.render(smoothedMask);
109
258
  };
110
259
  const cleanup = () => {
111
260
  props.canvasContextEventsCleanup();
112
261
  lazyProps.release();
113
262
  lazyProps = createLazyProps(rendererCreator);
114
- props.prevMask = undefined;
263
+ props.prevMask2 = undefined;
264
+ props.prevMask4 = undefined;
115
265
  };
116
266
  const release = () => {
117
267
  cleanup();
@@ -0,0 +1,17 @@
1
+ import type { TextureInfo } from './types';
2
+ import type { RendererOptions } from '../../types/render';
3
+ /**
4
+ * Create a background replacement renderer that composites
5
+ * the foreground and background as well as adding light wrap
6
+ * effect based on the mask and blurred background.
7
+ */
8
+ export declare const createReplaceRenderer: (processor: OffscreenCanvas) => {
9
+ render: ({ mask, foreground, background, blurredBackground, timestamp, }: {
10
+ mask: TextureInfo;
11
+ foreground: TextureInfo;
12
+ background: TextureInfo;
13
+ blurredBackground: TextureInfo;
14
+ timestamp: number;
15
+ }, options: RendererOptions) => TextureInfo;
16
+ release: () => void;
17
+ };
@@ -0,0 +1,126 @@
1
+ import { assert } from '@pexip/utils';
2
+ import { RENDERING_EVENTS } from '../../constants';
3
+ import { m3 } from '../../m3';
4
+ import { createGLProgram, createRGBAFrameTexture, createFrameBuffer, } from './webglUtils';
5
+ import vertexSrc from './shaders/matrix.vert';
6
+ import fragmentSrc from './shaders/replace.frag';
7
+ /**
8
+ * Create a background replacement renderer that composites
9
+ * the foreground and background as well as adding light wrap
10
+ * effect based on the mask and blurred background.
11
+ */
12
+ export const createReplaceRenderer = (processor) => {
13
+ const { gl, program, attribLocations, floatFullChannelSupported, uniformLocations, release, } = createGLProgram(processor, {
14
+ attribs: {
15
+ position: 'a_position',
16
+ texCoord: 'a_texCoord',
17
+ },
18
+ uniforms: {
19
+ matrix: 'u_matrix',
20
+ frameBackground: 'u_frame_background',
21
+ frameForeground: 'u_frame_foreground',
22
+ blurBackground: 'u_blur_background',
23
+ mask: 'u_mask',
24
+ confidenceThresholds: 'u_confidence_thresholds',
25
+ lightWrap: 'u_light_wrap',
26
+ lightWrapTightness: 'u_light_wrap_tightness',
27
+ lightWrapEdgeBand: 'u_edge_band',
28
+ time: 'u_time',
29
+ parity: 'u_parity',
30
+ frameSize: 'u_frame_size',
31
+ },
32
+ vertexSrc,
33
+ fragmentSrc,
34
+ });
35
+ gl.enable(gl.BLEND);
36
+ const vao = gl.createVertexArray();
37
+ gl.bindVertexArray(vao);
38
+ // Position
39
+ const positionBuffer = gl.createBuffer();
40
+ gl.enableVertexAttribArray(attribLocations.position);
41
+ gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
42
+ gl.vertexAttribPointer(attribLocations.position, 2, gl.FLOAT, false, 0, 0);
43
+ gl.bufferData(gl.ARRAY_BUFFER,
44
+ // prettier-ignore
45
+ new Float32Array([
46
+ 0, 0,
47
+ 0, 1,
48
+ 1, 0,
49
+ 1, 0,
50
+ 0, 1,
51
+ 1, 1,
52
+ ]), gl.STATIC_DRAW);
53
+ // Texture coordinates for the rectangle
54
+ const texCoordBuffer = gl.createBuffer();
55
+ gl.bindBuffer(gl.ARRAY_BUFFER, texCoordBuffer);
56
+ gl.bufferData(gl.ARRAY_BUFFER,
57
+ // prettier-ignore
58
+ new Float32Array([
59
+ 0, 0,
60
+ 0, 1,
61
+ 1, 0,
62
+ 1, 0,
63
+ 0, 1,
64
+ 1, 1,
65
+ ]), gl.STATIC_DRAW);
66
+ gl.enableVertexAttribArray(attribLocations.texCoord);
67
+ gl.vertexAttribPointer(attribLocations.texCoord, 2, gl.FLOAT, false, 0, 0);
68
+ const outputTexture = createRGBAFrameTexture(gl, processor.width, processor.height, true, floatFullChannelSupported);
69
+ const frameBuffer = createFrameBuffer(gl, outputTexture);
70
+ let parity = -1;
71
+ return {
72
+ render: ({ mask, foreground, background, blurredBackground, timestamp, }, options) => {
73
+ assert(!gl.isContextLost(), RENDERING_EVENTS.ContextLost);
74
+ gl.bindFramebuffer(gl.FRAMEBUFFER, frameBuffer);
75
+ gl.viewport(0, 0, processor.width, processor.height);
76
+ gl.useProgram(program);
77
+ gl.bindVertexArray(vao);
78
+ // Textures
79
+ gl.activeTexture(gl.TEXTURE0);
80
+ gl.uniform1i(uniformLocations.frameForeground, 0);
81
+ gl.bindTexture(gl.TEXTURE_2D, foreground.texture);
82
+ gl.activeTexture(gl.TEXTURE1);
83
+ gl.uniform1i(uniformLocations.frameBackground, 1);
84
+ gl.bindTexture(gl.TEXTURE_2D, background.texture);
85
+ gl.activeTexture(gl.TEXTURE2);
86
+ gl.uniform1i(uniformLocations.mask, 2);
87
+ gl.bindTexture(gl.TEXTURE_2D, mask.texture);
88
+ gl.activeTexture(gl.TEXTURE3);
89
+ gl.uniform1i(uniformLocations.blurBackground, 3);
90
+ gl.bindTexture(gl.TEXTURE_2D, blurredBackground.texture);
91
+ gl.uniform2f(uniformLocations.confidenceThresholds, options.backgroundThreshold, options.foregroundThreshold);
92
+ gl.uniform1f(uniformLocations.lightWrap, options.lightWrapIntensity);
93
+ gl.uniform1f(uniformLocations.lightWrapTightness, options.lightWrapTightness);
94
+ gl.uniform1f(uniformLocations.lightWrapEdgeBand, options.lightWrapEdgeBand);
95
+ parity = -parity;
96
+ gl.uniform1f(uniformLocations.parity, parity);
97
+ gl.uniform1f(uniformLocations.time,
98
+ // Use seconds and wrap to a modest range to avoid mediump precision loss
99
+ (timestamp * 1e-6) % 1024.0);
100
+ gl.uniform2f(uniformLocations.frameSize, processor.width, processor.height);
101
+ // No Flip Y Coordinate for drawing on the frame buffer
102
+ // Project, position and scale to full-size of the canvas
103
+ const matrix = m3.projection(1, 1, 1);
104
+ gl.uniformMatrix3fv(uniformLocations.matrix, false, matrix);
105
+ gl.drawArrays(gl.TRIANGLES, 0, 6);
106
+ // Cleanup
107
+ gl.bindBuffer(gl.ARRAY_BUFFER, null);
108
+ gl.bindFramebuffer(gl.FRAMEBUFFER, null);
109
+ gl.bindTexture(gl.TEXTURE_2D, null);
110
+ gl.bindVertexArray(null);
111
+ return {
112
+ texture: outputTexture,
113
+ width: processor.width,
114
+ height: processor.height,
115
+ };
116
+ },
117
+ release: () => {
118
+ gl.deleteBuffer(positionBuffer);
119
+ gl.deleteBuffer(texCoordBuffer);
120
+ gl.deleteTexture(outputTexture);
121
+ gl.deleteFramebuffer(frameBuffer);
122
+ gl.deleteVertexArray(vao);
123
+ release();
124
+ },
125
+ };
126
+ };
@@ -0,0 +1,15 @@
1
+ import type { RendererOptions } from '../../types/render';
2
+ import type { TextureInfo } from './types';
3
+ /**
4
+ * Based on Connected Component Labelling algorithm,
5
+ * find the center-most connected component in the mask and
6
+ * return a binary mask of just that component.
7
+ *
8
+ * @param processor - OffscreenCanvas for WebGL context
9
+ * @param width - target width of the downsampled mask to process (e.g. 32)
10
+ * @param height - target height of the downsampled mask to process (e.g. 18)
11
+ */
12
+ export declare function createComponentSelector(processor: OffscreenCanvas, width: number, height: number): {
13
+ render: (softMask: TextureInfo, options: RendererOptions) => TextureInfo;
14
+ release: () => void;
15
+ };
@@ -0,0 +1,102 @@
1
+ import { assert } from '@pexip/utils';
2
+ import { RENDERING_EVENTS } from '../../constants';
3
+ import { createTextureToTextureRenderer } from './textureToTexture';
4
+ import { scoreComponent, labelComponents, iou } from '../../utils';
5
+ /**
6
+ * Based on Connected Component Labelling algorithm,
7
+ * find the center-most connected component in the mask and
8
+ * return a binary mask of just that component.
9
+ *
10
+ * @param processor - OffscreenCanvas for WebGL context
11
+ * @param width - target width of the downsampled mask to process (e.g. 32)
12
+ * @param height - target height of the downsampled mask to process (e.g. 18)
13
+ */
14
+ export function createComponentSelector(processor, width, height) {
15
+ const gl = processor.getContext('webgl2', {
16
+ premultipliedAlpha: false,
17
+ powerPreference: 'high-performance',
18
+ });
19
+ const downSampler = createTextureToTextureRenderer(processor, true, false, width, height, false);
20
+ assert(gl, RENDERING_EVENTS.ContextCreationError);
21
+ const labels = new Int32Array(width * height);
22
+ const data = new Uint8Array(width * height);
23
+ const bin = new Uint8Array(width * height);
24
+ const bestMask = new Uint8Array(width * height);
25
+ const labelledMask = new Uint8Array(width * height);
26
+ let prevPrimaryScore = 0;
27
+ const outputTexture = gl.createTexture();
28
+ gl.bindTexture(gl.TEXTURE_2D, outputTexture);
29
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
30
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
31
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
32
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
33
+ const fbo = gl.createFramebuffer();
34
+ const config = {
35
+ // Frames persisted = -Math.log(1 + switchThreshold) / Math.log(decay)
36
+ // Width 0.3 switchThreshold and 0.95 decay, it persists about 6 frames
37
+ switchThreshold: 0.3,
38
+ decay: 0.95,
39
+ // Weights
40
+ area: 0.2,
41
+ height: 0.4,
42
+ center: 0.2,
43
+ iou: 0.2,
44
+ };
45
+ return {
46
+ render: (softMask, options) => {
47
+ const smallMask = downSampler.render(softMask);
48
+ // Read back single-channel 8-bit for simplicity here
49
+ gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
50
+ gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, smallMask.texture, 0);
51
+ gl.readPixels(0, 0, width, height, gl.RED, gl.UNSIGNED_BYTE, data);
52
+ gl.bindFramebuffer(gl.FRAMEBUFFER, null);
53
+ // Binarize using foregroundThreshold in 0..255 domain
54
+ const thresh = Math.max(0, Math.min(255, Math.floor(255 * options.foregroundThreshold)));
55
+ for (let i = 0; i < bin.length; i++) {
56
+ //@ts-expect-error will not be undefined
57
+ bin[i] = data[i] >= thresh ? 255 : 0;
58
+ }
59
+ let bestScore = -Infinity;
60
+ let bestLabel = 0;
61
+ const stats = labelComponents(bin, labels, width, height);
62
+ for (const [l, s] of stats.entries()) {
63
+ if (l === 0) {
64
+ continue; // background
65
+ }
66
+ for (let i = 0; i < labels.length; i++) {
67
+ labelledMask[i] = labels[i] === l ? 255 : 0;
68
+ }
69
+ const score = scoreComponent(s, iou(bestMask, labelledMask), {
70
+ ...config,
71
+ centerX: options.personCenter[0] * width,
72
+ centerY: options.personCenter[1] * height,
73
+ }, width, height);
74
+ if (score > bestScore) {
75
+ bestScore = score;
76
+ bestLabel = l;
77
+ }
78
+ }
79
+ // Hysteresis: switch only if significantly better
80
+ const shouldSwitch = bestScore > prevPrimaryScore * (1.0 + config.switchThreshold);
81
+ if (shouldSwitch || prevPrimaryScore === 0) {
82
+ for (let i = 0; i < labels.length; i++) {
83
+ bestMask[i] = labels[i] === bestLabel ? 255 : 0;
84
+ }
85
+ prevPrimaryScore = bestScore;
86
+ }
87
+ else {
88
+ prevPrimaryScore *= config.decay;
89
+ }
90
+ // Upload small filtered mask to GPU and return as TextureInfo (caller will upsample & combine)
91
+ gl.bindTexture(gl.TEXTURE_2D, outputTexture);
92
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.R8, width, height, 0, gl.RED, gl.UNSIGNED_BYTE, bestMask);
93
+ gl.bindTexture(gl.TEXTURE_2D, null);
94
+ return { texture: outputTexture, width, height };
95
+ },
96
+ release: () => {
97
+ gl.deleteTexture(outputTexture);
98
+ gl.deleteFramebuffer(fbo);
99
+ downSampler.release();
100
+ },
101
+ };
102
+ }
@@ -3,9 +3,9 @@ import type { TextureInfo } from './types';
3
3
  /**
4
4
  * Smooth the mask edge
5
5
  */
6
- export declare const createSmoothingMaskRenderer: (processor: OffscreenCanvas) => {
7
- render: ({ mask, prevMask, }: {
8
- prevMask?: TextureInfo;
6
+ export declare const createSmoothingMaskRenderer: (processor: OffscreenCanvas, width: number, height: number) => {
7
+ render: ({ mask, prevMask }: {
8
+ prevMask: TextureInfo;
9
9
  mask: TextureInfo;
10
10
  }, options: RendererOptions) => TextureInfo;
11
11
  release: () => void;
@@ -1,14 +1,14 @@
1
1
  import { assert } from '@pexip/utils';
2
2
  import { m3 } from '../../m3';
3
3
  import { RENDERING_EVENTS } from '../../constants';
4
- import { createGLProgram, createAndSetupTexture } from './webglUtils';
5
- import { vertexShaderSource } from './shaders/vertexShader';
6
- import { smoothingMaskFragmentShaderSource } from './shaders/smoothingMaskFragmentShader';
4
+ import { createGLProgram, createFrameBuffer, createMaskTexture, } from './webglUtils';
5
+ import vertexSrc from './shaders/matrix.vert';
6
+ import fragmentSrc from './shaders/smoothingMask.frag';
7
7
  /**
8
8
  * Smooth the mask edge
9
9
  */
10
- export const createSmoothingMaskRenderer = (processor) => {
11
- const { gl, program, attribLocations, uniformLocations, release } = createGLProgram(processor, {
10
+ export const createSmoothingMaskRenderer = (processor, width, height) => {
11
+ const { gl, program, attribLocations, uniformLocations, floatFullChannelSupported, floatSingleChannelSupported, release, } = createGLProgram(processor, {
12
12
  attribs: {
13
13
  position: 'a_position',
14
14
  texCoord: 'a_texCoord',
@@ -16,12 +16,13 @@ export const createSmoothingMaskRenderer = (processor) => {
16
16
  uniforms: {
17
17
  matrix: 'u_matrix',
18
18
  mask: 'u_mask',
19
- maskCombineRatio: 'u_mask_combine_ratio',
20
- foregroundThreshold: 'u_foreground_threshold',
21
19
  prevMask: 'u_mask_prev',
20
+ frame: 'u_frame',
21
+ prevFrame: 'u_frame_prev',
22
+ maskCombineRatio: 'u_mask_combine_ratio',
22
23
  },
23
- vertexSrc: vertexShaderSource,
24
- fragmentSrc: smoothingMaskFragmentShaderSource,
24
+ vertexSrc,
25
+ fragmentSrc,
25
26
  });
26
27
  const vao = gl.createVertexArray();
27
28
  gl.bindVertexArray(vao);
@@ -55,16 +56,13 @@ export const createSmoothingMaskRenderer = (processor) => {
55
56
  ]), gl.STATIC_DRAW);
56
57
  gl.enableVertexAttribArray(attribLocations.texCoord);
57
58
  gl.vertexAttribPointer(attribLocations.texCoord, 2, gl.FLOAT, false, 0, 0);
58
- const outputTexture = createAndSetupTexture(gl);
59
- gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, processor.width, processor.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
60
- const frameBuffer = gl.createFramebuffer();
61
- gl.bindFramebuffer(gl.FRAMEBUFFER, frameBuffer);
62
- gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, outputTexture, 0);
59
+ const outputTexture = createMaskTexture(gl, width, height, false, floatSingleChannelSupported, floatFullChannelSupported);
60
+ const frameBuffer = createFrameBuffer(gl, outputTexture);
63
61
  return {
64
- render: ({ mask, prevMask, }, options) => {
62
+ render: ({ mask, prevMask }, options) => {
65
63
  assert(!gl.isContextLost(), RENDERING_EVENTS.ContextLost);
66
64
  gl.bindFramebuffer(gl.FRAMEBUFFER, frameBuffer);
67
- gl.viewport(0, 0, processor.width, processor.height);
65
+ gl.viewport(0, 0, mask.width, mask.height);
68
66
  gl.clearColor(0, 0, 0, 0);
69
67
  gl.clear(gl.COLOR_BUFFER_BIT);
70
68
  gl.useProgram(program);
@@ -75,19 +73,22 @@ export const createSmoothingMaskRenderer = (processor) => {
75
73
  gl.bindTexture(gl.TEXTURE_2D, mask.texture);
76
74
  gl.activeTexture(gl.TEXTURE1);
77
75
  gl.uniform1i(uniformLocations.prevMask, 1);
78
- gl.bindTexture(gl.TEXTURE_2D, prevMask?.texture ?? mask.texture);
76
+ gl.bindTexture(gl.TEXTURE_2D, prevMask.texture);
79
77
  // No Flip Y Coordinate for drawing on the frame buffer
80
78
  // Project, position and scale to full-size of the canvas
81
79
  const matrix = m3.projection(1, 1, 1);
82
80
  gl.uniformMatrix3fv(uniformLocations.matrix, false, matrix);
83
81
  gl.uniform1f(uniformLocations.maskCombineRatio, options.maskCombineRatio);
84
- gl.uniform1f(uniformLocations.foregroundThreshold, options.foregroundThreshold);
85
82
  gl.drawArrays(gl.TRIANGLES, 0, 6);
86
- assert(outputTexture);
83
+ // Cleanup
84
+ gl.bindBuffer(gl.ARRAY_BUFFER, null);
85
+ gl.bindFramebuffer(gl.FRAMEBUFFER, null);
86
+ gl.bindTexture(gl.TEXTURE_2D, null);
87
+ gl.bindVertexArray(null);
87
88
  return {
88
89
  texture: outputTexture,
89
- width: processor.width,
90
- height: processor.height,
90
+ width,
91
+ height,
91
92
  };
92
93
  },
93
94
  release: () => {
@@ -0,0 +1,9 @@
1
+ import type { TextureInfo } from './types';
2
+ /**
3
+ * Create a tent blur renderer for small radius blurring of a mask.
4
+ * The blur is implemented as a separable filter.
5
+ */
6
+ export declare const createTentBlurRenderer: (processor: OffscreenCanvas, width: number, height: number) => {
7
+ render: (mask: TextureInfo, radiusPx: number) => TextureInfo;
8
+ release: () => void;
9
+ };