@webex/ladon-ts 5.9.2 → 5.10.1

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 (41) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/build/module/_virtual/_commonjsHelpers.js +7 -0
  3. package/build/module/_virtual/_commonjsHelpers.js.map +1 -0
  4. package/build/module/_virtual/logger.js +8 -0
  5. package/build/module/_virtual/logger.js.map +1 -0
  6. package/build/module/_virtual/logger2.js +5 -0
  7. package/build/module/_virtual/logger2.js.map +1 -0
  8. package/build/module/index.d.ts +1 -0
  9. package/build/module/index.js +38 -34
  10. package/build/module/index.js.map +1 -1
  11. package/build/module/lib/config-builder.js +56 -81
  12. package/build/module/lib/config-builder.js.map +1 -1
  13. package/build/module/lib/config.js +10 -9
  14. package/build/module/lib/config.js.map +1 -1
  15. package/build/module/lib/logger.d.ts +6 -0
  16. package/build/module/lib/logger.js +22 -0
  17. package/build/module/lib/logger.js.map +1 -0
  18. package/build/module/lib/pipeline.js +35 -37
  19. package/build/module/lib/pipeline.js.map +1 -1
  20. package/build/module/lib/renderer/blur-renderer.js +20 -30
  21. package/build/module/lib/renderer/blur-renderer.js.map +1 -1
  22. package/build/module/lib/renderer/replacement-renderer.js +7 -8
  23. package/build/module/lib/renderer/replacement-renderer.js.map +1 -1
  24. package/build/module/lib/renderer/webgl-renderer.js +14 -27
  25. package/build/module/lib/renderer/webgl-renderer.js.map +1 -1
  26. package/build/module/lib/resource-loader.js +8 -11
  27. package/build/module/lib/resource-loader.js.map +1 -1
  28. package/build/module/lib/util.js +40 -43
  29. package/build/module/lib/util.js.map +1 -1
  30. package/build/module/lib/version.d.ts +1 -1
  31. package/build/module/lib/version.js +1 -1
  32. package/build/module/lib/version.js.map +1 -1
  33. package/build/module/lib/worker-generator.js +7 -6
  34. package/build/module/lib/worker-generator.js.map +1 -1
  35. package/build/module/lib/worker-manager.js +14 -19
  36. package/build/module/lib/worker-manager.js.map +1 -1
  37. package/build/module/node_modules/js-logger/src/logger.js +108 -0
  38. package/build/module/node_modules/js-logger/src/logger.js.map +1 -0
  39. package/build/worker/segmentation.js +826 -822
  40. package/build/worker/segmentation.js.map +1 -1
  41. package/package.json +4 -1
@@ -1,9 +1,10 @@
1
- import { WorkerGenerator as f } from "./worker-generator.js";
2
- import { BlurRenderer as w } from "./renderer/blur-renderer.js";
3
- import { PassthroughRenderer as F } from "./renderer/passthrough-renderer.js";
4
- import { ReplacementRenderer as k } from "./renderer/replacement-renderer.js";
1
+ import { WorkerGenerator as w } from "./worker-generator.js";
2
+ import { logger as s } from "./logger.js";
3
+ import { BlurRenderer as F } from "./renderer/blur-renderer.js";
4
+ import { PassthroughRenderer as k } from "./renderer/passthrough-renderer.js";
5
+ import { ReplacementRenderer as C } from "./renderer/replacement-renderer.js";
5
6
  import { resizeImage as y } from "./util.js";
6
- class A {
7
+ class D {
7
8
  /**
8
9
  * Create a new pipeline instance.
9
10
  *
@@ -13,15 +14,13 @@ class A {
13
14
  * @param inputVideoElement - Video element serving as the input source.
14
15
  * @throws Error if the input canvas 2D context cannot be obtained.
15
16
  */
16
- constructor(e, t, r, n) {
17
- this.inputCanvas = e, this.outputCanvas = t, this.config = r, this.inputVideoElement = n, this.loaded = !1, this.isProcessingFrame = !1, this.pendingFramePromise = null;
18
- const s = this.inputCanvas.getContext("2d", {
17
+ constructor(e, t, r, a) {
18
+ this.inputCanvas = e, this.outputCanvas = t, this.config = r, this.inputVideoElement = a, this.loaded = !1, this.isProcessingFrame = !1, this.pendingFramePromise = null;
19
+ const n = this.inputCanvas.getContext("2d", {
19
20
  alpha: !1,
20
21
  willReadFrequently: !0
21
22
  });
22
- if (!s)
23
- throw new Error("[ladon-ts] Error getting input context 2d");
24
- this.inputCanvasContext = s;
23
+ n || s.errorAndThrow("Error getting input context 2d"), this.inputCanvasContext = n;
25
24
  }
26
25
  /**
27
26
  * Process a single frame and return the inference result.
@@ -57,29 +56,28 @@ class A {
57
56
  * @throws Error if the pipeline (generator or renderer) is not loaded.
58
57
  */
59
58
  async processFrameInternal(e, t = !0, r) {
60
- const n = performance.now();
61
- if (!this.generator || !this.renderer)
62
- throw new Error("[ladon-ts] Pipeline is not loaded.");
63
- let s = 0, i = 0, a;
59
+ const a = performance.now();
60
+ (!this.generator || !this.renderer) && s.errorAndThrow("Pipeline is not loaded.");
61
+ let n = 0, o = 0, i;
64
62
  if (t || !this.lastMaskResult) {
65
- const m = performance.now();
66
- r ?? (r = this.getInputFrameData()), a = this.createFrameDataCopy(r), s = performance.now() - m;
67
- const c = performance.now(), p = r, g = this.getMaskImageData(p);
68
- await this.generator.process(g, e), this.lastMaskResult = await this.generator.getLadonOutputForTimestamp(e), i = performance.now() - c;
63
+ const g = performance.now();
64
+ r ?? (r = this.getInputFrameData()), i = this.createFrameDataCopy(r), n = performance.now() - g;
65
+ const c = performance.now(), p = r, f = this.getMaskImageData(p);
66
+ await this.generator.process(f, e), this.lastMaskResult = await this.generator.getLadonOutputForTimestamp(e), o = performance.now() - c;
69
67
  } else
70
- a = this.inputVideoElement;
71
- const d = performance.now();
72
- await this.renderer.render(a, this.lastMaskResult);
73
- const l = performance.now() - d, o = performance.now(), u = o - n, h = this.createInferenceResult(e, t);
74
- return h.stats = {
68
+ i = this.inputVideoElement;
69
+ const l = performance.now();
70
+ await this.renderer.render(i, this.lastMaskResult);
71
+ const m = performance.now() - l, h = performance.now(), u = h - a, d = this.createInferenceResult(e, t);
72
+ return d.stats = {
75
73
  totalFrameTime: u,
76
- modelInferenceTime: i,
77
- preprocessingTime: s,
78
- postprocessingTime: l,
79
- frameStartTime: n,
80
- frameEndTime: o,
74
+ modelInferenceTime: o,
75
+ preprocessingTime: n,
76
+ postprocessingTime: m,
77
+ frameStartTime: a,
78
+ frameEndTime: h,
81
79
  inferenceSkipped: !t
82
- }, h;
80
+ }, d;
83
81
  }
84
82
  /**
85
83
  * Get the raw image data from the input canvas context.
@@ -159,20 +157,20 @@ class A {
159
157
  * @throws Error if an unsupported renderer type is specified in the configuration.
160
158
  */
161
159
  async load() {
162
- switch (this.config.render.type) {
160
+ switch (s.log("Creating renderer"), this.config.render.type) {
163
161
  case "blur":
164
- this.renderer = new w(this.outputCanvas, this.config);
162
+ this.renderer = new F(this.outputCanvas, this.config);
165
163
  break;
166
164
  case "passthrough":
167
- this.renderer = new F(this.outputCanvas, this.config);
165
+ this.renderer = new k(this.outputCanvas, this.config);
168
166
  break;
169
167
  case "replacement":
170
- this.renderer = new k(this.outputCanvas, this.config);
168
+ this.renderer = new C(this.outputCanvas, this.config);
171
169
  break;
172
170
  default:
173
- throw new Error("[ladon-ts] Renderer not implemented.");
171
+ s.errorAndThrow("Renderer not implemented.");
174
172
  }
175
- await this.renderer.load(), this.generator = new f(this.config), await this.generator.load(), this.loaded = !0;
173
+ s.log("Loading renderer"), await this.renderer.load(), s.log("Creating generator"), this.generator = new w(this.config), s.log("Loading generator"), await this.generator.load(), this.loaded = !0;
176
174
  }
177
175
  /**
178
176
  * Reset the pipeline state by clearing cached mask results.
@@ -193,6 +191,6 @@ class A {
193
191
  }
194
192
  }
195
193
  export {
196
- A as Pipeline
194
+ D as Pipeline
197
195
  };
198
196
  //# sourceMappingURL=pipeline.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"pipeline.js","sources":["../../../src/lib/pipeline.ts"],"sourcesContent":["import { PipelineConfig } from './config-types';\nimport { FrameData, LadonOutput, MaskGenerator, WorkerGenerator } from './generator';\nimport { IFrameInferenceResult, IPipeline } from './pipeline-types';\nimport { BlurRenderer, LadonRenderer, PassthroughRenderer, ReplacementRenderer } from './renderer';\nimport { resizeImage } from './util';\n\n/**\n * Core pipeline implementation for processing video frames with ML inference and rendering.\n * Handles frame processing, mask generation, and output rendering with proper timing controls.\n */\nexport class Pipeline implements IPipeline {\n /** The mask generator responsible for ML inference. */\n private generator?: MaskGenerator;\n\n /** The renderer responsible for compositing the final output. */\n private renderer?: LadonRenderer;\n\n /** Flag indicating whether the pipeline has been fully loaded and initialized. */\n private loaded = false;\n\n /** 2D rendering context for the input canvas used for frame processing. */\n private inputCanvasContext: CanvasRenderingContext2D;\n\n /** The most recent mask result from ML inference, used when skipping inference. */\n private lastMaskResult?: LadonOutput;\n\n /** Flag indicating whether a frame is currently being processed. */\n private isProcessingFrame = false;\n\n /** Promise for the currently pending frame processing operation. */\n private pendingFramePromise: Promise<IFrameInferenceResult> | null = null;\n\n /** Buffer for frame data copies to ensure they are not modified or garbage collected. */\n private frameDataBuffer?: Uint8ClampedArray;\n\n /**\n * Create a new pipeline instance.\n *\n * @param inputCanvas - Canvas element for input frame processing.\n * @param outputCanvas - Canvas element where the final rendered output will be displayed.\n * @param config - Pipeline configuration containing processing and rendering settings.\n * @param inputVideoElement - Video element serving as the input source.\n * @throws Error if the input canvas 2D context cannot be obtained.\n */\n constructor(\n private inputCanvas: HTMLCanvasElement,\n private outputCanvas: HTMLCanvasElement,\n public readonly config: PipelineConfig,\n private readonly inputVideoElement: HTMLVideoElement\n ) {\n const inputCanvasContext = this.inputCanvas.getContext('2d', {\n alpha: false,\n willReadFrequently: true,\n });\n\n if (!inputCanvasContext) {\n throw new Error('[ladon-ts] Error getting input context 2d');\n }\n this.inputCanvasContext = inputCanvasContext;\n }\n\n /**\n * Process a single frame and return the inference result.\n * This method handles frame data, performs inference if needed, and renders the output.\n * Includes frame processing serialization to prevent overlapping operations.\n *\n * @param timestamp - The timestamp of the frame in milliseconds.\n * @param performInference - Whether to perform mask inference or reuse the last result.\n * @param frameData - Optional frame data to process instead of capturing from input.\n * @returns Promise resolving to the frame processing result with timing statistics.\n * @throws Error if the pipeline (generator or renderer) is not loaded.\n */\n public async onFrame(\n timestamp: number,\n performInference = true,\n frameData?: FrameData\n ): Promise<IFrameInferenceResult> {\n // If already processing, wait for the current frame to finish\n if (this.isProcessingFrame && this.pendingFramePromise) {\n await this.pendingFramePromise;\n }\n\n // Set processing flag\n this.isProcessingFrame = true;\n\n try {\n this.pendingFramePromise = this.processFrameInternal(\n timestamp,\n performInference,\n frameData\n );\n return await this.pendingFramePromise;\n } finally {\n this.isProcessingFrame = false;\n this.pendingFramePromise = null;\n }\n }\n\n /**\n * Internal frame processing implementation.\n * Handles the actual frame processing logic including timing measurements.\n *\n * @param timestamp - The timestamp of the frame in milliseconds.\n * @param performInference - Whether to perform mask inference or reuse the last result.\n * @param frameData - Optional frame data to process instead of capturing from input.\n * @returns Promise resolving to the frame processing result with timing statistics.\n * @throws Error if the pipeline (generator or renderer) is not loaded.\n */\n private async processFrameInternal(\n timestamp: number,\n performInference = true,\n frameData?: FrameData\n ): Promise<IFrameInferenceResult> {\n const frameStartTime = performance.now();\n\n if (!this.generator || !this.renderer) {\n throw new Error('[ladon-ts] Pipeline is not loaded.');\n }\n\n let preprocessingTime = 0;\n let modelInferenceTime = 0;\n let renderFrameData: FrameData;\n\n if (performInference || !this.lastMaskResult) {\n // INFERENCE PATH: Capture canvas data, generate mask, render with same data\n const preprocessStart = performance.now();\n\n // Get canvas data for inference\n frameData ??= this.getInputFrameData();\n\n // Create stable copy for rendering synchronization\n renderFrameData = this.createFrameDataCopy(frameData);\n\n preprocessingTime = performance.now() - preprocessStart;\n\n // Generate mask\n const inferenceStart = performance.now();\n const rawImageData = frameData as Uint8ClampedArray;\n const maskImageData = this.getMaskImageData(rawImageData);\n await this.generator.process(maskImageData, timestamp);\n this.lastMaskResult = await this.generator.getLadonOutputForTimestamp(timestamp);\n modelInferenceTime = performance.now() - inferenceStart;\n } else {\n // SKIP INFERENCE PATH: Use video element directly for GPU optimization\n renderFrameData = this.inputVideoElement;\n }\n\n // Render with appropriate frame data\n const postprocessStart = performance.now();\n await this.renderer.render(renderFrameData, this.lastMaskResult);\n const postprocessingTime = performance.now() - postprocessStart;\n\n const frameEndTime = performance.now();\n const totalFrameTime = frameEndTime - frameStartTime;\n\n const result = this.createInferenceResult(timestamp, performInference);\n result.stats = {\n totalFrameTime,\n modelInferenceTime,\n preprocessingTime,\n postprocessingTime,\n frameStartTime,\n frameEndTime,\n inferenceSkipped: !performInference,\n };\n\n return result;\n }\n\n /**\n * Get the raw image data from the input canvas context.\n *\n * @returns The frame data as a Uint8ClampedArray.\n */\n private getInputFrameData(): FrameData {\n return this.inputCanvasContext.getImageData(\n 0,\n 0,\n this.config.input.width,\n this.config.input.height\n ).data;\n }\n\n /**\n * Create a copy of frame data to prevent modifications during async operations.\n */\n private createFrameDataCopy(frameData: FrameData): FrameData {\n if (frameData instanceof Uint8ClampedArray) {\n if (this.frameDataBuffer?.length !== frameData.length) {\n this.frameDataBuffer = new Uint8ClampedArray(frameData.length);\n }\n this.frameDataBuffer.set(frameData);\n return this.frameDataBuffer;\n }\n return frameData;\n }\n\n /**\n * Resize the image data if the model expects a different input size.\n * Handles scaling between input dimensions and model requirements.\n *\n * @param rawImageData - The raw image data to potentially resize.\n * @returns The processed mask image data, resized if necessary.\n */\n private getMaskImageData(rawImageData: Uint8ClampedArray): Uint8ClampedArray {\n const modelInput = this.config.mask.inputSize;\n const inputImage = this.config.input;\n\n // Check if resizing is needed based on configuration.\n if (modelInput.width === inputImage.width && modelInput.height === inputImage.height) {\n return rawImageData;\n }\n\n // Resize the image data.\n return resizeImage(\n rawImageData,\n inputImage.width,\n inputImage.height,\n modelInput.width,\n modelInput.height\n );\n }\n\n /**\n * Create the inference result object with appropriate data.\n * Handles both successful inference results and fallback cases.\n *\n * @param timestamp - The timestamp of the frame in milliseconds.\n * @param performedInference - Whether inference was actually performed for this frame.\n * @returns The inference result object containing mask data and metadata.\n */\n private createInferenceResult(\n timestamp: number,\n performedInference: boolean\n ): IFrameInferenceResult {\n if (!this.lastMaskResult) {\n return {\n facesAndLandmarks: { data: new Float32Array(0), dims: [] },\n gesture: new Float32Array(0),\n motion: 0,\n skipped: true,\n timestamp,\n // Stats will be added by the caller if needed\n };\n }\n\n return {\n facesAndLandmarks: this.lastMaskResult.facesAndLandmarks,\n gesture: this.lastMaskResult.gesture,\n motion: this.lastMaskResult.motion,\n skipped: !performedInference,\n timestamp,\n // Stats will be added by the caller if needed\n };\n }\n\n /**\n * Check if the pipeline is fully loaded and ready for processing.\n *\n * @returns True if the pipeline is loaded and ready, false otherwise.\n */\n public isLoaded(): boolean {\n return this.loaded;\n }\n\n /**\n * Load and initialize the pipeline components.\n * Creates the appropriate renderer and generator based on configuration.\n *\n * @returns Promise that resolves when all components are loaded and ready.\n * @throws Error if an unsupported renderer type is specified in the configuration.\n */\n public async load(): Promise<void> {\n switch (this.config.render.type) {\n case 'blur':\n this.renderer = new BlurRenderer(this.outputCanvas, this.config);\n break;\n case 'passthrough':\n this.renderer = new PassthroughRenderer(this.outputCanvas, this.config);\n break;\n case 'replacement':\n this.renderer = new ReplacementRenderer(this.outputCanvas, this.config);\n break;\n default:\n throw new Error('[ladon-ts] Renderer not implemented.');\n }\n\n await this.renderer.load();\n\n this.generator = new WorkerGenerator(this.config);\n await this.generator.load();\n\n this.loaded = true;\n }\n\n /**\n * Reset the pipeline state by clearing cached mask results.\n * Useful when switching between different processing modes or sources.\n */\n public reset(): void {\n this.lastMaskResult = undefined;\n }\n\n /**\n * Destroy the pipeline and clean up all resources.\n * Properly disposes of the renderer and generator to prevent memory leaks.\n *\n * @returns Promise that resolves when cleanup is complete.\n */\n public async destroy(): Promise<void> {\n this.reset();\n this.renderer?.destroy();\n await this.generator?.destroy();\n this.loaded = false;\n }\n}\n"],"names":["Pipeline","inputCanvas","outputCanvas","config","inputVideoElement","inputCanvasContext","timestamp","performInference","frameData","frameStartTime","preprocessingTime","modelInferenceTime","renderFrameData","preprocessStart","inferenceStart","rawImageData","maskImageData","postprocessStart","postprocessingTime","frameEndTime","totalFrameTime","result","_a","modelInput","inputImage","resizeImage","performedInference","BlurRenderer","PassthroughRenderer","ReplacementRenderer","WorkerGenerator","_b"],"mappings":"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"}
1
+ {"version":3,"file":"pipeline.js","sources":["../../../src/lib/pipeline.ts"],"sourcesContent":["import { PipelineConfig } from './config-types';\nimport { FrameData, LadonOutput, MaskGenerator, WorkerGenerator } from './generator';\nimport { logger } from './logger';\nimport { IFrameInferenceResult, IPipeline } from './pipeline-types';\nimport { BlurRenderer, LadonRenderer, PassthroughRenderer, ReplacementRenderer } from './renderer';\nimport { resizeImage } from './util';\n\n/**\n * Core pipeline implementation for processing video frames with ML inference and rendering.\n * Handles frame processing, mask generation, and output rendering with proper timing controls.\n */\nexport class Pipeline implements IPipeline {\n /** The mask generator responsible for ML inference. */\n private generator?: MaskGenerator;\n\n /** The renderer responsible for compositing the final output. */\n private renderer?: LadonRenderer;\n\n /** Flag indicating whether the pipeline has been fully loaded and initialized. */\n private loaded = false;\n\n /** 2D rendering context for the input canvas used for frame processing. */\n private inputCanvasContext: CanvasRenderingContext2D;\n\n /** The most recent mask result from ML inference, used when skipping inference. */\n private lastMaskResult?: LadonOutput;\n\n /** Flag indicating whether a frame is currently being processed. */\n private isProcessingFrame = false;\n\n /** Promise for the currently pending frame processing operation. */\n private pendingFramePromise: Promise<IFrameInferenceResult> | null = null;\n\n /** Buffer for frame data copies to ensure they are not modified or garbage collected. */\n private frameDataBuffer?: Uint8ClampedArray;\n\n /**\n * Create a new pipeline instance.\n *\n * @param inputCanvas - Canvas element for input frame processing.\n * @param outputCanvas - Canvas element where the final rendered output will be displayed.\n * @param config - Pipeline configuration containing processing and rendering settings.\n * @param inputVideoElement - Video element serving as the input source.\n * @throws Error if the input canvas 2D context cannot be obtained.\n */\n constructor(\n private inputCanvas: HTMLCanvasElement,\n private outputCanvas: HTMLCanvasElement,\n public readonly config: PipelineConfig,\n private readonly inputVideoElement: HTMLVideoElement\n ) {\n const inputCanvasContext = this.inputCanvas.getContext('2d', {\n alpha: false,\n willReadFrequently: true,\n });\n\n if (!inputCanvasContext) {\n logger.errorAndThrow('Error getting input context 2d');\n }\n this.inputCanvasContext = inputCanvasContext;\n }\n\n /**\n * Process a single frame and return the inference result.\n * This method handles frame data, performs inference if needed, and renders the output.\n * Includes frame processing serialization to prevent overlapping operations.\n *\n * @param timestamp - The timestamp of the frame in milliseconds.\n * @param performInference - Whether to perform mask inference or reuse the last result.\n * @param frameData - Optional frame data to process instead of capturing from input.\n * @returns Promise resolving to the frame processing result with timing statistics.\n * @throws Error if the pipeline (generator or renderer) is not loaded.\n */\n public async onFrame(\n timestamp: number,\n performInference = true,\n frameData?: FrameData\n ): Promise<IFrameInferenceResult> {\n // If already processing, wait for the current frame to finish\n if (this.isProcessingFrame && this.pendingFramePromise) {\n await this.pendingFramePromise;\n }\n\n // Set processing flag\n this.isProcessingFrame = true;\n\n try {\n this.pendingFramePromise = this.processFrameInternal(\n timestamp,\n performInference,\n frameData\n );\n return await this.pendingFramePromise;\n } finally {\n this.isProcessingFrame = false;\n this.pendingFramePromise = null;\n }\n }\n\n /**\n * Internal frame processing implementation.\n * Handles the actual frame processing logic including timing measurements.\n *\n * @param timestamp - The timestamp of the frame in milliseconds.\n * @param performInference - Whether to perform mask inference or reuse the last result.\n * @param frameData - Optional frame data to process instead of capturing from input.\n * @returns Promise resolving to the frame processing result with timing statistics.\n * @throws Error if the pipeline (generator or renderer) is not loaded.\n */\n private async processFrameInternal(\n timestamp: number,\n performInference = true,\n frameData?: FrameData\n ): Promise<IFrameInferenceResult> {\n const frameStartTime = performance.now();\n\n if (!this.generator || !this.renderer) {\n logger.errorAndThrow('Pipeline is not loaded.');\n }\n\n let preprocessingTime = 0;\n let modelInferenceTime = 0;\n let renderFrameData: FrameData;\n\n if (performInference || !this.lastMaskResult) {\n // INFERENCE PATH: Capture canvas data, generate mask, render with same data\n const preprocessStart = performance.now();\n\n // Get canvas data for inference\n frameData ??= this.getInputFrameData();\n\n // Create stable copy for rendering synchronization\n renderFrameData = this.createFrameDataCopy(frameData);\n\n preprocessingTime = performance.now() - preprocessStart;\n\n // Generate mask\n const inferenceStart = performance.now();\n const rawImageData = frameData as Uint8ClampedArray;\n const maskImageData = this.getMaskImageData(rawImageData);\n await this.generator.process(maskImageData, timestamp);\n this.lastMaskResult = await this.generator.getLadonOutputForTimestamp(timestamp);\n modelInferenceTime = performance.now() - inferenceStart;\n } else {\n // SKIP INFERENCE PATH: Use video element directly for GPU optimization\n renderFrameData = this.inputVideoElement;\n }\n\n // Render with appropriate frame data\n const postprocessStart = performance.now();\n await this.renderer.render(renderFrameData, this.lastMaskResult);\n const postprocessingTime = performance.now() - postprocessStart;\n\n const frameEndTime = performance.now();\n const totalFrameTime = frameEndTime - frameStartTime;\n\n const result = this.createInferenceResult(timestamp, performInference);\n result.stats = {\n totalFrameTime,\n modelInferenceTime,\n preprocessingTime,\n postprocessingTime,\n frameStartTime,\n frameEndTime,\n inferenceSkipped: !performInference,\n };\n\n return result;\n }\n\n /**\n * Get the raw image data from the input canvas context.\n *\n * @returns The frame data as a Uint8ClampedArray.\n */\n private getInputFrameData(): FrameData {\n return this.inputCanvasContext.getImageData(\n 0,\n 0,\n this.config.input.width,\n this.config.input.height\n ).data;\n }\n\n /**\n * Create a copy of frame data to prevent modifications during async operations.\n */\n private createFrameDataCopy(frameData: FrameData): FrameData {\n if (frameData instanceof Uint8ClampedArray) {\n if (this.frameDataBuffer?.length !== frameData.length) {\n this.frameDataBuffer = new Uint8ClampedArray(frameData.length);\n }\n this.frameDataBuffer.set(frameData);\n return this.frameDataBuffer;\n }\n return frameData;\n }\n\n /**\n * Resize the image data if the model expects a different input size.\n * Handles scaling between input dimensions and model requirements.\n *\n * @param rawImageData - The raw image data to potentially resize.\n * @returns The processed mask image data, resized if necessary.\n */\n private getMaskImageData(rawImageData: Uint8ClampedArray): Uint8ClampedArray {\n const modelInput = this.config.mask.inputSize;\n const inputImage = this.config.input;\n\n // Check if resizing is needed based on configuration.\n if (modelInput.width === inputImage.width && modelInput.height === inputImage.height) {\n return rawImageData;\n }\n\n // Resize the image data.\n return resizeImage(\n rawImageData,\n inputImage.width,\n inputImage.height,\n modelInput.width,\n modelInput.height\n );\n }\n\n /**\n * Create the inference result object with appropriate data.\n * Handles both successful inference results and fallback cases.\n *\n * @param timestamp - The timestamp of the frame in milliseconds.\n * @param performedInference - Whether inference was actually performed for this frame.\n * @returns The inference result object containing mask data and metadata.\n */\n private createInferenceResult(\n timestamp: number,\n performedInference: boolean\n ): IFrameInferenceResult {\n if (!this.lastMaskResult) {\n return {\n facesAndLandmarks: { data: new Float32Array(0), dims: [] },\n gesture: new Float32Array(0),\n motion: 0,\n skipped: true,\n timestamp,\n // Stats will be added by the caller if needed\n };\n }\n\n return {\n facesAndLandmarks: this.lastMaskResult.facesAndLandmarks,\n gesture: this.lastMaskResult.gesture,\n motion: this.lastMaskResult.motion,\n skipped: !performedInference,\n timestamp,\n // Stats will be added by the caller if needed\n };\n }\n\n /**\n * Check if the pipeline is fully loaded and ready for processing.\n *\n * @returns True if the pipeline is loaded and ready, false otherwise.\n */\n public isLoaded(): boolean {\n return this.loaded;\n }\n\n /**\n * Load and initialize the pipeline components.\n * Creates the appropriate renderer and generator based on configuration.\n *\n * @returns Promise that resolves when all components are loaded and ready.\n * @throws Error if an unsupported renderer type is specified in the configuration.\n */\n public async load(): Promise<void> {\n logger.log('Creating renderer');\n switch (this.config.render.type) {\n case 'blur':\n this.renderer = new BlurRenderer(this.outputCanvas, this.config);\n break;\n case 'passthrough':\n this.renderer = new PassthroughRenderer(this.outputCanvas, this.config);\n break;\n case 'replacement':\n this.renderer = new ReplacementRenderer(this.outputCanvas, this.config);\n break;\n default:\n logger.errorAndThrow('Renderer not implemented.');\n }\n\n logger.log('Loading renderer');\n await this.renderer.load();\n\n logger.log('Creating generator');\n this.generator = new WorkerGenerator(this.config);\n\n logger.log('Loading generator');\n await this.generator.load();\n\n this.loaded = true;\n }\n\n /**\n * Reset the pipeline state by clearing cached mask results.\n * Useful when switching between different processing modes or sources.\n */\n public reset(): void {\n this.lastMaskResult = undefined;\n }\n\n /**\n * Destroy the pipeline and clean up all resources.\n * Properly disposes of the renderer and generator to prevent memory leaks.\n *\n * @returns Promise that resolves when cleanup is complete.\n */\n public async destroy(): Promise<void> {\n this.reset();\n this.renderer?.destroy();\n await this.generator?.destroy();\n this.loaded = false;\n }\n}\n"],"names":["Pipeline","inputCanvas","outputCanvas","config","inputVideoElement","inputCanvasContext","logger","timestamp","performInference","frameData","frameStartTime","preprocessingTime","modelInferenceTime","renderFrameData","preprocessStart","inferenceStart","rawImageData","maskImageData","postprocessStart","postprocessingTime","frameEndTime","totalFrameTime","result","_a","modelInput","inputImage","resizeImage","performedInference","BlurRenderer","PassthroughRenderer","ReplacementRenderer","WorkerGenerator","_b"],"mappings":"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"}
@@ -1,8 +1,9 @@
1
- import { compileShader as o, linkProgram as h } from "../util.js";
2
- import { WebGLRenderer as a } from "./webgl-renderer.js";
3
- class _ extends a {
1
+ import { logger as s } from "../logger.js";
2
+ import { compileShader as h, linkProgram as a } from "../util.js";
3
+ import { WebGLRenderer as g } from "./webgl-renderer.js";
4
+ class c extends g {
4
5
  constructor(e, i) {
5
- if (super(e, i), this.output_canvas = e, this.config = i, this.fragment_shader_common = `#version 300 es
6
+ super(e, i), this.output_canvas = e, this.config = i, this.fragment_shader_common = `#version 300 es
6
7
  precision highp float;
7
8
 
8
9
  #define KERNEL_SIZE ${this.config.render.kernelSize}
@@ -63,27 +64,22 @@ void main() {
63
64
  vec3 color = mix(original, blurred, mask); // Ensure mask properly determines blur influence on background
64
65
  outColor = vec4(color, 1.0);
65
66
  }
66
- `, this.config.render.kernelSize % 2 === 0)
67
- throw new Error("[ladon-ts] Kernel_size must be odd");
67
+ `, this.config.render.kernelSize % 2 === 0 && s.errorAndThrow("Kernel_size must be odd");
68
68
  {
69
- const r = o(
69
+ const r = h(
70
70
  this.gl,
71
71
  this.gl.VERTEX_SHADER,
72
72
  this.vertex_shader_src
73
- ), t = o(
73
+ ), t = h(
74
74
  this.gl,
75
75
  this.gl.FRAGMENT_SHADER,
76
76
  this.fragment_shader_pass_1_src
77
77
  );
78
- this.program_1 = h(this.gl, r, t), this.gl.useProgram(this.program_1);
79
- const s = this.gl.createFramebuffer();
80
- if (!s)
81
- throw new Error("[ladon-ts] Error creating framebuffer");
82
- this.framebuffer = s;
78
+ this.program_1 = a(this.gl, r, t), this.gl.useProgram(this.program_1);
79
+ const o = this.gl.createFramebuffer();
80
+ o || s.errorAndThrow("Error creating framebuffer"), this.framebuffer = o;
83
81
  const n = this.gl.createTexture();
84
- if (!n)
85
- throw new Error("[ladon-ts] Error creating output texture");
86
- this.transfer_texture = n, this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, this.framebuffer), this.gl.bindTexture(this.gl.TEXTURE_2D, this.transfer_texture), this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MIN_FILTER, this.gl.LINEAR), this.gl.texParameteri(
82
+ n || s.errorAndThrow("Error creating output texture"), this.transfer_texture = n, this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, this.framebuffer), this.gl.bindTexture(this.gl.TEXTURE_2D, this.transfer_texture), this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MIN_FILTER, this.gl.LINEAR), this.gl.texParameteri(
87
83
  this.gl.TEXTURE_2D,
88
84
  this.gl.TEXTURE_WRAP_S,
89
85
  this.gl.CLAMP_TO_EDGE
@@ -109,29 +105,23 @@ void main() {
109
105
  0
110
106
  ), this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, null), this.setSharedUniforms(this.program_1);
111
107
  const l = this.gl.getUniformLocation(this.program_1, "u_kernel");
112
- if (!l)
113
- throw new Error('[ladon-ts] Error finding "u_kernel" uniform');
114
- this.gl.uniform1fv(l, this.getBlurKernel());
108
+ l || s.errorAndThrow('Error finding "u_kernel" uniform'), this.gl.uniform1fv(l, this.getBlurKernel());
115
109
  }
116
110
  {
117
- const r = o(
111
+ const r = h(
118
112
  this.gl,
119
113
  this.gl.VERTEX_SHADER,
120
114
  this.vertex_shader_src
121
- ), t = o(
115
+ ), t = h(
122
116
  this.gl,
123
117
  this.gl.FRAGMENT_SHADER,
124
118
  this.fragment_shader_pass_2_src
125
119
  );
126
- this.program_2 = h(this.gl, r, t), this.gl.useProgram(this.program_2);
127
- const s = this.gl.getUniformLocation(this.program_2, "u_blur");
128
- if (!s)
129
- throw new Error('[ladon-ts] Error finding "u_blur" uniform');
130
- this.u_blur = s, this.gl.activeTexture(this.gl.TEXTURE2), this.gl.bindTexture(this.gl.TEXTURE_2D, this.transfer_texture), this.gl.uniform1i(this.u_blur, 2), this.setSharedUniforms(this.program_2);
120
+ this.program_2 = a(this.gl, r, t), this.gl.useProgram(this.program_2);
121
+ const o = this.gl.getUniformLocation(this.program_2, "u_blur");
122
+ o || s.errorAndThrow('Error finding "u_blur" uniform'), this.u_blur = o, this.gl.activeTexture(this.gl.TEXTURE2), this.gl.bindTexture(this.gl.TEXTURE_2D, this.transfer_texture), this.gl.uniform1i(this.u_blur, 2), this.setSharedUniforms(this.program_2);
131
123
  const n = this.gl.getUniformLocation(this.program_2, "u_kernel");
132
- if (!n)
133
- throw new Error('[ladon-ts] Error finding "u_kernel" uniform');
134
- this.gl.uniform1fv(n, this.getBlurKernel());
124
+ n || s.errorAndThrow('Error finding "u_kernel" uniform'), this.gl.uniform1fv(n, this.getBlurKernel());
135
125
  }
136
126
  }
137
127
  async render(e, i) {
@@ -154,6 +144,6 @@ void main() {
154
144
  }
155
145
  }
156
146
  export {
157
- _ as BlurRenderer
147
+ c as BlurRenderer
158
148
  };
159
149
  //# sourceMappingURL=blur-renderer.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"blur-renderer.js","sources":["../../../../src/lib/renderer/blur-renderer.ts"],"sourcesContent":["import { BlurConfig, PipelineConfig } from '../config-types';\nimport { FrameData, LadonOutput } from '../generator';\nimport { compileShader, linkProgram } from '../util';\n\nimport { LadonRenderer } from './ladon-renderer';\nimport { WebGLRenderer } from './webgl-renderer';\n\nexport class BlurRenderer extends WebGLRenderer implements LadonRenderer {\n private program_1: WebGLProgram;\n private program_2: WebGLProgram;\n\n private framebuffer: WebGLFramebuffer;\n private transfer_texture: WebGLTexture;\n\n private u_blur: WebGLUniformLocation;\n\n private fragment_shader_common = `#version 300 es\nprecision highp float;\n\n#define KERNEL_SIZE ${(this.config.render as BlurConfig).kernelSize}\n#define HORIZONTAL vec2(1.0, 0.0)\n#define VERTICAL vec2(0.0, 1.0)\n\nout vec4 outColor;\n\nuniform sampler2D u_image;\nuniform sampler2D u_mask;\nuniform vec2 u_resolution;\nuniform float u_kernel[KERNEL_SIZE];\n\n// Function to retrieve the mask value\nfloat get_mask(vec2 st) {\n return texture(u_mask, st).r;\n}\n\n// Get the inverse of the masked area\nfloat get_inv_mask(vec2 st) {\n return 1.0 - get_mask(st);\n}\n\n// Blurring function\nvec3 blur(sampler2D src, vec2 uv, vec2 direction) {\n vec4 accum = vec4(0.0);\n vec2 step = direction / u_resolution;\n int offset = (KERNEL_SIZE - 1) / 2;\n\n float weightSum = 0.0;\n for (int i = 0; i < KERNEL_SIZE; i++) {\n vec2 sampleUV = uv + step * float(i - offset);\n vec4 sampleTex = texture(src, sampleUV);\n float weight = u_kernel[i];\n accum += sampleTex * weight;\n weightSum += weight;\n }\n\n weightSum = max(weightSum, 0.001); // Avoid division by zero\n return accum.rgb / weightSum;\n}\n`;\n\n private fragment_shader_pass_1_src =\n this.fragment_shader_common +\n `\nvoid main() {\n vec2 st = gl_FragCoord.xy / u_resolution;\n outColor = vec4(blur(u_image, st, HORIZONTAL), 1.0);\n}\n`;\n\n public fragment_shader_pass_2_src =\n this.fragment_shader_common +\n `\nuniform sampler2D u_blur;\n\nvoid main() {\n ${this.getUVCalculation()};\n\n vec3 blurred = blur(u_blur, st, VERTICAL);\n vec3 original = texture(u_image, st).rgb;\n float mask = get_inv_mask(st); // Use inverse mask to blur background, not user\n\n // Correct blending based on the mask\n vec3 color = mix(original, blurred, mask); // Ensure mask properly determines blur influence on background\n outColor = vec4(color, 1.0);\n}\n`;\n\n constructor(public output_canvas: HTMLCanvasElement, public readonly config: PipelineConfig) {\n super(output_canvas, config);\n\n if ((this.config.render as BlurConfig).kernelSize % 2 === 0) {\n throw new Error('[ladon-ts] Kernel_size must be odd');\n }\n\n // program pass 1 setup\n {\n const vertex_shader = compileShader(\n this.gl,\n this.gl.VERTEX_SHADER,\n this.vertex_shader_src\n );\n const fragment_shader = compileShader(\n this.gl,\n this.gl.FRAGMENT_SHADER,\n this.fragment_shader_pass_1_src\n );\n\n this.program_1 = linkProgram(this.gl, vertex_shader, fragment_shader);\n this.gl.useProgram(this.program_1);\n\n const framebuffer = this.gl.createFramebuffer();\n if (!framebuffer) {\n throw new Error('[ladon-ts] Error creating framebuffer');\n }\n this.framebuffer = framebuffer;\n\n const transfer_texture = this.gl.createTexture();\n if (!transfer_texture) {\n throw new Error('[ladon-ts] Error creating output texture');\n }\n this.transfer_texture = transfer_texture;\n\n this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, this.framebuffer);\n this.gl.bindTexture(this.gl.TEXTURE_2D, this.transfer_texture);\n this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MIN_FILTER, this.gl.LINEAR);\n this.gl.texParameteri(\n this.gl.TEXTURE_2D,\n this.gl.TEXTURE_WRAP_S,\n this.gl.CLAMP_TO_EDGE\n );\n this.gl.texParameteri(\n this.gl.TEXTURE_2D,\n this.gl.TEXTURE_WRAP_T,\n this.gl.CLAMP_TO_EDGE\n );\n this.gl.texImage2D(\n this.gl.TEXTURE_2D,\n 0,\n this.gl.RGBA,\n this.config.input.width,\n this.config.input.height,\n 0,\n this.gl.RGBA,\n this.gl.UNSIGNED_BYTE,\n null\n );\n this.gl.framebufferTexture2D(\n this.gl.FRAMEBUFFER,\n this.gl.COLOR_ATTACHMENT0,\n this.gl.TEXTURE_2D,\n this.transfer_texture,\n 0\n );\n this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, null);\n\n this.setSharedUniforms(this.program_1);\n\n const u_kernel = this.gl.getUniformLocation(this.program_1, 'u_kernel');\n if (!u_kernel) {\n throw new Error('[ladon-ts] Error finding \"u_kernel\" uniform');\n }\n this.gl.uniform1fv(u_kernel, this.getBlurKernel());\n }\n\n // program pass 2 setup\n {\n const vertex_shader = compileShader(\n this.gl,\n this.gl.VERTEX_SHADER,\n this.vertex_shader_src\n );\n const fragment_shader = compileShader(\n this.gl,\n this.gl.FRAGMENT_SHADER,\n this.fragment_shader_pass_2_src\n );\n\n this.program_2 = linkProgram(this.gl, vertex_shader, fragment_shader);\n this.gl.useProgram(this.program_2);\n\n const u_blur = this.gl.getUniformLocation(this.program_2, 'u_blur');\n if (!u_blur) {\n throw new Error('[ladon-ts] Error finding \"u_blur\" uniform');\n }\n this.u_blur = u_blur;\n this.gl.activeTexture(this.gl.TEXTURE2);\n this.gl.bindTexture(this.gl.TEXTURE_2D, this.transfer_texture);\n this.gl.uniform1i(this.u_blur, 2);\n\n this.setSharedUniforms(this.program_2);\n\n const u_kernel = this.gl.getUniformLocation(this.program_2, 'u_kernel');\n if (!u_kernel) {\n throw new Error('[ladon-ts] Error finding \"u_kernel\" uniform');\n }\n this.gl.uniform1fv(u_kernel, this.getBlurKernel());\n }\n }\n\n public async render(input: FrameData, mask?: LadonOutput): Promise<void> {\n this.uploadVideoStream(input);\n this.uploadMask(mask);\n\n // program pass 1 - blur in one direction & output to framebuffer\n this.gl.useProgram(this.program_1);\n\n this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, this.framebuffer);\n this.gl.drawArrays(this.gl.TRIANGLES, 0, this.verts.length / 3);\n this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, null);\n\n // program pass 2 - blur in other direction & draw to screen\n this.gl.useProgram(this.program_2);\n this.gl.drawArrays(this.gl.TRIANGLES, 0, this.verts.length / 3);\n\n this.postDraw();\n }\n\n public async load(): Promise<void> {\n // empty\n }\n\n public destroy(): void {\n // empty\n }\n\n private getBlurKernel(): Float32Array {\n const kernel = [];\n let sum = 0;\n for (let i = 0; i < (this.config.render as BlurConfig).kernelSize; i++) {\n let w = i - ((this.config.render as BlurConfig).kernelSize - 1) / 2;\n w = Math.exp(-(w ** 2) / (this.config.render as BlurConfig).sigma ** 2);\n kernel.push(w);\n sum += w;\n }\n\n for (let i = 0; i < (this.config.render as BlurConfig).kernelSize; i++) {\n kernel[i] = kernel[i] / sum;\n }\n\n return new Float32Array(kernel);\n }\n}\n"],"names":["BlurRenderer","WebGLRenderer","output_canvas","config","vertex_shader","compileShader","fragment_shader","linkProgram","framebuffer","transfer_texture","u_kernel","u_blur","input","mask","kernel","sum","i","w"],"mappings":"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"}
1
+ {"version":3,"file":"blur-renderer.js","sources":["../../../../src/lib/renderer/blur-renderer.ts"],"sourcesContent":["import { BlurConfig, PipelineConfig } from '../config-types';\nimport { FrameData, LadonOutput } from '../generator';\nimport { logger } from '../logger';\nimport { compileShader, linkProgram } from '../util';\n\nimport { LadonRenderer } from './ladon-renderer';\nimport { WebGLRenderer } from './webgl-renderer';\n\nexport class BlurRenderer extends WebGLRenderer implements LadonRenderer {\n private program_1: WebGLProgram;\n private program_2: WebGLProgram;\n\n private framebuffer: WebGLFramebuffer;\n private transfer_texture: WebGLTexture;\n\n private u_blur: WebGLUniformLocation;\n\n private fragment_shader_common = `#version 300 es\nprecision highp float;\n\n#define KERNEL_SIZE ${(this.config.render as BlurConfig).kernelSize}\n#define HORIZONTAL vec2(1.0, 0.0)\n#define VERTICAL vec2(0.0, 1.0)\n\nout vec4 outColor;\n\nuniform sampler2D u_image;\nuniform sampler2D u_mask;\nuniform vec2 u_resolution;\nuniform float u_kernel[KERNEL_SIZE];\n\n// Function to retrieve the mask value\nfloat get_mask(vec2 st) {\n return texture(u_mask, st).r;\n}\n\n// Get the inverse of the masked area\nfloat get_inv_mask(vec2 st) {\n return 1.0 - get_mask(st);\n}\n\n// Blurring function\nvec3 blur(sampler2D src, vec2 uv, vec2 direction) {\n vec4 accum = vec4(0.0);\n vec2 step = direction / u_resolution;\n int offset = (KERNEL_SIZE - 1) / 2;\n\n float weightSum = 0.0;\n for (int i = 0; i < KERNEL_SIZE; i++) {\n vec2 sampleUV = uv + step * float(i - offset);\n vec4 sampleTex = texture(src, sampleUV);\n float weight = u_kernel[i];\n accum += sampleTex * weight;\n weightSum += weight;\n }\n\n weightSum = max(weightSum, 0.001); // Avoid division by zero\n return accum.rgb / weightSum;\n}\n`;\n\n private fragment_shader_pass_1_src =\n this.fragment_shader_common +\n `\nvoid main() {\n vec2 st = gl_FragCoord.xy / u_resolution;\n outColor = vec4(blur(u_image, st, HORIZONTAL), 1.0);\n}\n`;\n\n public fragment_shader_pass_2_src =\n this.fragment_shader_common +\n `\nuniform sampler2D u_blur;\n\nvoid main() {\n ${this.getUVCalculation()};\n\n vec3 blurred = blur(u_blur, st, VERTICAL);\n vec3 original = texture(u_image, st).rgb;\n float mask = get_inv_mask(st); // Use inverse mask to blur background, not user\n\n // Correct blending based on the mask\n vec3 color = mix(original, blurred, mask); // Ensure mask properly determines blur influence on background\n outColor = vec4(color, 1.0);\n}\n`;\n\n constructor(public output_canvas: HTMLCanvasElement, public readonly config: PipelineConfig) {\n super(output_canvas, config);\n\n if ((this.config.render as BlurConfig).kernelSize % 2 === 0) {\n logger.errorAndThrow('Kernel_size must be odd');\n }\n\n // program pass 1 setup\n {\n const vertex_shader = compileShader(\n this.gl,\n this.gl.VERTEX_SHADER,\n this.vertex_shader_src\n );\n const fragment_shader = compileShader(\n this.gl,\n this.gl.FRAGMENT_SHADER,\n this.fragment_shader_pass_1_src\n );\n\n this.program_1 = linkProgram(this.gl, vertex_shader, fragment_shader);\n this.gl.useProgram(this.program_1);\n\n const framebuffer = this.gl.createFramebuffer();\n if (!framebuffer) {\n logger.errorAndThrow('Error creating framebuffer');\n }\n this.framebuffer = framebuffer;\n\n const transfer_texture = this.gl.createTexture();\n if (!transfer_texture) {\n logger.errorAndThrow('Error creating output texture');\n }\n this.transfer_texture = transfer_texture;\n\n this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, this.framebuffer);\n this.gl.bindTexture(this.gl.TEXTURE_2D, this.transfer_texture);\n this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MIN_FILTER, this.gl.LINEAR);\n this.gl.texParameteri(\n this.gl.TEXTURE_2D,\n this.gl.TEXTURE_WRAP_S,\n this.gl.CLAMP_TO_EDGE\n );\n this.gl.texParameteri(\n this.gl.TEXTURE_2D,\n this.gl.TEXTURE_WRAP_T,\n this.gl.CLAMP_TO_EDGE\n );\n this.gl.texImage2D(\n this.gl.TEXTURE_2D,\n 0,\n this.gl.RGBA,\n this.config.input.width,\n this.config.input.height,\n 0,\n this.gl.RGBA,\n this.gl.UNSIGNED_BYTE,\n null\n );\n this.gl.framebufferTexture2D(\n this.gl.FRAMEBUFFER,\n this.gl.COLOR_ATTACHMENT0,\n this.gl.TEXTURE_2D,\n this.transfer_texture,\n 0\n );\n this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, null);\n\n this.setSharedUniforms(this.program_1);\n\n const u_kernel = this.gl.getUniformLocation(this.program_1, 'u_kernel');\n if (!u_kernel) {\n logger.errorAndThrow('Error finding \"u_kernel\" uniform');\n }\n this.gl.uniform1fv(u_kernel, this.getBlurKernel());\n }\n\n // program pass 2 setup\n {\n const vertex_shader = compileShader(\n this.gl,\n this.gl.VERTEX_SHADER,\n this.vertex_shader_src\n );\n const fragment_shader = compileShader(\n this.gl,\n this.gl.FRAGMENT_SHADER,\n this.fragment_shader_pass_2_src\n );\n\n this.program_2 = linkProgram(this.gl, vertex_shader, fragment_shader);\n this.gl.useProgram(this.program_2);\n\n const u_blur = this.gl.getUniformLocation(this.program_2, 'u_blur');\n if (!u_blur) {\n logger.errorAndThrow('Error finding \"u_blur\" uniform');\n }\n this.u_blur = u_blur;\n this.gl.activeTexture(this.gl.TEXTURE2);\n this.gl.bindTexture(this.gl.TEXTURE_2D, this.transfer_texture);\n this.gl.uniform1i(this.u_blur, 2);\n\n this.setSharedUniforms(this.program_2);\n\n const u_kernel = this.gl.getUniformLocation(this.program_2, 'u_kernel');\n if (!u_kernel) {\n logger.errorAndThrow('Error finding \"u_kernel\" uniform');\n }\n this.gl.uniform1fv(u_kernel, this.getBlurKernel());\n }\n }\n\n public async render(input: FrameData, mask?: LadonOutput): Promise<void> {\n this.uploadVideoStream(input);\n this.uploadMask(mask);\n\n // program pass 1 - blur in one direction & output to framebuffer\n this.gl.useProgram(this.program_1);\n\n this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, this.framebuffer);\n this.gl.drawArrays(this.gl.TRIANGLES, 0, this.verts.length / 3);\n this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, null);\n\n // program pass 2 - blur in other direction & draw to screen\n this.gl.useProgram(this.program_2);\n this.gl.drawArrays(this.gl.TRIANGLES, 0, this.verts.length / 3);\n\n this.postDraw();\n }\n\n public async load(): Promise<void> {\n // empty\n }\n\n public destroy(): void {\n // empty\n }\n\n private getBlurKernel(): Float32Array {\n const kernel = [];\n let sum = 0;\n for (let i = 0; i < (this.config.render as BlurConfig).kernelSize; i++) {\n let w = i - ((this.config.render as BlurConfig).kernelSize - 1) / 2;\n w = Math.exp(-(w ** 2) / (this.config.render as BlurConfig).sigma ** 2);\n kernel.push(w);\n sum += w;\n }\n\n for (let i = 0; i < (this.config.render as BlurConfig).kernelSize; i++) {\n kernel[i] = kernel[i] / sum;\n }\n\n return new Float32Array(kernel);\n }\n}\n"],"names":["BlurRenderer","WebGLRenderer","output_canvas","config","logger","vertex_shader","compileShader","fragment_shader","linkProgram","framebuffer","transfer_texture","u_kernel","u_blur","input","mask","kernel","sum","i","w"],"mappings":"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"}
@@ -1,6 +1,7 @@
1
- import { compileShader as a, linkProgram as g } from "../util.js";
2
- import { WebGLRenderer as h } from "./webgl-renderer.js";
3
- class l extends h {
1
+ import { logger as g } from "../logger.js";
2
+ import { compileShader as a, linkProgram as h } from "../util.js";
3
+ import { WebGLRenderer as d } from "./webgl-renderer.js";
4
+ class T extends d {
4
5
  /**
5
6
  * Create a new replacement renderer instance.
6
7
  *
@@ -17,7 +18,7 @@ class l extends h {
17
18
  this.videoFrameInterval = 0;
18
19
  this.fragmentShader = this.buildFragmentShader();
19
20
  const i = a(this.gl, this.gl.VERTEX_SHADER, this.vertex_shader_src), o = a(this.gl, this.gl.FRAGMENT_SHADER, this.fragmentShader);
20
- this.program = g(this.gl, i, o), this.gl.useProgram(this.program), this.setSharedUniforms(this.program), this.setupBackgroundTexture();
21
+ this.program = h(this.gl, i, o), this.gl.useProgram(this.program), this.setSharedUniforms(this.program), this.setupBackgroundTexture();
21
22
  }
22
23
  /**
23
24
  * Build the fragment shader source code for the replacement effect.
@@ -65,9 +66,7 @@ void main() {
65
66
  }
66
67
  );
67
68
  const r = this.gl.getUniformLocation(this.program, "u_background");
68
- if (!r)
69
- throw new Error('[ladon-ts] Error finding "u_background" uniform');
70
- this.gl.activeTexture(this.gl.TEXTURE2), this.gl.bindTexture(this.gl.TEXTURE_2D, this.backgroundTexture), this.gl.uniform1i(r, 2);
69
+ r || g.errorAndThrow('Error finding "u_background" uniform'), this.gl.activeTexture(this.gl.TEXTURE2), this.gl.bindTexture(this.gl.TEXTURE_2D, this.backgroundTexture), this.gl.uniform1i(r, 2);
71
70
  }
72
71
  /**
73
72
  * Render a frame with the replacement background effect.
@@ -140,6 +139,6 @@ void main() {
140
139
  }
141
140
  }
142
141
  export {
143
- l as ReplacementRenderer
142
+ T as ReplacementRenderer
144
143
  };
145
144
  //# sourceMappingURL=replacement-renderer.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"replacement-renderer.js","sources":["../../../../src/lib/renderer/replacement-renderer.ts"],"sourcesContent":["import { PipelineConfig, ReplacementConfig } from '../config-types';\nimport { FrameData, LadonOutput } from '../generator';\nimport { compileShader, linkProgram } from '../util';\n\nimport { LadonRenderer } from './ladon-renderer';\nimport { WebGLRenderer } from './webgl-renderer';\n\n/**\n * WebGL renderer implementation for virtual background replacement effects.\n * Handles rendering of video backgrounds with proper frame rate throttling.\n */\nexport class ReplacementRenderer extends WebGLRenderer implements LadonRenderer {\n /** The compiled WebGL shader program used for rendering. */\n private program: WebGLProgram;\n\n /** Timestamp of the last video frame update in milliseconds. */\n private lastVideoFrameTime = 0;\n\n /** Interval between video frame updates in milliseconds. */\n private videoFrameInterval: number;\n\n /** WebGL texture containing the background image or video frame. */\n protected backgroundTexture!: WebGLTexture;\n\n /** Fragment shader source code for the replacement effect. */\n protected fragmentShader!: string;\n\n /**\n * Create a new replacement renderer instance.\n *\n * @param outputCanvas - The canvas element where the rendered output will be displayed.\n * @param config - Pipeline configuration containing render settings and background source.\n */\n constructor(public outputCanvas: HTMLCanvasElement, public readonly config: PipelineConfig) {\n super(outputCanvas, config);\n\n // Initialize video frame interval based on background source\n const renderConfig = this.config.render as ReplacementConfig;\n const backgroundSource = renderConfig.background;\n\n if (backgroundSource instanceof HTMLVideoElement) {\n // Use video's frame rate, or default to 30fps\n const videoFPS = this.getVideoFrameRate(backgroundSource) || 30;\n this.videoFrameInterval = 1000 / videoFPS;\n } else {\n this.videoFrameInterval = 0; // No throttling for static backgrounds\n }\n\n // Build the fragment shader\n this.fragmentShader = this.buildFragmentShader();\n\n // Compile and link shaders\n const vertexShader = compileShader(this.gl, this.gl.VERTEX_SHADER, this.vertex_shader_src);\n const fragmentShader = compileShader(this.gl, this.gl.FRAGMENT_SHADER, this.fragmentShader);\n\n this.program = linkProgram(this.gl, vertexShader, fragmentShader);\n this.gl.useProgram(this.program);\n\n this.setSharedUniforms(this.program);\n\n this.setupBackgroundTexture();\n }\n\n /**\n * Build the fragment shader source code for the replacement effect.\n * Creates a shader that blends the background with the input video based on the mask.\n *\n * @returns The complete fragment shader source code.\n */\n protected buildFragmentShader(): string {\n return `${this.fragment_shader_header}\n \nuniform sampler2D u_background;\n\n${this.shared_shader_functions}\n\nvoid main() {\n ${this.getUVCalculation()}\n\n vec3 background = texture(u_background, st).rgb;\n vec3 image = texture(u_image, st).rgb;\n float mask = get_mask(st);\n\n vec3 color = mix(background, image, mask);\n outColor = vec4(color, 1.0);\n}`;\n }\n\n /**\n * Set up the WebGL texture for the background image or video.\n * Creates and configures the texture that will be used as the replacement background.\n */\n protected setupBackgroundTexture(): void {\n const renderConfig = this.config.render as ReplacementConfig;\n const backgroundSource = renderConfig.background;\n\n this.backgroundTexture = this.createTexture(\n this.gl.RGBA,\n backgroundSource.width,\n backgroundSource.height,\n () => {\n this.gl.texImage2D(\n this.gl.TEXTURE_2D,\n 0,\n this.gl.RGBA,\n this.gl.RGBA,\n this.gl.UNSIGNED_BYTE,\n backgroundSource\n );\n }\n );\n\n const uBackground = this.gl.getUniformLocation(this.program, 'u_background');\n if (!uBackground) {\n throw new Error('[ladon-ts] Error finding \"u_background\" uniform');\n }\n this.gl.activeTexture(this.gl.TEXTURE2);\n this.gl.bindTexture(this.gl.TEXTURE_2D, this.backgroundTexture);\n this.gl.uniform1i(uBackground, 2);\n }\n\n /**\n * Render a frame with the replacement background effect.\n * Combines the input video stream with the background based on the provided mask.\n * Includes frame rate throttling for video backgrounds to prevent stuttering.\n * Canvas backgrounds are updated every frame to support dynamic content.\n *\n * @param input - The input frame data (video element or image data).\n * @param mask - Optional mask data for determining foreground/background blend.\n * @returns Promise that resolves when rendering is complete.\n */\n public async render(input: FrameData, mask?: LadonOutput): Promise<void> {\n this.uploadVideoStream(input);\n this.uploadMask(mask);\n\n const renderConfig = this.config.render as ReplacementConfig;\n const shouldDrawVideoBackground = !renderConfig.static;\n const backgroundSource = renderConfig.background;\n\n if (shouldDrawVideoBackground) {\n if (backgroundSource instanceof HTMLVideoElement) {\n // Throttle video backgrounds to prevent stuttering\n await this.updateVideoBackgroundTexture(backgroundSource);\n } else {\n // Always update canvas backgrounds (like dynamic Game of Life)\n this.gl.activeTexture(this.gl.TEXTURE2);\n this.gl.bindTexture(this.gl.TEXTURE_2D, this.backgroundTexture);\n\n this.gl.texImage2D(\n this.gl.TEXTURE_2D,\n 0,\n this.gl.RGBA,\n this.gl.RGBA,\n this.gl.UNSIGNED_BYTE,\n backgroundSource\n );\n }\n }\n\n this.gl.useProgram(this.program);\n this.gl.drawArrays(this.gl.TRIANGLES, 0, this.verts.length / 3);\n this.postDraw();\n }\n\n /**\n * Throttle video background updates and upload texture only when needed.\n * This prevents stuttering and unnecessary GPU uploads when inference is skipped.\n *\n * @param backgroundSource - The video element to potentially upload.\n * @returns Promise that resolves after appropriate throttling and upload.\n */\n private async updateVideoBackgroundTexture(backgroundSource: HTMLVideoElement): Promise<void> {\n if (this.videoFrameInterval <= 0) return;\n\n const now = performance.now();\n const timeSinceLastUpdate = now - this.lastVideoFrameTime;\n\n if (timeSinceLastUpdate >= this.videoFrameInterval) {\n // Only upload texture when enough time has passed\n this.gl.activeTexture(this.gl.TEXTURE2);\n this.gl.bindTexture(this.gl.TEXTURE_2D, this.backgroundTexture);\n\n this.gl.texImage2D(\n this.gl.TEXTURE_2D,\n 0,\n this.gl.RGBA,\n this.gl.RGBA,\n this.gl.UNSIGNED_BYTE,\n backgroundSource\n );\n\n this.lastVideoFrameTime = now;\n }\n }\n\n /**\n * Attempt to determine the frame rate of a video element.\n * Tries to extract frame rate from video track settings if available.\n *\n * @param video - The video element to analyze.\n * @returns The video's frame rate in frames per second, or undefined if not determinable.\n */\n private getVideoFrameRate(video: HTMLVideoElement): number | undefined {\n // Try to get frame rate from video tracks\n if (video.srcObject instanceof MediaStream) {\n const videoTrack = video.srcObject.getVideoTracks()[0];\n if (videoTrack?.getSettings) {\n return videoTrack.getSettings().frameRate;\n }\n }\n return undefined; // Fall back to default\n }\n\n /**\n * Load and initialize the renderer.\n * Currently a no-op for this renderer implementation.\n *\n * @returns Promise that resolves when loading is complete.\n */\n public async load(): Promise<void> {\n // empty\n }\n\n /**\n * Clean up resources and destroy the renderer.\n * Currently a no-op for this renderer implementation.\n */\n public destroy(): void {\n // empty\n }\n}\n"],"names":["ReplacementRenderer","WebGLRenderer","outputCanvas","config","backgroundSource","videoFPS","vertexShader","compileShader","fragmentShader","linkProgram","uBackground","input","mask","renderConfig","shouldDrawVideoBackground","now","video","videoTrack"],"mappings":"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"}
1
+ {"version":3,"file":"replacement-renderer.js","sources":["../../../../src/lib/renderer/replacement-renderer.ts"],"sourcesContent":["import { PipelineConfig, ReplacementConfig } from '../config-types';\nimport { FrameData, LadonOutput } from '../generator';\nimport { logger } from '../logger';\nimport { compileShader, linkProgram } from '../util';\n\nimport { LadonRenderer } from './ladon-renderer';\nimport { WebGLRenderer } from './webgl-renderer';\n\n/**\n * WebGL renderer implementation for virtual background replacement effects.\n * Handles rendering of video backgrounds with proper frame rate throttling.\n */\nexport class ReplacementRenderer extends WebGLRenderer implements LadonRenderer {\n /** The compiled WebGL shader program used for rendering. */\n private program: WebGLProgram;\n\n /** Timestamp of the last video frame update in milliseconds. */\n private lastVideoFrameTime = 0;\n\n /** Interval between video frame updates in milliseconds. */\n private videoFrameInterval: number;\n\n /** WebGL texture containing the background image or video frame. */\n protected backgroundTexture!: WebGLTexture;\n\n /** Fragment shader source code for the replacement effect. */\n protected fragmentShader!: string;\n\n /**\n * Create a new replacement renderer instance.\n *\n * @param outputCanvas - The canvas element where the rendered output will be displayed.\n * @param config - Pipeline configuration containing render settings and background source.\n */\n constructor(public outputCanvas: HTMLCanvasElement, public readonly config: PipelineConfig) {\n super(outputCanvas, config);\n\n // Initialize video frame interval based on background source\n const renderConfig = this.config.render as ReplacementConfig;\n const backgroundSource = renderConfig.background;\n\n if (backgroundSource instanceof HTMLVideoElement) {\n // Use video's frame rate, or default to 30fps\n const videoFPS = this.getVideoFrameRate(backgroundSource) || 30;\n this.videoFrameInterval = 1000 / videoFPS;\n } else {\n this.videoFrameInterval = 0; // No throttling for static backgrounds\n }\n\n // Build the fragment shader\n this.fragmentShader = this.buildFragmentShader();\n\n // Compile and link shaders\n const vertexShader = compileShader(this.gl, this.gl.VERTEX_SHADER, this.vertex_shader_src);\n const fragmentShader = compileShader(this.gl, this.gl.FRAGMENT_SHADER, this.fragmentShader);\n\n this.program = linkProgram(this.gl, vertexShader, fragmentShader);\n this.gl.useProgram(this.program);\n\n this.setSharedUniforms(this.program);\n\n this.setupBackgroundTexture();\n }\n\n /**\n * Build the fragment shader source code for the replacement effect.\n * Creates a shader that blends the background with the input video based on the mask.\n *\n * @returns The complete fragment shader source code.\n */\n protected buildFragmentShader(): string {\n return `${this.fragment_shader_header}\n \nuniform sampler2D u_background;\n\n${this.shared_shader_functions}\n\nvoid main() {\n ${this.getUVCalculation()}\n\n vec3 background = texture(u_background, st).rgb;\n vec3 image = texture(u_image, st).rgb;\n float mask = get_mask(st);\n\n vec3 color = mix(background, image, mask);\n outColor = vec4(color, 1.0);\n}`;\n }\n\n /**\n * Set up the WebGL texture for the background image or video.\n * Creates and configures the texture that will be used as the replacement background.\n */\n protected setupBackgroundTexture(): void {\n const renderConfig = this.config.render as ReplacementConfig;\n const backgroundSource = renderConfig.background;\n\n this.backgroundTexture = this.createTexture(\n this.gl.RGBA,\n backgroundSource.width,\n backgroundSource.height,\n () => {\n this.gl.texImage2D(\n this.gl.TEXTURE_2D,\n 0,\n this.gl.RGBA,\n this.gl.RGBA,\n this.gl.UNSIGNED_BYTE,\n backgroundSource\n );\n }\n );\n\n const uBackground = this.gl.getUniformLocation(this.program, 'u_background');\n if (!uBackground) {\n logger.errorAndThrow('Error finding \"u_background\" uniform');\n }\n this.gl.activeTexture(this.gl.TEXTURE2);\n this.gl.bindTexture(this.gl.TEXTURE_2D, this.backgroundTexture);\n this.gl.uniform1i(uBackground, 2);\n }\n\n /**\n * Render a frame with the replacement background effect.\n * Combines the input video stream with the background based on the provided mask.\n * Includes frame rate throttling for video backgrounds to prevent stuttering.\n * Canvas backgrounds are updated every frame to support dynamic content.\n *\n * @param input - The input frame data (video element or image data).\n * @param mask - Optional mask data for determining foreground/background blend.\n * @returns Promise that resolves when rendering is complete.\n */\n public async render(input: FrameData, mask?: LadonOutput): Promise<void> {\n this.uploadVideoStream(input);\n this.uploadMask(mask);\n\n const renderConfig = this.config.render as ReplacementConfig;\n const shouldDrawVideoBackground = !renderConfig.static;\n const backgroundSource = renderConfig.background;\n\n if (shouldDrawVideoBackground) {\n if (backgroundSource instanceof HTMLVideoElement) {\n // Throttle video backgrounds to prevent stuttering\n await this.updateVideoBackgroundTexture(backgroundSource);\n } else {\n // Always update canvas backgrounds (like dynamic Game of Life)\n this.gl.activeTexture(this.gl.TEXTURE2);\n this.gl.bindTexture(this.gl.TEXTURE_2D, this.backgroundTexture);\n\n this.gl.texImage2D(\n this.gl.TEXTURE_2D,\n 0,\n this.gl.RGBA,\n this.gl.RGBA,\n this.gl.UNSIGNED_BYTE,\n backgroundSource\n );\n }\n }\n\n this.gl.useProgram(this.program);\n this.gl.drawArrays(this.gl.TRIANGLES, 0, this.verts.length / 3);\n this.postDraw();\n }\n\n /**\n * Throttle video background updates and upload texture only when needed.\n * This prevents stuttering and unnecessary GPU uploads when inference is skipped.\n *\n * @param backgroundSource - The video element to potentially upload.\n * @returns Promise that resolves after appropriate throttling and upload.\n */\n private async updateVideoBackgroundTexture(backgroundSource: HTMLVideoElement): Promise<void> {\n if (this.videoFrameInterval <= 0) return;\n\n const now = performance.now();\n const timeSinceLastUpdate = now - this.lastVideoFrameTime;\n\n if (timeSinceLastUpdate >= this.videoFrameInterval) {\n // Only upload texture when enough time has passed\n this.gl.activeTexture(this.gl.TEXTURE2);\n this.gl.bindTexture(this.gl.TEXTURE_2D, this.backgroundTexture);\n\n this.gl.texImage2D(\n this.gl.TEXTURE_2D,\n 0,\n this.gl.RGBA,\n this.gl.RGBA,\n this.gl.UNSIGNED_BYTE,\n backgroundSource\n );\n\n this.lastVideoFrameTime = now;\n }\n }\n\n /**\n * Attempt to determine the frame rate of a video element.\n * Tries to extract frame rate from video track settings if available.\n *\n * @param video - The video element to analyze.\n * @returns The video's frame rate in frames per second, or undefined if not determinable.\n */\n private getVideoFrameRate(video: HTMLVideoElement): number | undefined {\n // Try to get frame rate from video tracks\n if (video.srcObject instanceof MediaStream) {\n const videoTrack = video.srcObject.getVideoTracks()[0];\n if (videoTrack?.getSettings) {\n return videoTrack.getSettings().frameRate;\n }\n }\n return undefined; // Fall back to default\n }\n\n /**\n * Load and initialize the renderer.\n * Currently a no-op for this renderer implementation.\n *\n * @returns Promise that resolves when loading is complete.\n */\n public async load(): Promise<void> {\n // empty\n }\n\n /**\n * Clean up resources and destroy the renderer.\n * Currently a no-op for this renderer implementation.\n */\n public destroy(): void {\n // empty\n }\n}\n"],"names":["ReplacementRenderer","WebGLRenderer","outputCanvas","config","backgroundSource","videoFPS","vertexShader","compileShader","fragmentShader","linkProgram","uBackground","logger","input","mask","renderConfig","shouldDrawVideoBackground","now","video","videoTrack"],"mappings":"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"}
@@ -1,5 +1,6 @@
1
- import { bilinearUpsample as h } from "../util.js";
2
- class o {
1
+ import { logger as r } from "../logger.js";
2
+ import { bilinearUpsample as g } from "../util.js";
3
+ class l {
3
4
  constructor(t, i) {
4
5
  this.output_canvas = t, this.config = i, this.verts = new Float32Array([
5
6
  -1,
@@ -52,15 +53,11 @@ float get_mask(vec2 uv) {
52
53
  this.gl = e;
53
54
  else {
54
55
  const s = this.output_canvas.getContext("2d");
55
- if (!s)
56
- throw new Error("[ladon-ts] Output canvas context must be webgl2 or 2d");
57
- this.output_canvas_2d = s, this.intermediate_canvas = document.createElement("canvas"), this.intermediate_canvas.height = this.config.input.height, this.intermediate_canvas.width = this.config.input.width;
58
- const r = this.intermediate_canvas.getContext("webgl2", {
56
+ s || r.errorAndThrow("Output canvas context must be webgl2 or 2d"), this.output_canvas_2d = s, this.intermediate_canvas = document.createElement("canvas"), this.intermediate_canvas.height = this.config.input.height, this.intermediate_canvas.width = this.config.input.width;
57
+ const h = this.intermediate_canvas.getContext("webgl2", {
59
58
  antialias: !0
60
59
  });
61
- if (!r)
62
- throw new Error("[ladon-ts] Error getting output context webgl");
63
- this.gl = r;
60
+ h || r.errorAndThrow("Error getting output context webgl"), this.gl = h;
64
61
  }
65
62
  this.initTexturesAndBuffers();
66
63
  }
@@ -75,13 +72,9 @@ float get_mask(vec2 uv) {
75
72
  this.config.mask.upscaleSize.height
76
73
  );
77
74
  const t = this.gl.createVertexArray();
78
- if (!t)
79
- throw new Error("[ladon-ts] Error creating vertex array");
80
- this.gl.bindVertexArray(t);
75
+ t || r.errorAndThrow("Error creating vertex array"), this.gl.bindVertexArray(t);
81
76
  const i = this.gl.createBuffer();
82
- if (!i)
83
- throw new Error("[ladon-ts] Error creating buffer");
84
- this.gl.bindBuffer(this.gl.ARRAY_BUFFER, i), this.gl.bufferData(this.gl.ARRAY_BUFFER, this.verts, this.gl.STATIC_DRAW), this.gl.vertexAttribPointer(0, 3, this.gl.FLOAT, !1, 0, 0), this.gl.enableVertexAttribArray(0), this.gl.viewport(0, 0, this.config.input.width, this.config.input.height);
77
+ i || r.errorAndThrow("Error creating buffer"), this.gl.bindBuffer(this.gl.ARRAY_BUFFER, i), this.gl.bufferData(this.gl.ARRAY_BUFFER, this.verts, this.gl.STATIC_DRAW), this.gl.vertexAttribPointer(0, 3, this.gl.FLOAT, !1, 0, 0), this.gl.enableVertexAttribArray(0), this.gl.viewport(0, 0, this.config.input.width, this.config.input.height);
85
78
  }
86
79
  createTexture(t, i, e, s = () => {
87
80
  this.gl.texImage2D(
@@ -96,23 +89,17 @@ float get_mask(vec2 uv) {
96
89
  null
97
90
  );
98
91
  }) {
99
- const r = this.gl.createTexture();
100
- if (!r)
101
- throw new Error("[ladon-ts] Error creating texture");
102
- return this.gl.bindTexture(this.gl.TEXTURE_2D, r), this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MIN_FILTER, this.gl.LINEAR), this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MAG_FILTER, this.gl.LINEAR), this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_S, this.gl.CLAMP_TO_EDGE), this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_T, this.gl.CLAMP_TO_EDGE), s(), this.gl.bindTexture(this.gl.TEXTURE_2D, null), r;
92
+ const h = this.gl.createTexture();
93
+ return h || r.errorAndThrow("Error creating texture"), this.gl.bindTexture(this.gl.TEXTURE_2D, h), this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MIN_FILTER, this.gl.LINEAR), this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MAG_FILTER, this.gl.LINEAR), this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_S, this.gl.CLAMP_TO_EDGE), this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_T, this.gl.CLAMP_TO_EDGE), s(), this.gl.bindTexture(this.gl.TEXTURE_2D, null), h;
103
94
  }
104
95
  getUVCalculation() {
105
96
  return this.config.render.horizontalMirror ? "vec2 st = vec2(1.0 - gl_FragCoord.x / u_resolution.x, 1.0 - gl_FragCoord.y / u_resolution.y);" : "vec2 st = vec2(gl_FragCoord.x / u_resolution.x, 1.0 - gl_FragCoord.y / u_resolution.y);";
106
97
  }
107
98
  setSharedUniforms(t) {
108
99
  const i = this.gl.getUniformLocation(t, "u_resolution");
109
- if (!i)
110
- throw new Error('[ladon-ts] Error finding "u_resolution" uniform');
111
- this.gl.uniform2f(i, this.config.input.width, this.config.input.height);
100
+ i || r.errorAndThrow('Error finding "u_resolution" uniform'), this.gl.uniform2f(i, this.config.input.width, this.config.input.height);
112
101
  const e = this.gl.getUniformLocation(t, "u_image");
113
- if (!e)
114
- throw new Error('[ladon-ts] Error finding "u_image" uniform');
115
- this.gl.activeTexture(this.gl.TEXTURE0), this.gl.bindTexture(this.gl.TEXTURE_2D, this.image_texture), this.gl.uniform1i(e, 0);
102
+ e || r.errorAndThrow('Error finding "u_image" uniform'), this.gl.activeTexture(this.gl.TEXTURE0), this.gl.bindTexture(this.gl.TEXTURE_2D, this.image_texture), this.gl.uniform1i(e, 0);
116
103
  const s = this.gl.getUniformLocation(t, "u_mask");
117
104
  s && (this.gl.activeTexture(this.gl.TEXTURE1), this.gl.bindTexture(this.gl.TEXTURE_2D, this.mask_texture), this.gl.uniform1i(s, 1));
118
105
  }
@@ -138,7 +125,7 @@ float get_mask(vec2 uv) {
138
125
  }
139
126
  uploadMask(t) {
140
127
  if (t != null && t.warm) {
141
- const i = h(
128
+ const i = g(
142
129
  t.data,
143
130
  this.config.mask.outputSize.width,
144
131
  this.config.mask.outputSize.height,
@@ -169,6 +156,6 @@ float get_mask(vec2 uv) {
169
156
  }
170
157
  }
171
158
  export {
172
- o as WebGLRenderer
159
+ l as WebGLRenderer
173
160
  };
174
161
  //# sourceMappingURL=webgl-renderer.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"webgl-renderer.js","sources":["../../../../src/lib/renderer/webgl-renderer.ts"],"sourcesContent":["// webgl-renderer.ts\n\nimport { PipelineConfig } from '../config-types';\nimport { FrameData, LadonOutput } from '../generator';\nimport { bilinearUpsample } from '../util';\n\ntype TextureInitializer = () => void;\nexport abstract class WebGLRenderer {\n protected intermediate_canvas?: HTMLCanvasElement;\n protected output_canvas_2d?: CanvasRenderingContext2D;\n\n protected gl: WebGL2RenderingContext;\n\n protected image_texture!: WebGLTexture;\n protected mask_texture!: WebGLTexture;\n\n protected verts = new Float32Array([\n -1.0,\n -1.0,\n 0.0, // Vertex 1\n -1.0,\n 1.0,\n 0.0, // Vertex 2\n 1.0,\n 1.0,\n 0.0, // Vertex 3\n 1.0,\n 1.0,\n 0.0, // Vertex 4\n 1.0,\n -1.0,\n 0.0, // Vertex 5\n -1.0,\n -1.0,\n 0.0, // Vertex 6\n ]);\n\n protected vertex_shader_src = `#version 300 es\n\nin vec4 position;\n\nvoid main() {\n gl_Position = position;\n}\n`;\n\n // Shared fragment shader code\n protected fragment_shader_header = `#version 300 es\nprecision highp float;\n\nout vec4 outColor;\n\nuniform sampler2D u_image;\nuniform sampler2D u_mask;\nuniform vec2 u_resolution;\n`;\n\n // Shared shader functions for enhanced edge blending\n protected shared_shader_functions = `\nfloat get_mask(vec2 uv) {\n float maskValue = texture(u_mask, uv).r;\n return smoothstep(0.0, 1.0, maskValue);\n}\n`;\n\n constructor(public output_canvas: HTMLCanvasElement, public readonly config: PipelineConfig) {\n const gl = this.output_canvas.getContext('webgl2', { antialias: true });\n\n if (!gl) {\n const output_2d = this.output_canvas.getContext('2d');\n\n if (!output_2d) {\n throw new Error('[ladon-ts] Output canvas context must be webgl2 or 2d');\n }\n\n this.output_canvas_2d = output_2d;\n this.intermediate_canvas = document.createElement('canvas');\n this.intermediate_canvas.height = this.config.input.height;\n this.intermediate_canvas.width = this.config.input.width;\n\n const gl_intermediate = this.intermediate_canvas.getContext('webgl2', {\n antialias: true,\n });\n\n if (!gl_intermediate) {\n throw new Error('[ladon-ts] Error getting output context webgl');\n }\n\n this.gl = gl_intermediate;\n } else {\n this.gl = gl;\n }\n\n // Initialize textures and buffers\n this.initTexturesAndBuffers();\n }\n\n protected initTexturesAndBuffers() {\n // Initialize image texture\n this.image_texture = this.createTexture(\n this.gl.RGBA,\n this.config.input.width,\n this.config.input.height\n );\n\n // Initialize mask texture\n this.mask_texture = this.createTexture(\n this.gl.R8,\n this.config.mask.upscaleSize.width,\n this.config.mask.upscaleSize.height\n );\n\n // Create and bind vertex array\n const vertex_array = this.gl.createVertexArray();\n if (!vertex_array) {\n throw new Error('[ladon-ts] Error creating vertex array');\n }\n this.gl.bindVertexArray(vertex_array);\n\n // Create and bind buffer\n const buffer = this.gl.createBuffer();\n if (!buffer) {\n throw new Error('[ladon-ts] Error creating buffer');\n }\n this.gl.bindBuffer(this.gl.ARRAY_BUFFER, buffer);\n this.gl.bufferData(this.gl.ARRAY_BUFFER, this.verts, this.gl.STATIC_DRAW);\n this.gl.vertexAttribPointer(0, 3, this.gl.FLOAT, false, 0, 0);\n this.gl.enableVertexAttribArray(0);\n\n this.gl.viewport(0, 0, this.config.input.width, this.config.input.height);\n }\n\n protected createTexture(\n internalFormat: number,\n width: number,\n height: number,\n initializeTexture: TextureInitializer = () => {\n // Default initializer creates an empty texture\n this.gl.texImage2D(\n this.gl.TEXTURE_2D,\n 0,\n internalFormat,\n width,\n height,\n 0,\n internalFormat === this.gl.RGBA ? this.gl.RGBA : this.gl.RED,\n this.gl.UNSIGNED_BYTE,\n null\n );\n }\n ): WebGLTexture {\n const texture = this.gl.createTexture();\n if (!texture) {\n throw new Error('[ladon-ts] Error creating texture');\n }\n\n this.gl.bindTexture(this.gl.TEXTURE_2D, texture);\n\n // Set texture parameters\n this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MIN_FILTER, this.gl.LINEAR);\n this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MAG_FILTER, this.gl.LINEAR);\n this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_S, this.gl.CLAMP_TO_EDGE);\n this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_T, this.gl.CLAMP_TO_EDGE);\n\n // Initialize the texture using the provided function\n initializeTexture();\n\n this.gl.bindTexture(this.gl.TEXTURE_2D, null);\n return texture;\n }\n\n protected getUVCalculation() {\n if (this.config.render.horizontalMirror) {\n // Flip both x and y coordinates when horizontal mirroring\n return `vec2 st = vec2(1.0 - gl_FragCoord.x / u_resolution.x, 1.0 - gl_FragCoord.y / u_resolution.y);`;\n }\n\n // Default behavior (no mirroring)\n return `vec2 st = vec2(gl_FragCoord.x / u_resolution.x, 1.0 - gl_FragCoord.y / u_resolution.y);`;\n }\n\n protected setSharedUniforms(program: WebGLProgram) {\n const u_resolution = this.gl.getUniformLocation(program, 'u_resolution');\n if (!u_resolution) {\n throw new Error('[ladon-ts] Error finding \"u_resolution\" uniform');\n }\n this.gl.uniform2f(u_resolution, this.config.input.width, this.config.input.height);\n\n const u_image = this.gl.getUniformLocation(program, 'u_image');\n if (!u_image) {\n throw new Error('[ladon-ts] Error finding \"u_image\" uniform');\n }\n this.gl.activeTexture(this.gl.TEXTURE0);\n this.gl.bindTexture(this.gl.TEXTURE_2D, this.image_texture);\n this.gl.uniform1i(u_image, 0);\n\n const u_mask = this.gl.getUniformLocation(program, 'u_mask');\n if (u_mask) {\n this.gl.activeTexture(this.gl.TEXTURE1);\n this.gl.bindTexture(this.gl.TEXTURE_2D, this.mask_texture);\n this.gl.uniform1i(u_mask, 1);\n }\n }\n\n protected uploadVideoStream(input: FrameData) {\n this.gl.activeTexture(this.gl.TEXTURE0);\n this.gl.bindTexture(this.gl.TEXTURE_2D, this.image_texture);\n\n // Upload video frame or image data to the texture\n if (input instanceof HTMLVideoElement) {\n this.gl.texImage2D(\n this.gl.TEXTURE_2D,\n 0,\n this.gl.RGBA,\n this.gl.RGBA,\n this.gl.UNSIGNED_BYTE,\n input\n );\n } else {\n this.gl.texImage2D(\n this.gl.TEXTURE_2D,\n 0,\n this.gl.RGBA,\n this.config.input.width,\n this.config.input.height,\n 0,\n this.gl.RGBA,\n this.gl.UNSIGNED_BYTE,\n input\n );\n }\n }\n\n protected uploadMask(mask?: LadonOutput) {\n // The mask may not be included if it has not been generated (e.g., frame skipped).\n if (mask?.warm) {\n const maskData = bilinearUpsample(\n mask.data,\n this.config.mask.outputSize.width,\n this.config.mask.outputSize.height,\n this.config.mask.upscaleSize.width,\n this.config.mask.upscaleSize.height\n );\n\n this.gl.activeTexture(this.gl.TEXTURE1);\n this.gl.bindTexture(this.gl.TEXTURE_2D, this.mask_texture);\n this.gl.texImage2D(\n this.gl.TEXTURE_2D,\n 0,\n this.gl.R8,\n this.config.mask.upscaleSize.width,\n this.config.mask.upscaleSize.height,\n 0,\n this.gl.RED,\n this.gl.UNSIGNED_BYTE,\n maskData\n );\n }\n }\n\n protected postDraw() {\n if (this.output_canvas_2d && this.intermediate_canvas) {\n this.output_canvas_2d.drawImage(\n this.intermediate_canvas,\n 0,\n 0,\n this.config.input.width,\n this.config.input.height\n );\n }\n }\n}\n"],"names":["WebGLRenderer","output_canvas","config","gl","output_2d","gl_intermediate","vertex_array","buffer","internalFormat","width","height","initializeTexture","texture","program","u_resolution","u_image","u_mask","input","mask","maskData","bilinearUpsample"],"mappings":"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"}
1
+ {"version":3,"file":"webgl-renderer.js","sources":["../../../../src/lib/renderer/webgl-renderer.ts"],"sourcesContent":["// webgl-renderer.ts\n\nimport { PipelineConfig } from '../config-types';\nimport { FrameData, LadonOutput } from '../generator';\nimport { logger } from '../logger';\nimport { bilinearUpsample } from '../util';\n\ntype TextureInitializer = () => void;\nexport abstract class WebGLRenderer {\n protected intermediate_canvas?: HTMLCanvasElement;\n protected output_canvas_2d?: CanvasRenderingContext2D;\n\n protected gl: WebGL2RenderingContext;\n\n protected image_texture!: WebGLTexture;\n protected mask_texture!: WebGLTexture;\n\n protected verts = new Float32Array([\n -1.0,\n -1.0,\n 0.0, // Vertex 1\n -1.0,\n 1.0,\n 0.0, // Vertex 2\n 1.0,\n 1.0,\n 0.0, // Vertex 3\n 1.0,\n 1.0,\n 0.0, // Vertex 4\n 1.0,\n -1.0,\n 0.0, // Vertex 5\n -1.0,\n -1.0,\n 0.0, // Vertex 6\n ]);\n\n protected vertex_shader_src = `#version 300 es\n\nin vec4 position;\n\nvoid main() {\n gl_Position = position;\n}\n`;\n\n // Shared fragment shader code\n protected fragment_shader_header = `#version 300 es\nprecision highp float;\n\nout vec4 outColor;\n\nuniform sampler2D u_image;\nuniform sampler2D u_mask;\nuniform vec2 u_resolution;\n`;\n\n // Shared shader functions for enhanced edge blending\n protected shared_shader_functions = `\nfloat get_mask(vec2 uv) {\n float maskValue = texture(u_mask, uv).r;\n return smoothstep(0.0, 1.0, maskValue);\n}\n`;\n\n constructor(public output_canvas: HTMLCanvasElement, public readonly config: PipelineConfig) {\n const gl = this.output_canvas.getContext('webgl2', { antialias: true });\n\n if (!gl) {\n const output_2d = this.output_canvas.getContext('2d');\n\n if (!output_2d) {\n logger.errorAndThrow('Output canvas context must be webgl2 or 2d');\n }\n\n this.output_canvas_2d = output_2d;\n this.intermediate_canvas = document.createElement('canvas');\n this.intermediate_canvas.height = this.config.input.height;\n this.intermediate_canvas.width = this.config.input.width;\n\n const gl_intermediate = this.intermediate_canvas.getContext('webgl2', {\n antialias: true,\n });\n\n if (!gl_intermediate) {\n logger.errorAndThrow('Error getting output context webgl');\n }\n\n this.gl = gl_intermediate;\n } else {\n this.gl = gl;\n }\n\n // Initialize textures and buffers\n this.initTexturesAndBuffers();\n }\n\n protected initTexturesAndBuffers() {\n // Initialize image texture\n this.image_texture = this.createTexture(\n this.gl.RGBA,\n this.config.input.width,\n this.config.input.height\n );\n\n // Initialize mask texture\n this.mask_texture = this.createTexture(\n this.gl.R8,\n this.config.mask.upscaleSize.width,\n this.config.mask.upscaleSize.height\n );\n\n // Create and bind vertex array\n const vertex_array = this.gl.createVertexArray();\n if (!vertex_array) {\n logger.errorAndThrow('Error creating vertex array');\n }\n this.gl.bindVertexArray(vertex_array);\n\n // Create and bind buffer\n const buffer = this.gl.createBuffer();\n if (!buffer) {\n logger.errorAndThrow('Error creating buffer');\n }\n this.gl.bindBuffer(this.gl.ARRAY_BUFFER, buffer);\n this.gl.bufferData(this.gl.ARRAY_BUFFER, this.verts, this.gl.STATIC_DRAW);\n this.gl.vertexAttribPointer(0, 3, this.gl.FLOAT, false, 0, 0);\n this.gl.enableVertexAttribArray(0);\n\n this.gl.viewport(0, 0, this.config.input.width, this.config.input.height);\n }\n\n protected createTexture(\n internalFormat: number,\n width: number,\n height: number,\n initializeTexture: TextureInitializer = () => {\n // Default initializer creates an empty texture\n this.gl.texImage2D(\n this.gl.TEXTURE_2D,\n 0,\n internalFormat,\n width,\n height,\n 0,\n internalFormat === this.gl.RGBA ? this.gl.RGBA : this.gl.RED,\n this.gl.UNSIGNED_BYTE,\n null\n );\n }\n ): WebGLTexture {\n const texture = this.gl.createTexture();\n if (!texture) {\n logger.errorAndThrow('Error creating texture');\n }\n\n this.gl.bindTexture(this.gl.TEXTURE_2D, texture);\n\n // Set texture parameters\n this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MIN_FILTER, this.gl.LINEAR);\n this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MAG_FILTER, this.gl.LINEAR);\n this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_S, this.gl.CLAMP_TO_EDGE);\n this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_T, this.gl.CLAMP_TO_EDGE);\n\n // Initialize the texture using the provided function\n initializeTexture();\n\n this.gl.bindTexture(this.gl.TEXTURE_2D, null);\n return texture;\n }\n\n protected getUVCalculation() {\n if (this.config.render.horizontalMirror) {\n // Flip both x and y coordinates when horizontal mirroring\n return `vec2 st = vec2(1.0 - gl_FragCoord.x / u_resolution.x, 1.0 - gl_FragCoord.y / u_resolution.y);`;\n }\n\n // Default behavior (no mirroring)\n return `vec2 st = vec2(gl_FragCoord.x / u_resolution.x, 1.0 - gl_FragCoord.y / u_resolution.y);`;\n }\n\n protected setSharedUniforms(program: WebGLProgram) {\n const u_resolution = this.gl.getUniformLocation(program, 'u_resolution');\n if (!u_resolution) {\n logger.errorAndThrow('Error finding \"u_resolution\" uniform');\n }\n this.gl.uniform2f(u_resolution, this.config.input.width, this.config.input.height);\n\n const u_image = this.gl.getUniformLocation(program, 'u_image');\n if (!u_image) {\n logger.errorAndThrow('Error finding \"u_image\" uniform');\n }\n this.gl.activeTexture(this.gl.TEXTURE0);\n this.gl.bindTexture(this.gl.TEXTURE_2D, this.image_texture);\n this.gl.uniform1i(u_image, 0);\n\n const u_mask = this.gl.getUniformLocation(program, 'u_mask');\n if (u_mask) {\n this.gl.activeTexture(this.gl.TEXTURE1);\n this.gl.bindTexture(this.gl.TEXTURE_2D, this.mask_texture);\n this.gl.uniform1i(u_mask, 1);\n }\n }\n\n protected uploadVideoStream(input: FrameData) {\n this.gl.activeTexture(this.gl.TEXTURE0);\n this.gl.bindTexture(this.gl.TEXTURE_2D, this.image_texture);\n\n // Upload video frame or image data to the texture\n if (input instanceof HTMLVideoElement) {\n this.gl.texImage2D(\n this.gl.TEXTURE_2D,\n 0,\n this.gl.RGBA,\n this.gl.RGBA,\n this.gl.UNSIGNED_BYTE,\n input\n );\n } else {\n this.gl.texImage2D(\n this.gl.TEXTURE_2D,\n 0,\n this.gl.RGBA,\n this.config.input.width,\n this.config.input.height,\n 0,\n this.gl.RGBA,\n this.gl.UNSIGNED_BYTE,\n input\n );\n }\n }\n\n protected uploadMask(mask?: LadonOutput) {\n // The mask may not be included if it has not been generated (e.g., frame skipped).\n if (mask?.warm) {\n const maskData = bilinearUpsample(\n mask.data,\n this.config.mask.outputSize.width,\n this.config.mask.outputSize.height,\n this.config.mask.upscaleSize.width,\n this.config.mask.upscaleSize.height\n );\n\n this.gl.activeTexture(this.gl.TEXTURE1);\n this.gl.bindTexture(this.gl.TEXTURE_2D, this.mask_texture);\n this.gl.texImage2D(\n this.gl.TEXTURE_2D,\n 0,\n this.gl.R8,\n this.config.mask.upscaleSize.width,\n this.config.mask.upscaleSize.height,\n 0,\n this.gl.RED,\n this.gl.UNSIGNED_BYTE,\n maskData\n );\n }\n }\n\n protected postDraw() {\n if (this.output_canvas_2d && this.intermediate_canvas) {\n this.output_canvas_2d.drawImage(\n this.intermediate_canvas,\n 0,\n 0,\n this.config.input.width,\n this.config.input.height\n );\n }\n }\n}\n"],"names":["WebGLRenderer","output_canvas","config","gl","output_2d","logger","gl_intermediate","vertex_array","buffer","internalFormat","width","height","initializeTexture","texture","program","u_resolution","u_image","u_mask","input","mask","maskData","bilinearUpsample"],"mappings":"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"}