@meta-sam/graphics 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,61 @@
1
+ SAM License
2
+ Last Updated: November 19, 2025
3
+
4
+ “Agreement” means the terms and conditions for use, reproduction, distribution and modification of the SAM Materials set forth herein.
5
+
6
+
7
+ “SAM Materials” means, collectively, Documentation and the models, software and algorithms, including machine-learning model code, trained model weights, inference-enabling code, training-enabling code, fine-tuning enabling code, and other elements of the foregoing distributed by Meta and made available under this Agreement.
8
+
9
+ “Documentation” means the specifications, manuals and documentation accompanying
10
+ SAM Materials distributed by Meta.
11
+
12
+
13
+ “Licensee” or “you” means you, or your employer or any other person or entity (if you are entering into this Agreement on such person or entity’s behalf), of the age required under applicable laws, rules or regulations to provide legal consent and that has legal authority to bind your employer or such other person or entity if you are entering in this Agreement on their behalf.
14
+
15
+
16
+ “Meta” or “we” means Meta Platforms Ireland Limited (if you are located in or, if you are an entity, your principal place of business is in the EEA or Switzerland) or Meta Platforms, Inc. (if you are located outside of the EEA or Switzerland).
17
+
18
+
19
+ “Sanctions” means any economic or trade sanctions or restrictions administered or enforced by the United States (including the Office of Foreign Assets Control of the U.S. Department of the Treasury (“OFAC”), the U.S. Department of State and the U.S. Department of Commerce), the United Nations, the European Union, or the United Kingdom.
20
+
21
+
22
+ “Trade Controls” means any of the following: Sanctions and applicable export and import controls.
23
+
24
+ By using or distributing any portion or element of the SAM Materials, you agree to be bound by this Agreement.
25
+
26
+
27
+ 1. License Rights and Redistribution.
28
+
29
+
30
+ a. Grant of Rights. You are granted a non-exclusive, worldwide, non-transferable and royalty-free limited license under Meta’s intellectual property or other rights owned by Meta embodied in the SAM Materials to use, reproduce, distribute, copy, create derivative works of, and make modifications to the SAM Materials.
31
+
32
+ b. Redistribution and Use.
33
+ i. Distribution of SAM Materials, and any derivative works thereof, are subject to the terms of this Agreement. If you distribute or make the SAM Materials, or any derivative works thereof, available to a third party, you may only do so under the terms of this Agreement and you shall provide a copy of this Agreement with any such SAM Materials.
34
+
35
+
36
+ ii. If you submit for publication the results of research you perform on, using, or otherwise in connection with SAM Materials, you must acknowledge the use of SAM Materials in your publication.
37
+
38
+
39
+ iii. Your use of the SAM Materials must comply with applicable laws and regulations, including Trade Control Laws and applicable privacy and data protection laws.
40
+ iv. Your use of the SAM Materials will not involve or encourage others to reverse engineer, decompile or discover the underlying components of the SAM Materials.
41
+ v. You are not the target of Trade Controls and your use of SAM Materials must comply with Trade Controls. You agree not to use, or permit others to use, SAM Materials for any activities subject to the International Traffic in Arms Regulations (ITAR) or end uses prohibited by Trade Controls, including those related to military or warfare purposes, nuclear industries or applications, espionage, or the development or use of guns or illegal weapons.
42
+ 2. User Support. Your use of the SAM Materials is done at your own discretion; Meta does not process any information nor provide any service in relation to such use. Meta is under no obligation to provide any support services for the SAM Materials. Any support provided is “as is”, “with all faults”, and without warranty of any kind.
43
+
44
+
45
+ 3. Disclaimer of Warranty. UNLESS REQUIRED BY APPLICABLE LAW, THE SAM MATERIALS AND ANY OUTPUT AND RESULTS THEREFROM ARE PROVIDED ON AN “AS IS” BASIS, WITHOUT WARRANTIES OF ANY KIND, AND META DISCLAIMS ALL WARRANTIES OF ANY KIND, BOTH EXPRESS AND IMPLIED, INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. YOU ARE SOLELY RESPONSIBLE FOR DETERMINING THE APPROPRIATENESS OF USING OR REDISTRIBUTING THE SAM MATERIALS AND ASSUME ANY RISKS ASSOCIATED WITH YOUR USE OF THE SAM MATERIALS AND ANY OUTPUT AND RESULTS.
46
+
47
+ 4. Limitation of Liability. IN NO EVENT WILL META OR ITS AFFILIATES BE LIABLE UNDER ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, TORT, NEGLIGENCE, PRODUCTS LIABILITY, OR OTHERWISE, ARISING OUT OF THIS AGREEMENT, FOR ANY LOST PROFITS OR ANY DIRECT OR INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL, EXEMPLARY OR PUNITIVE DAMAGES, EVEN IF META OR ITS AFFILIATES HAVE BEEN ADVISED OF THE POSSIBILITY OF ANY OF THE FOREGOING.
48
+
49
+ 5. Intellectual Property.
50
+
51
+
52
+ a. Subject to Meta’s ownership of SAM Materials and derivatives made by or for Meta, with respect to any derivative works and modifications of the SAM Materials that are made by you, as between you and Meta, you are and will be the owner of such derivative works and modifications.
53
+
54
+ b. If you institute litigation or other proceedings against Meta or any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the SAM Materials, outputs or results, or any portion of any of the foregoing, constitutes infringement of intellectual property or other rights owned or licensable by you, then any licenses granted to you under this Agreement shall terminate as of the date such litigation or claim is filed or instituted. You will indemnify and hold harmless Meta from and against any claim by any third party arising out of or related to your use or distribution of the SAM Materials.
55
+
56
+ 6. Term and Termination. The term of this Agreement will commence upon your acceptance of this Agreement or access to the SAM Materials and will continue in full force and effect until terminated in accordance with the terms and conditions herein. Meta may terminate this Agreement if you are in breach of any term or condition of this Agreement. Upon termination of this Agreement, you shall delete and cease use of the SAM Materials. Sections 3, 4 and 7 shall survive the termination of this Agreement.
57
+
58
+ 7. Governing Law and Jurisdiction. This Agreement will be governed and construed under the laws of the State of California without regard to choice of law principles, and the UN Convention on Contracts for the International Sale of Goods does not apply to this Agreement. The courts of California shall have exclusive jurisdiction of any dispute arising out of this Agreement.
59
+
60
+
61
+ 8. Modifications and Amendments. Meta may modify this Agreement from time to time; provided that they are similar in spirit to the current version of the Agreement, but may differ in detail to address new problems or concerns. All such changes will be effective immediately. Your continued use of the SAM Materials after any modification to this Agreement constitutes your agreement to such modification. Except as provided in this Agreement, no modification or addition to any provision of this Agreement will be binding unless it is in writing and signed by an authorized representative of both you and Meta.
package/README.md ADDED
@@ -0,0 +1,490 @@
1
+ # @meta-sam/graphics
2
+
3
+ `@meta-sam/graphics` is an ESM-only, framework-neutral Canvas 2D renderer for
4
+ SAM 3 segmentation results. It retains complete masks and boxes from
5
+ `@meta-sam/parser`, traces masks during asynchronous updates, and creates browser
6
+ `Path2D` objects lazily while rendering.
7
+
8
+ ## Installation
9
+
10
+ ```sh
11
+ npm install @meta-sam/graphics @meta-sam/parser @meta-sam/video
12
+ ```
13
+
14
+ ## Image quick start
15
+
16
+ The renderer accepts image or video results and snapshots from `@meta-sam/parser`.
17
+ Call and await `update()` before `render()`; rendering is synchronous and sees only
18
+ the last successfully committed update.
19
+
20
+ <!-- readme-example -->
21
+
22
+ ```ts
23
+ import { SegmentationRenderer } from '@meta-sam/graphics';
24
+ import type { ImageSegmentationResult } from '@meta-sam/parser';
25
+
26
+ const result: ImageSegmentationResult = {
27
+ media: 'image',
28
+ revision: 1,
29
+ records: [
30
+ {
31
+ kind: 'mask',
32
+ order: 0,
33
+ objectId: 'bus',
34
+ identity: 'image:*:bus',
35
+ revision: 1,
36
+ mask: {
37
+ encoding: 'one_bit',
38
+ width: 5,
39
+ height: 5,
40
+ payload: '!!!!!(QO(0lu8?',
41
+ },
42
+ },
43
+ {
44
+ kind: 'box',
45
+ order: 1,
46
+ objectId: 'bus',
47
+ left: 1,
48
+ top: 1,
49
+ right: 4,
50
+ bottom: 4,
51
+ },
52
+ ],
53
+ diagnostics: [],
54
+ rawOutput: '',
55
+ outcome: { status: 'completed' },
56
+ };
57
+
58
+ const canvas = document.querySelector<HTMLCanvasElement>('#overlay');
59
+ if (canvas === null) throw new Error('Missing overlay canvas.');
60
+
61
+ const context = canvas.getContext('2d');
62
+ if (context === null) throw new Error('Canvas 2D is unavailable.');
63
+
64
+ const renderer = new SegmentationRenderer();
65
+ await renderer.update(result);
66
+ renderer.render(context, {
67
+ media: 'image',
68
+ source: { x: 0, y: 0, width: 5, height: 5 },
69
+ target: { x: 0, y: 0, width: canvas.width, height: canvas.height },
70
+ });
71
+
72
+ // Remove retained overlays while keeping the renderer reusable.
73
+ renderer.clear();
74
+ // Permanently release the renderer when ownership ends.
75
+ renderer.dispose();
76
+ ```
77
+
78
+ The mask payload in this example is one complete, canonical `one_bit` payload. A
79
+ real application normally passes parser snapshots to `update()` as they arrive and
80
+ then passes the final result when parsing completes.
81
+
82
+ ## Video and frame selection
83
+
84
+ Use video results with `media: 'video'` and select the frame to draw with
85
+ `frameIndex`:
86
+
87
+ ```ts
88
+ await renderer.update(videoResult);
89
+ renderer.render(context, {
90
+ media: 'video',
91
+ frameIndex: 42,
92
+ source: { x: 0, y: 0, width: video.videoWidth, height: video.videoHeight },
93
+ target: { x: 0, y: 0, width: canvas.width, height: canvas.height },
94
+ });
95
+ ```
96
+
97
+ `frameIndex` must be a non-negative safe integer. Records with the same frame index
98
+ are drawn; records without a frame reference are global and are drawn on every
99
+ video frame. The retained result media must match the render options.
100
+
101
+ ## Same-canvas decoded video composition
102
+
103
+ `renderVideoFrame()` is a small structural adapter for Canvas media players. Its
104
+ context matches the frame, composition/fallback canvases and contexts, frame-index,
105
+ and abort fields supplied by `@meta-sam/video`'s `MediaPlayerRenderContext`, but
106
+ graphics does not import or depend on the video package. It clears the isolated
107
+ composition canvas, draws the decoded frame first, then draws the exact frame-indexed
108
+ SAM overlay. When the media player supplies `fallbackCanvas`/`fallbackCtx`, the helper
109
+ also draws the same decoded frame with the same fit and DPR but without overlays, ready
110
+ for a missed final-frame deadline.
111
+
112
+ <!-- readme-example -->
113
+
114
+ ```ts
115
+ import {
116
+ SegmentationRenderer,
117
+ type VideoFrameCompositionOptions,
118
+ } from '@meta-sam/graphics';
119
+ import type { VideoSegmentationResult } from '@meta-sam/parser';
120
+ import type { IMediaPlayer } from '@meta-sam/video';
121
+
122
+ declare const player: IMediaPlayer;
123
+ declare const result: VideoSegmentationResult;
124
+
125
+ const renderer = new SegmentationRenderer();
126
+ await renderer.update(result);
127
+
128
+ const hiddenIds = new Set<string>();
129
+ let pixelRatio = window.devicePixelRatio || 1;
130
+ const composition: VideoFrameCompositionOptions = {
131
+ fit: 'contain',
132
+ devicePixelRatio: () => pixelRatio,
133
+ hiddenIds,
134
+ };
135
+
136
+ player.setCustomRender((context) => {
137
+ renderer.renderVideoFrame(context, composition);
138
+ });
139
+
140
+ // The visible canvas backing store owns resize policy. After changing its width or
141
+ // height, update pixelRatio if needed and re-render the retained decoded frame.
142
+ pixelRatio = 2;
143
+ await player.forceRender();
144
+ ```
145
+
146
+ `fit` may be `contain` (the default), `cover`, or `fill`. The helper uses the decoded
147
+ frame dimensions as `source`, derives a logical display rectangle from
148
+ `composition.canvas.width / devicePixelRatio` and height, and recomputes `target` on
149
+ every render. Set the visible output canvas backing width and height to the rounded
150
+ logical size times the same DPR before opening or forcing a render. This makes resize
151
+ behavior explicit and also works for offscreen canvases, where CSS dimensions and the
152
+ global device pixel ratio are unavailable.
153
+
154
+ The helper checks an already-aborted signal before touching the composition canvas and
155
+ returns `false`; otherwise it returns `true` after synchronous composition. The media
156
+ player's isolated canvas and generation fence prevent a render aborted during later
157
+ asynchronous custom work from reaching the visible canvas. `hiddenIds` contains object
158
+ IDs, not mask identities. Callers still own and await `update()` separately, so a render
159
+ while an update is pending sees the prior committed segmentation state.
160
+
161
+ The lower-level `render()` API remains available for overlay-only canvases and custom
162
+ source/target rectangles.
163
+
164
+ ## Results, records, and updates
165
+
166
+ Parser snapshots and final results are **cumulative views**, not deltas. Pass the
167
+ complete current view to every `update()` call.
168
+
169
+ - `update()` calls are serialized in invocation order. Always await the update that
170
+ must be visible before calling `render()`.
171
+ - Masks and boxes are retained. Text and point records are accepted but are not
172
+ drawn.
173
+ - Mask records are collapsed by `identity`; the greatest per-mask `revision` wins.
174
+ At the same revision, conflicting mask data is rejected.
175
+ - Boxes are collapsed by frame and `objectId`; the last box in the accepted view
176
+ wins.
177
+ - Masks are always drawn before boxes, regardless of record order. An all-zero mask
178
+ has no path and is skipped.
179
+ - For the same media, a snapshot revision older than the committed snapshot is
180
+ ignored. At an accepted equal or newer snapshot revision, identities omitted from
181
+ the cumulative view are removed. A present mask with an older per-mask revision
182
+ keeps its retained geometry.
183
+ - Changing media starts a fresh retained view even without `{ reset: true }`.
184
+
185
+ All records in an input count toward resource limits before masks and boxes are
186
+ collapsed.
187
+
188
+ ## Complete mask payloads
189
+
190
+ Graphics accepts only complete masks with this shape:
191
+
192
+ ```ts
193
+ {
194
+ encoding: 'one_bit',
195
+ width: 5,
196
+ height: 5,
197
+ payload: '!!!!!(QO(0lu8?'
198
+ }
199
+ ```
200
+
201
+ `width` and `height` must be positive safe integers, their area must be within the
202
+ configured and codec limits, and `payload` must be within the configured and codec
203
+ length limits. During `update()`, the renderer strictly decodes the entire payload,
204
+ verifies decoder finalization, verifies that the representation is canonical, and
205
+ traces the resulting binary raster. Partial, truncated, trailing, invalid, and
206
+ noncanonical payloads reject the update. Unsupported encodings reject the update.
207
+
208
+ Use `@meta-sam/parser` to produce complete mask records. Graphics does not accept
209
+ streaming mask fragments.
210
+
211
+ ## Source and target rectangles
212
+
213
+ `source` selects the coordinate-space rectangle to map, and `target` is the canvas
214
+ rectangle to fill. Both require finite coordinates and strictly positive width and
215
+ height; origins may be negative. Graphics clips to `target` and applies this mapping:
216
+
217
+ ```text
218
+ scaleX = target.width / source.width
219
+ scaleY = target.height / source.height
220
+ offsetX = target.x - source.x * scaleX
221
+ offsetY = target.y - source.y * scaleY
222
+ ```
223
+
224
+ The axes scale independently; the renderer does not choose a contain or cover fit.
225
+ Use a `source` rectangle in the same coordinate space as mask pixels and box edges.
226
+ Canvas state is enclosed by `save()` and `restore()`, including when drawing throws.
227
+
228
+ ## Visibility and styling
229
+
230
+ Hide an object without changing retained state by passing its `objectId` in an array
231
+ or `ReadonlySet`:
232
+
233
+ ```ts
234
+ renderer.render(context, {
235
+ media: 'image',
236
+ source,
237
+ target,
238
+ hiddenIds: new Set(['background', 'person-2']),
239
+ });
240
+ ```
241
+
242
+ A hidden object contributes neither its mask nor its box, and its mask `Path2D` is
243
+ not created. Colors and geometry are intentionally fixed; fill and outline opacity
244
+ are configurable:
245
+
246
+ - Each `objectId` hashes deterministically to one of eight colors.
247
+ - Every mask is traced once into a marching-squares contour: its vertices are the
248
+ midpoints of the edges between neighbouring pixel centers, so a straight
249
+ boundary follows the pixel edge while corners and diagonals are cut at 45°
250
+ instead of stepping. One closed subpath is emitted per region, holes included.
251
+ - Each closed polygon is then decimated and smoothed. Decimation drops every
252
+ vertex the boundary passes straight through, so a straight run — or one 45°
253
+ diagonal — collapses to its two endpoints. The result is emitted as a uniform
254
+ quadratic B-spline: for the polygon `p₀…pₙ₋₁` the subpath is
255
+ `M m₀ Q p₁ m₁ Q p₂ m₂ … Q p₀ m₀ Z`, where `mᵢ` is the midpoint of `pᵢpᵢ₊₁`.
256
+ The curve therefore runs through every edge midpoint and takes each vertex as
257
+ a control point rather than passing through it. A 1-pixel staircase — what a
258
+ mask at native video resolution actually is — becomes a curve within about a
259
+ tenth of a pixel of the line it approximates instead of a visible step; a long
260
+ edge keeps its own tangent at its midpoint, so decimated straight runs stay
261
+ straight; and a genuine right-angle corner, which marching squares has already
262
+ bevelled at 45°, rounds by under half a source pixel. Coordinates are emitted
263
+ with at most one decimal, exact for the half-pixel vertex lattice and within
264
+ 0.05 pixels for the quarter-pixel midpoints.
265
+ - A contour of at most four vertices — a lone pixel, a two-pixel sliver — keeps
266
+ its straight segments, so a single pixel still renders as a full half-pixel
267
+ diamond instead of being smoothed inward.
268
+ - That single path is both filled and stroked, so the body and the edge can never
269
+ disagree: filled in the object color with the `evenodd` rule, then stroked in the
270
+ same color with round joins and caps. Fill opacity defaults to `0.35`; contour
271
+ opacity defaults to `0.8`. The path is cached per mask and counts toward the cache
272
+ and `maxPathComplexity` limits, which measure the emitted path string; smoothing
273
+ roughly doubles that string for the same polygon, since a curve carries a control
274
+ point as well as an endpoint.
275
+ - The contour width defaults to `0.003 × min(source.width, source.height)` — about
276
+ 2.2 pixels for 720p media — expressed in source pixels, so it scales with the
277
+ source-to-target transform and tracks the media resolution rather than a fixed
278
+ pixel count. Turn the contour off with `maskOutline: false`, or configure its
279
+ source-pixel weight and opacity with `maskOutline: { width: 2, opacity: 0.9 }`.
280
+ - Boxes use the same color at `globalAlpha = 1` and a source-coordinate line width
281
+ of `2 / max(abs(scaleX), abs(scaleY))`.
282
+ - Masks render before boxes.
283
+
284
+ The renderer does not mutate records. Object colors, contour geometry, box styling,
285
+ draw order, and source transforms are not configurable.
286
+
287
+ ## Lifecycle and transactional behavior
288
+
289
+ State changes are transactional. Mask validation, decoding, tracing, and aggregate
290
+ limit checks complete before a candidate state commits. If an update rejects, the
291
+ previous retained state and path cache remain available.
292
+
293
+ - `update(result, { reset: true })` ignores retained media and revisions. The old
294
+ state and cache are dropped only after the replacement commits successfully.
295
+ - `clear()` removes committed state and cached `Path2D` objects while keeping the
296
+ renderer reusable. It also fences already queued stale updates before they decode
297
+ or commit.
298
+ - `dispose()` is idempotent, clears state and cache, and permanently fences queued
299
+ work. Later `render()` calls throw `RendererDisposedError`; later `update()` calls
300
+ reject with it. `clear()` after disposal is a no-op.
301
+
302
+ `render()` never waits for a pending update. It draws the previous committed state
303
+ until that update resolves.
304
+
305
+ ## Browser requirements
306
+
307
+ Importing the package and calling `update()` do not access `Path2D`. Rendering
308
+ requires a Canvas 2D context and a browser-compatible `globalThis.Path2D` constructor.
309
+ If `Path2D` is absent, `render()` throws `Path2DUnavailableError` before mutating the
310
+ canvas context. Mask `Path2D` objects are created lazily and retained in a bounded
311
+ least-recently-used cache; each render also creates an uncached path for target
312
+ clipping.
313
+
314
+ ## Configuration
315
+
316
+ <!-- readme-example -->
317
+
318
+ ```ts
319
+ import {
320
+ SegmentationRenderer,
321
+ type SegmentationRendererOptions,
322
+ } from '@meta-sam/graphics';
323
+
324
+ const options: SegmentationRendererOptions = {
325
+ maskFillOpacity: 0.5,
326
+ maskOutline: { width: 2, opacity: 0.9 },
327
+ maxCachedPaths: 128,
328
+ maxCachedComplexity: 250_000,
329
+ maxRecords: 20_000,
330
+ maxMasks: 4_096,
331
+ maxBoxes: 8_192,
332
+ maxMaskArea: 16_777_216,
333
+ maxMaskPayloadLength: 2_000_000,
334
+ maxPathComplexity: 250_000,
335
+ maxRetainedComplexity: 1_000_000,
336
+ };
337
+ const renderer = new SegmentationRenderer(options);
338
+ ```
339
+
340
+ | Option | Default |
341
+ | ----------------------- | ------------------------------------------ |
342
+ | `maskFillOpacity` | `0.35` |
343
+ | `maskOutline` | `true` |
344
+ | `maskOutline.width` | `0.003 × min(source.width, source.height)` |
345
+ | `maskOutline.opacity` | `0.8` |
346
+ | `maxCachedPaths` | `128` |
347
+ | `maxCachedComplexity` | `250_000` |
348
+ | `maxRecords` | `20_000` |
349
+ | `maxMasks` | `4_096` |
350
+ | `maxBoxes` | `8_192` |
351
+ | `maxMaskArea` | `16_777_216` |
352
+ | `maxMaskPayloadLength` | `2_000_000` |
353
+ | `maxPathComplexity` | `250_000` |
354
+ | `maxRetainedComplexity` | `1_000_000` |
355
+
356
+ `maskFillOpacity` and `maskOutline.opacity` must be finite numbers in the inclusive
357
+ range from `0` through `1`. An outline with opacity `0` remains enabled and is still
358
+ stroked. `maskOutline` also accepts `true`, `false`, or `{ width }`; width is in source
359
+ pixels and must be finite and greater than zero. Every resource override must be a
360
+ positive safe integer. Invalid constructor options throw `TypeError`. Constructor
361
+ settings are resolved once, so later mutation of an options object has no effect.
362
+ Cache limits evict least-recently-used paths. Other resource limits reject an update
363
+ before it commits.
364
+
365
+ ## Public API
366
+
367
+ Only the package root is public; deep imports are not supported.
368
+
369
+ ### Runtime exports
370
+
371
+ | Export | Purpose |
372
+ | -------------------------------- | ----------------------------------------------------------- |
373
+ | `SegmentationRenderer` | Retain parser views and render masks and boxes. |
374
+ | `SegmentationGraphicsError` | Base class for package-specific errors. |
375
+ | `UnsupportedMaskEncodingError` | Reject a mask encoding other than `one_bit`. |
376
+ | `InvalidMaskPayloadError` | Reject invalid mask data or a conflicting mask revision. |
377
+ | `SegmentationResourceLimitError` | Reject an update that exceeds a configured resource limit. |
378
+ | `InvalidRenderOptionsError` | Reject invalid result, frame, rectangle, or transform data. |
379
+ | `RendererDisposedError` | Reject operations after permanent disposal. |
380
+ | `Path2DUnavailableError` | Report a missing browser `Path2D` implementation. |
381
+
382
+ ### Type exports
383
+
384
+ - `Rectangle`
385
+ - `SegmentationCanvasContext`
386
+ - `MaskOutlineOptions`
387
+ - `SegmentationRendererOptions`
388
+ - `SegmentationUpdateOptions`
389
+ - `ImageRenderOptions`
390
+ - `VideoRenderOptions`
391
+ - `SegmentationRenderOptions`
392
+ - `VideoFrameCompositionContext`
393
+ - `VideoFrameCompositionOptions`
394
+ - `VideoFrameFit`
395
+
396
+ Opacity configuration is declared on the renderer options:
397
+
398
+ ```ts
399
+ interface MaskOutlineOptions {
400
+ readonly width?: number;
401
+ /** Contour opacity. Defaults to 0.8. */
402
+ readonly opacity?: number;
403
+ }
404
+
405
+ interface SegmentationRendererOptions {
406
+ /** Mask fill opacity. Defaults to 0.35. */
407
+ readonly maskFillOpacity?: number;
408
+ readonly maskOutline?: boolean | MaskOutlineOptions;
409
+ // Resource limit options are unchanged.
410
+ }
411
+ ```
412
+
413
+ The primary signatures are:
414
+
415
+ ```ts
416
+ class SegmentationRenderer {
417
+ constructor(options?: SegmentationRendererOptions);
418
+ update(
419
+ result: SegmentationResult | SegmentationSnapshot,
420
+ options?: SegmentationUpdateOptions,
421
+ ): Promise<void>;
422
+ render(context: SegmentationCanvasContext, options: SegmentationRenderOptions): void;
423
+ renderVideoFrame(
424
+ context: VideoFrameCompositionContext,
425
+ options?: VideoFrameCompositionOptions,
426
+ ): boolean;
427
+ clear(): void;
428
+ dispose(): void;
429
+ }
430
+ ```
431
+
432
+ `SegmentationResult` and `SegmentationSnapshot` are public types from
433
+ `@meta-sam/parser`.
434
+
435
+ ## Errors
436
+
437
+ All package-specific errors extend `SegmentationGraphicsError` and expose a stable
438
+ `code`. Handle expected categories with `instanceof`:
439
+
440
+ ```ts
441
+ import { SegmentationGraphicsError, SegmentationRenderer } from '@meta-sam/graphics';
442
+
443
+ const renderer = new SegmentationRenderer();
444
+
445
+ try {
446
+ await renderer.update(result);
447
+ } catch (error) {
448
+ if (error instanceof SegmentationGraphicsError) {
449
+ console.error(error.code, error.message, error.cause);
450
+ } else {
451
+ throw error;
452
+ }
453
+ }
454
+ ```
455
+
456
+ | Error | `code` |
457
+ | -------------------------------- | ------------------------ |
458
+ | `UnsupportedMaskEncodingError` | `unsupported_encoding` |
459
+ | `InvalidMaskPayloadError` | `invalid_mask_payload` |
460
+ | `SegmentationResourceLimitError` | `resource_limit` |
461
+ | `InvalidRenderOptionsError` | `invalid_render_options` |
462
+ | `RendererDisposedError` | `renderer_disposed` |
463
+ | `Path2DUnavailableError` | `path2d_unavailable` |
464
+
465
+ Invalid constructor limits throw ordinary `TypeError`. Update failures reject the
466
+ returned promise; render failures throw synchronously.
467
+
468
+ ## Runtime and compatibility
469
+
470
+ - ESM only. Use `import`; there is no CommonJS export.
471
+ - Supported Node.js versions are `^20.17.0 || >=22.9.0`.
472
+ - The package targets ES2022 and declares `@meta-sam/parser` as its only runtime
473
+ dependency.
474
+ - Importing and updating are safe without browser globals. Rendering requires Canvas
475
+ 2D and `Path2D`.
476
+ - The package is side-effect-free and publishes only `dist` and this README.
477
+
478
+ ## Related packages
479
+
480
+ | Package | Role |
481
+ | -------------------- | ------------------------------------------------------------------------- |
482
+ | `@meta-sam/parser` | Parse structural response events into segmentation snapshots and results. |
483
+ | `@meta-sam/graphics` | Retain mask paths and render Canvas 2D overlays. |
484
+ | `@meta-sam/video` | Decode media into Canvas with packet-exact frame metadata and audio. |
485
+ | `@meta-sam/react` | Provide React bindings over the video and graphics packages. |
486
+
487
+ ## License
488
+
489
+ The source is licensed under the SAM License. See `LICENSE` in this package or the
490
+ repository root for the license text.
@@ -0,0 +1,34 @@
1
+ export interface TracedContour {
2
+ readonly d: string;
3
+ readonly complexity: number;
4
+ }
5
+ export interface TraceContourOptions {
6
+ /**
7
+ * Replace each closed polygon with a uniform quadratic B-spline through its
8
+ * edge midpoints. Defaults to enabled; pass `false` for the raw polygons.
9
+ */
10
+ readonly smooth?: boolean;
11
+ }
12
+ /**
13
+ * Traces the contour of a binary raster as closed polygons with marching
14
+ * squares, then smooths each polygon.
15
+ *
16
+ * Contour vertices are the midpoints of the edges between neighbouring pixel
17
+ * centers, so a boundary that runs straight follows the pixel edge exactly
18
+ * while a corner or a diagonal is cut at 45° instead of stepping. Every
19
+ * segment is emitted with the filled region on its right, which makes outer
20
+ * contours wind opposite to the holes they enclose; the caller fills the
21
+ * result with `evenodd` and strokes the same path.
22
+ *
23
+ * The two saddle cases (a filled diagonal pair) are both resolved as a filled
24
+ * center, matching the eight-connected reading of the raster. That choice is
25
+ * what makes every crossing the endpoint of exactly one segment, so chaining
26
+ * the segments is a walk rather than a search.
27
+ *
28
+ * A polygon is then decimated — a vertex the contour passes straight through
29
+ * is dropped, collapsing a straight run to its two endpoints — and emitted as
30
+ * a quadratic B-spline through the edge midpoints, which turns the 1-pixel
31
+ * staircase of a native-resolution mask into a smooth boundary. Contours of at
32
+ * most `SMALL_CONTOUR_VERTICES` vertices keep their straight segments.
33
+ */
34
+ export declare function traceContour(raster: Uint8Array, width: number, height: number, limit: number, options?: TraceContourOptions): TracedContour;