rig-play 1.2.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -338,6 +338,23 @@ calling it twice is a no-op. Load once for the page's lifetime and you can
338
338
  ignore it; load and unload repeatedly without it and you leak an atlas per
339
339
  cycle.
340
340
 
341
+ ### Loading for the canvas mode only, and `data:` regions
342
+
343
+ The region cut makes the rigid tier's bitmaps, which only the DOM mode draws.
344
+ `loadModel({ regions })` says what to do about it:
345
+
346
+ - `'blob'` (default) — `blob:` URLs the load owns; `assets.dispose()` frees them.
347
+ - `'data'` — `data:` URLs instead, for a content policy or a snapshot that does
348
+ not carry `blob:`. The same PNG bytes, inline; nothing is minted, so there is
349
+ nothing to free, and the base64 lives as long as the map does.
350
+ - `'none'` — no cut: the pages load, `assets.regionImages` is empty, and the
351
+ load is for the canvas mode. A DOM-mode player opened on it — `new RigPlayer`
352
+ without `mode: 'canvas'`, or `player.mode = 'dom'` — is refused by name, and
353
+ a refused mode change leaves the player as it was.
354
+
355
+ `assets.regions` records the choice. `unpackRegions` takes the same thing as
356
+ `{ encoding: 'blob' | 'data' }`.
357
+
341
358
  ### Pages that ship at another resolution
342
359
 
343
360
  A page image may ship at a resolution its document does not declare — a
@@ -389,7 +406,8 @@ the page's own URL holds premultiplied pixels (see below).
389
406
  ### What unloading frees
390
407
 
391
408
  `revokeRegions()` — and `assets.dispose()`, which calls it — frees the blob URLs
392
- `unpackRegions` minted, and nothing else. The page images are held by
409
+ `unpackRegions` minted, and nothing else (a `regions: 'data'` or `'none'` load
410
+ minted none, so it frees nothing). The page images are held by
393
411
  `assets.pageImages` and by every player drawing from them, so a page is
394
412
  released once you drop the assets and every player is disposed. With the
395
413
  `webgl` mesh backend the shared blitter also uploads each page image on first
@@ -435,7 +453,7 @@ onto shared pages instead. Both play here, unchanged:
435
453
  GPU pages handed back) and creates the other over the same root and assets;
436
454
  `pixelRatio` and `clipping` carry over and the held pose is redrawn at once.
437
455
  `player.renderer` is then a `CanvasRenderer`, whose knobs are `pixelRatio`,
438
- `clipping`, `viewport` and `backend`. What each mode costs, as counters:
456
+ `syncPixelRatio()`, `clipping`, `viewport` and `backend`. What each mode costs, as counters:
439
457
  - **dom** — one element per drawn slot, one style write per changed slot,
440
458
  and the mesh tier's rasters (the knobs below).
441
459
  - **canvas** — one canvas element per player and, per frame that changed,
@@ -583,12 +601,12 @@ name, and unloading is still elements first, bitmaps second.
583
601
  | holding a pose (the caller stopped advancing) | `player.pause()`, `player.resume()` | `frame` keeps drawing the held pose. |
584
602
  | moving to a time (the caller's own, on the spine-core side) | `player.seek(time)`, `RigPlayerOptions.seekStep`, `player.time` | A seek reopens the animation and steps forward in `seekStep` increments (1/60 s by default). Events and completes crossed on the way are not fired. |
585
603
  | events (the caller's own listener on the spine-core side) | `player.on('event', listener)`, `player.on('complete', listener)` | `'event'` hands a `RigEvent` (`name`, `time`, `int`, `float`, `string`); `'complete'` hands `{ animation }`. `on` returns a function that removes the listener. |
586
- | skin (set by the caller on its `Skeleton`) | `RigPlayerOptions.skin` | A constructor option. The names are in `ModelAssets.skins`. Without it the rig is posed with no skin set. |
604
+ | skin (set by the caller on its `Skeleton`) | `RigPlayerOptions.skin`, `player.skin` | A constructor option, and settable on a live player since 1.4.0: the pose re-resolves under the new skin and the track keeps its animation, loop and time (reopened and stepped there, as `seek` does). The names are in `ModelAssets.skins`. Without it the rig is posed with no skin set. |
587
605
  | `SpineHtmlRenderer` | `DomRenderer`, reached as `player.renderer` | In `mode: 'dom'`, the default. `mode: 'canvas'` is new and makes `player.renderer` a `CanvasRenderer`. |
588
606
  | `new SpineHtmlRenderer(root, regionImages)` | `new DomRenderer(root, regionImages, pages?)` | Only for drawing your own frames; `RigPlayer` constructs its renderer. `pages` is the page-image map the mesh tier samples (`ModelAssets.pageImages`). A hand-built `regionImages` map is still accepted. |
589
607
  | `renderer.render(skeleton)` | `renderer.render(frame)` | Takes a `PosedFrame`, not a skeleton. `player.posedFrame()` returns the one the player would draw. |
590
608
  | `renderer.pixelRatio` | `player.renderer.pixelRatio` | On both renderers. Carried over when `player.mode` changes. |
591
- | `renderer.syncPixelRatio()` | `DomRenderer.syncPixelRatio()` | DOM mode only. `player.renderer` is typed as either renderer, so narrow it (`instanceof DomRenderer`) before calling. |
609
+ | `renderer.syncPixelRatio()` | `player.renderer.syncPixelRatio()` | On both renderers, the same meaning and the same deadband (since 1.3.0; before it, DOM mode only). |
592
610
  | `renderer.meshBackingPixels` | `DomRenderer.meshBackingPixels` | DOM mode only. |
593
611
  | `renderer.meshBackend`, `meshBackendActive`, type `MeshBackend` | `DomRenderer.meshBackend`, `meshBackendActive`, type `MeshBackend` | Same values (`'canvas2d'` default, `'webgl'`). Also settable up front as `RigPlayerOptions.meshBackend`. |
594
612
  | `renderer.triangleExpand` | `DomRenderer.triangleExpand` | Same default (0.5). |
@@ -618,9 +636,6 @@ export, for which 1.0.0 declares nothing:
618
636
  load, several players.
619
637
  - **More than one track, mixing between animations, or queueing one after another.**
620
638
  `RigPlayer` declares one track; `play` reopens it at the animation's first pose.
621
- - **Changing the skin after construction.** `skin` is a `RigPlayerOptions` field and
622
- `RigPlayer` declares no way to change it later; a second player over the same assets
623
- is the declared route.
624
639
  - **Changing one slot from code** (its attachment, its colour). `RigPlayer` declares only
625
640
  the whole-rig `color`. The frame record is a plain object, so a caller that draws its
626
641
  own frames through `DomRenderer.render` or `CanvasRenderer.render` decides what is in
@@ -633,8 +648,6 @@ export, for which 1.0.0 declares nothing:
633
648
 
634
649
  Knobs on `DomRenderer` that `CanvasRenderer` does not declare:
635
650
 
636
- - `syncPixelRatio()`. `CanvasRenderer.pixelRatio` exists and is set by hand
637
- (`devicePixelRatio * rootScale`).
638
651
  - `meshBackend` and `meshBackendActive`. The canvas mode has its own pair, `backend` and
639
652
  `backendActive` (type `CanvasBackend`, `'webgl'` by default).
640
653
  - `triangleExpand`, `meshBackingPixels`, `clipWriteCount`, `meshCount`, `meshReuseCount`,
@@ -52,7 +52,12 @@ export interface CanvasViewport {
52
52
  export declare class CanvasRenderer {
53
53
  private readonly root;
54
54
  private readonly pages;
55
- /** Backing-store pixels per root CSS pixel; defaults to the device pixel ratio. Fold a root scale in, as for the DOM tier. */
55
+ /**
56
+ * Backing-store pixels per root CSS pixel; defaults to the device pixel
57
+ * ratio. A root under a CSS-scaled ancestor rasters at the wrong resolution
58
+ * until the scale is folded in — `syncPixelRatio()` measures it, as for the
59
+ * DOM tier. The next `render` sizes the backing store to the new ratio.
60
+ */
56
61
  pixelRatio: number;
57
62
  /** Apply the clips the frame carries (default). False draws every slot unclipped and counts the clips in `clipSkipCount`. */
58
63
  clipping: boolean;
@@ -113,6 +118,16 @@ export declare class CanvasRenderer {
113
118
  */
114
119
  constructor(root: HTMLElement, pages: ReadonlyMap<string, HTMLImageElement>);
115
120
  /** Grows the automatic viewport to cover `box` (world units, Y up) ahead of the frames reaching it. */
121
+ /**
122
+ * Measures the root's effective on-screen scale and sets `pixelRatio` to
123
+ * `devicePixelRatio × scale`, returning the ratio now in effect — the same
124
+ * call, the same meaning and the same deadband as `DomRenderer`'s, so code
125
+ * written against `player.renderer` survives a `mode` change. One forced
126
+ * layout per call (a hidden box appended to the root, read, removed), which
127
+ * is why the renderer never calls it for you. The backing store follows on
128
+ * the next `render`; a root that is not laid out leaves the ratio alone.
129
+ */
130
+ syncPixelRatio(): number;
116
131
  reserve(box: CanvasViewport): void;
117
132
  /** Draws one frame. */
118
133
  render(frame: PosedFrame): void;
@@ -1 +1 @@
1
- {"version":3,"file":"CanvasRenderer.d.ts","sourceRoot":"","sources":["../src/CanvasRenderer.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAwB,UAAU,EAAa,MAAM,iBAAiB,CAAC;AAEnF,+DAA+D;AAC/D,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,UAAU,CAAC;AAEjD,4EAA4E;AAC5E,MAAM,WAAW,cAAc;IAC7B,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAYD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,qBAAa,cAAc;IAgEvB,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,KAAK;IAhExB,8HAA8H;IAC9H,UAAU,SAA+D;IACzE,6HAA6H;IAC7H,QAAQ,UAAQ;IAChB;;;;OAIG;IACH,QAAQ,EAAE,cAAc,GAAG,IAAI,CAAQ;IACvC,mHAAmH;IACnH,OAAO,EAAE,aAAa,CAAW;IACjC,wEAAwE;IACxE,aAAa,EAAE,aAAa,CAAW;IACvC,mEAAmE;IACnE,QAAQ,CAAC,MAAM,EAAE,iBAAiB,CAAC;IAEnC,8EAA8E;IAC9E,SAAS,SAAK;IACd,oEAAoE;IACpE,YAAY,SAAK;IACjB,iEAAiE;IACjE,cAAc,SAAK;IACnB,oFAAoF;IACpF,YAAY,SAAK;IACjB,yDAAyD;IACzD,KAAK,SAAK;IACV,wEAAwE;IACxE,kBAAkB,SAAK;IACvB,yCAAyC;IACzC,SAAS,SAAK;IACd,gIAAgI;IAChI,aAAa,SAAK;IAElB,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAkC;IACtD,gHAAgH;IAChH,OAAO,CAAC,IAAI,CAAK;IACjB,OAAO,CAAC,GAAG,CAAK;IAChB,OAAO,CAAC,KAAK,CAAK;IAClB,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,QAAQ,CAAK;IACrB,OAAO,CAAC,QAAQ,CAAK;IACrB,OAAO,CAAC,YAAY,CAAM;IAC1B,OAAO,CAAC,IAAI,CAA0B;IACtC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAkB;IACvC,sFAAsF;IACtF,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA+B;IACvD,yGAAyG;IACzG,OAAO,CAAC,GAAG,CAAyB;IACpC,OAAO,CAAC,SAAS,CAAM;IACvB,OAAO,CAAC,OAAO,CAAiB;IAChC,OAAO,CAAC,OAAO,CAAyB;IACxC,OAAO,CAAC,QAAQ,CAAiB;IAEjC;;;;;;OAMG;gBAEgB,IAAI,EAAE,WAAW,EACjB,KAAK,EAAE,WAAW,CAAC,MAAM,EAAE,gBAAgB,CAAC;IAgB/D,uGAAuG;IACvG,OAAO,CAAC,GAAG,EAAE,cAAc,GAAG,IAAI;IAIlC,uBAAuB;IACvB,MAAM,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI;IAuD/B,+GAA+G;IAC/G,OAAO,IAAI,IAAI;IAYf,OAAO,CAAC,MAAM;IAkBd,OAAO,CAAC,OAAO;IAUf,oFAAoF;IACpF,OAAO,CAAC,WAAW;IAkBnB,uGAAuG;IACvG,OAAO,CAAC,gBAAgB;IAoExB,iFAAiF;IACjF,OAAO,CAAC,UAAU;IAalB,OAAO,CAAC,MAAM,CAAa;IAE3B;;;OAGG;IACH,OAAO,CAAC,QAAQ;IA4BhB,+CAA+C;IAC/C,OAAO,CAAC,aAAa;IAmErB,OAAO,CAAC,MAAM;CAmEf"}
1
+ {"version":3,"file":"CanvasRenderer.d.ts","sourceRoot":"","sources":["../src/CanvasRenderer.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAwB,UAAU,EAAa,MAAM,iBAAiB,CAAC;AAEnF,+DAA+D;AAC/D,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,UAAU,CAAC;AAEjD,4EAA4E;AAC5E,MAAM,WAAW,cAAc;IAC7B,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAYD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,qBAAa,cAAc;IAqEvB,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,KAAK;IArExB;;;;;OAKG;IACH,UAAU,SAA+D;IACzE,6HAA6H;IAC7H,QAAQ,UAAQ;IAChB;;;;OAIG;IACH,QAAQ,EAAE,cAAc,GAAG,IAAI,CAAQ;IACvC,mHAAmH;IACnH,OAAO,EAAE,aAAa,CAAW;IACjC,wEAAwE;IACxE,aAAa,EAAE,aAAa,CAAW;IACvC,mEAAmE;IACnE,QAAQ,CAAC,MAAM,EAAE,iBAAiB,CAAC;IAEnC,8EAA8E;IAC9E,SAAS,SAAK;IACd,oEAAoE;IACpE,YAAY,SAAK;IACjB,iEAAiE;IACjE,cAAc,SAAK;IACnB,oFAAoF;IACpF,YAAY,SAAK;IACjB,yDAAyD;IACzD,KAAK,SAAK;IACV,wEAAwE;IACxE,kBAAkB,SAAK;IACvB,yCAAyC;IACzC,SAAS,SAAK;IACd,gIAAgI;IAChI,aAAa,SAAK;IAElB,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAkC;IACtD,gHAAgH;IAChH,OAAO,CAAC,IAAI,CAAK;IACjB,OAAO,CAAC,GAAG,CAAK;IAChB,OAAO,CAAC,KAAK,CAAK;IAClB,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,QAAQ,CAAK;IACrB,OAAO,CAAC,QAAQ,CAAK;IACrB,OAAO,CAAC,YAAY,CAAM;IAC1B,OAAO,CAAC,IAAI,CAA0B;IACtC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAkB;IACvC,sFAAsF;IACtF,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA+B;IACvD,yGAAyG;IACzG,OAAO,CAAC,GAAG,CAAyB;IACpC,OAAO,CAAC,SAAS,CAAM;IACvB,OAAO,CAAC,OAAO,CAAiB;IAChC,OAAO,CAAC,OAAO,CAAyB;IACxC,OAAO,CAAC,QAAQ,CAAiB;IAEjC;;;;;;OAMG;gBAEgB,IAAI,EAAE,WAAW,EACjB,KAAK,EAAE,WAAW,CAAC,MAAM,EAAE,gBAAgB,CAAC;IAgB/D,uGAAuG;IACvG;;;;;;;;OAQG;IACH,cAAc,IAAI,MAAM;IAKxB,OAAO,CAAC,GAAG,EAAE,cAAc,GAAG,IAAI;IAIlC,uBAAuB;IACvB,MAAM,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI;IAuD/B,+GAA+G;IAC/G,OAAO,IAAI,IAAI;IAYf,OAAO,CAAC,MAAM;IAkBd,OAAO,CAAC,OAAO;IAUf,oFAAoF;IACpF,OAAO,CAAC,WAAW;IAkBnB,uGAAuG;IACvG,OAAO,CAAC,gBAAgB;IAoExB,iFAAiF;IACjF,OAAO,CAAC,UAAU;IAalB,OAAO,CAAC,MAAM,CAAa;IAE3B;;;OAGG;IACH,OAAO,CAAC,QAAQ;IA4BhB,+CAA+C;IAC/C,OAAO,CAAC,aAAa;IAmErB,OAAO,CAAC,MAAM;CAmEf"}
@@ -1 +1 @@
1
- {"version":3,"file":"DomRenderer.d.ts","sourceRoot":"","sources":["../src/DomRenderer.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"DomRenderer.d.ts","sourceRoot":"","sources":["../src/DomRenderer.ts"],"names":[],"mappings":"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"}
@@ -90,6 +90,16 @@ export declare function straightAlphaDerivations(): number;
90
90
  * happens once it has.
91
91
  */
92
92
  export declare function straightAlphaSource(page: HTMLImageElement, pma: boolean): PageSource;
93
+ /**
94
+ * How a cut region's PNG is handed to the `<img>`: a `blob:` URL this module
95
+ * owns and `revokeRegions` frees (the default), or a `data:` URL that owns
96
+ * nothing and needs no revoke.
97
+ */
98
+ export type RegionEncoding = 'blob' | 'data';
99
+ export interface UnpackRegionsOptions {
100
+ /** `'blob'` by default. */
101
+ encoding?: RegionEncoding;
102
+ }
93
103
  /**
94
104
  * Cuts every region of a page layout — a model document's `pages` section —
95
105
  * out of its page image into its own bitmap once at load time, restoring 90°
@@ -151,7 +161,7 @@ export declare function straightAlphaSource(page: HTMLImageElement, pma: boolean
151
161
  * unpackRegions() with a revokeRegions(); a caller that loads once for the
152
162
  * page lifetime can ignore it.
153
163
  */
154
- export declare function unpackRegions(pages: readonly PageLayout[], pageImages: ReadonlyMap<string, HTMLImageElement>): Promise<Map<string, RegionImage>>;
164
+ export declare function unpackRegions(pages: readonly PageLayout[], pageImages: ReadonlyMap<string, HTMLImageElement>, options?: UnpackRegionsOptions): Promise<Map<string, RegionImage>>;
155
165
  /**
156
166
  * Frees the blob URLs unpackRegions minted for `images` — the counterpart of
157
167
  * unpackRegions, to call once the skeletons using them are gone (after
@@ -1 +1 @@
1
- {"version":3,"file":"DomTexture.d.ts","sourceRoot":"","sources":["../src/DomTexture.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;GAIG;AACH,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,kFAAkF;IAClF,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,SAAS,UAAU,EAAE,CAAC;CAChC;AAQD,MAAM,WAAW,WAAW;IAC1B;;;;OAIG;IACH,GAAG,EAAE,MAAM,CAAC;IACZ;;;;;;;;;OASG;IACH,KAAK,EAAE,MAAM,CAAC;IACd,oDAAoD;IACpD,MAAM,EAAE,MAAM,CAAC;CAChB;AAkBD;;;GAGG;AACH,MAAM,MAAM,UAAU,GAAG,gBAAgB,GAAG,iBAAiB,CAAC;AAqC9D;;;;;;;;GAQG;AACH,wBAAgB,wBAAwB,IAAI,MAAM,CAEjD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,gBAAgB,EAAE,GAAG,EAAE,OAAO,GAAG,UAAU,CAcpF;AAgSD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4DG;AACH,wBAAsB,aAAa,CACjC,KAAK,EAAE,SAAS,UAAU,EAAE,EAC5B,UAAU,EAAE,WAAW,CAAC,MAAM,EAAE,gBAAgB,CAAC,GAChD,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,CA0EnC;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,GAAG,IAAI,CAEpE"}
1
+ {"version":3,"file":"DomTexture.d.ts","sourceRoot":"","sources":["../src/DomTexture.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;GAIG;AACH,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,kFAAkF;IAClF,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,SAAS,UAAU,EAAE,CAAC;CAChC;AAQD,MAAM,WAAW,WAAW;IAC1B;;;;OAIG;IACH,GAAG,EAAE,MAAM,CAAC;IACZ;;;;;;;;;OASG;IACH,KAAK,EAAE,MAAM,CAAC;IACd,oDAAoD;IACpD,MAAM,EAAE,MAAM,CAAC;CAChB;AAkBD;;;GAGG;AACH,MAAM,MAAM,UAAU,GAAG,gBAAgB,GAAG,iBAAiB,CAAC;AAqC9D;;;;;;;;GAQG;AACH,wBAAgB,wBAAwB,IAAI,MAAM,CAEjD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,gBAAgB,EAAE,GAAG,EAAE,OAAO,GAAG,UAAU,CAcpF;AAuSD;;;;GAIG;AACH,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,MAAM,CAAC;AAE7C,MAAM,WAAW,oBAAoB;IACnC,2BAA2B;IAC3B,QAAQ,CAAC,EAAE,cAAc,CAAC;CAC3B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4DG;AACH,wBAAsB,aAAa,CACjC,KAAK,EAAE,SAAS,UAAU,EAAE,EAC5B,UAAU,EAAE,WAAW,CAAC,MAAM,EAAE,gBAAgB,CAAC,EACjD,OAAO,GAAE,oBAAyB,GACjC,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,CA4EnC;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,GAAG,IAAI,CAEpE"}
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  export { type CanvasBackend, CanvasRenderer, type CanvasViewport } from './CanvasRenderer.js';
2
2
  export { DomRenderer, type MeshBackend } from './DomRenderer.js';
3
- export { type PageLayout, type PageRegion, type RegionImage, revokeRegions, unpackRegions } from './DomTexture.js';
4
- export { type LoadModelOptions, loadModel, type ModelAssets, type ModelDocument } from './loadModel.js';
3
+ export { type PageLayout, type PageRegion, type RegionEncoding, type RegionImage, revokeRegions, type UnpackRegionsOptions, unpackRegions } from './DomTexture.js';
4
+ export { type LoadModelOptions, loadModel, type ModelAssets, type ModelDocument, type RegionLoading } from './loadModel.js';
5
5
  export { type AdjustableBones, type BoneAdjust, type BoneLocals, type BoneWorld, type PoseBounds, type RenderMode, type RigEvent, RigPlayer, type RigPlayerOptions } from './player.js';
6
6
  export type { BlendMode, PosedClip, PosedFrame, PosedMesh, PosedRegion, PosedSlot, } from './posedFrame.js';
7
7
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,aAAa,EAAE,cAAc,EAAE,KAAK,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAC9F,OAAO,EAAE,WAAW,EAAE,KAAK,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACjE,OAAO,EAAE,KAAK,UAAU,EAAE,KAAK,UAAU,EAAE,KAAK,WAAW,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACnH,OAAO,EAAE,KAAK,gBAAgB,EAAE,SAAS,EAAE,KAAK,WAAW,EAAE,KAAK,aAAa,EAAE,MAAM,gBAAgB,CAAC;AACxG,OAAO,EAAE,KAAK,eAAe,EAAE,KAAK,UAAU,EAAE,KAAK,UAAU,EAAE,KAAK,SAAS,EAAE,KAAK,UAAU,EAAE,KAAK,UAAU,EAAE,KAAK,QAAQ,EAAE,SAAS,EAAE,KAAK,gBAAgB,EAAE,MAAM,aAAa,CAAC;AACxL,YAAY,EACV,SAAS,EACT,SAAS,EACT,UAAU,EACV,SAAS,EACT,WAAW,EACX,SAAS,GACV,MAAM,iBAAiB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,aAAa,EAAE,cAAc,EAAE,KAAK,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAC9F,OAAO,EAAE,WAAW,EAAE,KAAK,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACjE,OAAO,EAAE,KAAK,UAAU,EAAE,KAAK,UAAU,EAAE,KAAK,cAAc,EAAE,KAAK,WAAW,EAAE,aAAa,EAAE,KAAK,oBAAoB,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACnK,OAAO,EAAE,KAAK,gBAAgB,EAAE,SAAS,EAAE,KAAK,WAAW,EAAE,KAAK,aAAa,EAAE,KAAK,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC5H,OAAO,EAAE,KAAK,eAAe,EAAE,KAAK,UAAU,EAAE,KAAK,UAAU,EAAE,KAAK,SAAS,EAAE,KAAK,UAAU,EAAE,KAAK,UAAU,EAAE,KAAK,QAAQ,EAAE,SAAS,EAAE,KAAK,gBAAgB,EAAE,MAAM,aAAa,CAAC;AACxL,YAAY,EACV,SAAS,EACT,SAAS,EACT,UAAU,EACV,SAAS,EACT,WAAW,EACX,SAAS,GACV,MAAM,iBAAiB,CAAC"}
package/dist/index.js CHANGED
@@ -1,3 +1,31 @@
1
+ const SCALE_PROBE_PX = 100;
2
+ const PIXEL_RATIO_DEADBAND = 1e-3;
3
+ function syncedPixelRatio(root, current) {
4
+ const probe = document.createElement("div");
5
+ probe.style.position = "absolute";
6
+ probe.style.left = "0";
7
+ probe.style.top = "0";
8
+ probe.style.width = `${SCALE_PROBE_PX}px`;
9
+ probe.style.height = `${SCALE_PROBE_PX}px`;
10
+ probe.style.visibility = "hidden";
11
+ probe.style.pointerEvents = "none";
12
+ root.appendChild(probe);
13
+ let width = 0;
14
+ let height = 0;
15
+ try {
16
+ const rect = probe.getBoundingClientRect();
17
+ width = rect.width;
18
+ height = rect.height;
19
+ } finally {
20
+ probe.remove();
21
+ }
22
+ const scale = Math.max(width, height) / SCALE_PROBE_PX;
23
+ if (!(scale > 0)) return current;
24
+ const dpr = typeof devicePixelRatio === "number" ? devicePixelRatio : 1;
25
+ const next = dpr * scale;
26
+ if (current > 0 && Math.abs(next - current) < current * PIXEL_RATIO_DEADBAND) return current;
27
+ return next;
28
+ }
1
29
  const RUNTIME_PI = 3.1415927;
2
30
  const RAD$3 = RUNTIME_PI / 180;
3
31
  const RUNTIME_DEG = 180 / RUNTIME_PI;
@@ -5208,7 +5236,7 @@ function cutBackingPixels(placed, pageImages) {
5208
5236
  const plan = planCut(placed, image);
5209
5237
  return plan.cw * plan.ch;
5210
5238
  }
5211
- async function cutRegion(placed, pageImages) {
5239
+ async function cutRegion(placed, pageImages, encoding) {
5212
5240
  const { region, page } = placed;
5213
5241
  const image = pageImages.get(page.name);
5214
5242
  if (!image) throw new Error(`Missing page image: ${page.name}`);
@@ -5227,6 +5255,7 @@ async function cutRegion(placed, pageImages) {
5227
5255
  }
5228
5256
  const source = straightAlphaSource(image, page.pma === true);
5229
5257
  ctx.drawImage(source, plan.sx, plan.sy, plan.sw, plan.sh, 0, 0, plan.sw, plan.sh);
5258
+ if (encoding === "data") return { url: canvas.toDataURL("image/png"), width: w, height: h };
5230
5259
  const blob = await new Promise((resolve, reject) => {
5231
5260
  canvas.toBlob((b) => b ? resolve(b) : reject(new Error("toBlob failed")), "image/png");
5232
5261
  });
@@ -5234,7 +5263,9 @@ async function cutRegion(placed, pageImages) {
5234
5263
  ownedUrls.add(url);
5235
5264
  return { url, width: w, height: h };
5236
5265
  }
5237
- async function unpackRegions(pages, pageImages) {
5266
+ async function unpackRegions(pages, pageImages, options = {}) {
5267
+ const encoding = options.encoding ?? "blob";
5268
+ if (encoding !== "blob" && encoding !== "data") throw new Error(`encoding must be 'blob' or 'data', got ${String(options.encoding)}`);
5238
5269
  const regions = pages.flatMap((page) => page.regions.map((region) => ({ region, page })));
5239
5270
  const cuts = new Array(regions.length);
5240
5271
  let failedIndex = -1;
@@ -5255,7 +5286,7 @@ async function unpackRegions(pages, pageImages) {
5255
5286
  const job = { done: false, promise: Promise.resolve() };
5256
5287
  job.promise = (async () => {
5257
5288
  try {
5258
- cuts[index] = await cutRegion(region, pageImages);
5289
+ cuts[index] = await cutRegion(region, pageImages, encoding);
5259
5290
  } catch (error) {
5260
5291
  if (failedIndex === -1 || index < failedIndex) {
5261
5292
  failedIndex = index;
@@ -5738,8 +5769,6 @@ function getMeshGlBlitter() {
5738
5769
  const regionVertices = new Float32Array(8);
5739
5770
  const SVG_NS = "http://www.w3.org/2000/svg";
5740
5771
  let tintFilterSeq = 0;
5741
- const SCALE_PROBE_PX = 100;
5742
- const PIXEL_RATIO_DEADBAND = 1e-3;
5743
5772
  function expandPoint(x, y, cx, cy, amount) {
5744
5773
  const dx = x - cx;
5745
5774
  const dy = y - cy;
@@ -5950,32 +5979,8 @@ class DomRenderer {
5950
5979
  * @returns the pixel ratio now in effect (the current one if nothing moved).
5951
5980
  */
5952
5981
  syncPixelRatio() {
5953
- const probe = document.createElement("div");
5954
- probe.style.position = "absolute";
5955
- probe.style.left = "0";
5956
- probe.style.top = "0";
5957
- probe.style.width = `${SCALE_PROBE_PX}px`;
5958
- probe.style.height = `${SCALE_PROBE_PX}px`;
5959
- probe.style.visibility = "hidden";
5960
- probe.style.pointerEvents = "none";
5961
- this.root.appendChild(probe);
5962
- let width = 0;
5963
- let height = 0;
5964
- try {
5965
- const rect = probe.getBoundingClientRect();
5966
- width = rect.width;
5967
- height = rect.height;
5968
- } finally {
5969
- probe.remove();
5970
- }
5971
- const scale = Math.max(width, height) / SCALE_PROBE_PX;
5972
- if (!(scale > 0)) return this.pixelRatio;
5973
- const dpr = typeof devicePixelRatio === "number" ? devicePixelRatio : 1;
5974
- const next = dpr * scale;
5975
- const current = this.pixelRatio;
5976
- if (current > 0 && Math.abs(next - current) < current * PIXEL_RATIO_DEADBAND) return current;
5977
- this.pixelRatio = next;
5978
- return next;
5982
+ this.pixelRatio = syncedPixelRatio(this.root, this.pixelRatio);
5983
+ return this.pixelRatio;
5979
5984
  }
5980
5985
  /**
5981
5986
  * Draws one frame. Slots the frame does not carry — no attachment, a
@@ -6640,7 +6645,12 @@ class CanvasRenderer {
6640
6645
  this.ctx = canvas.getContext("2d");
6641
6646
  root.appendChild(canvas);
6642
6647
  }
6643
- /** Backing-store pixels per root CSS pixel; defaults to the device pixel ratio. Fold a root scale in, as for the DOM tier. */
6648
+ /**
6649
+ * Backing-store pixels per root CSS pixel; defaults to the device pixel
6650
+ * ratio. A root under a CSS-scaled ancestor rasters at the wrong resolution
6651
+ * until the scale is folded in — `syncPixelRatio()` measures it, as for the
6652
+ * DOM tier. The next `render` sizes the backing store to the new ratio.
6653
+ */
6644
6654
  pixelRatio = typeof devicePixelRatio === "number" ? devicePixelRatio : 1;
6645
6655
  /** Apply the clips the frame carries (default). False draws every slot unclipped and counts the clips in `clipSkipCount`. */
6646
6656
  clipping = true;
@@ -6693,6 +6703,19 @@ class CanvasRenderer {
6693
6703
  nextSig = new Float64Array(256);
6694
6704
  nextRefs = [];
6695
6705
  /** Grows the automatic viewport to cover `box` (world units, Y up) ahead of the frames reaching it. */
6706
+ /**
6707
+ * Measures the root's effective on-screen scale and sets `pixelRatio` to
6708
+ * `devicePixelRatio × scale`, returning the ratio now in effect — the same
6709
+ * call, the same meaning and the same deadband as `DomRenderer`'s, so code
6710
+ * written against `player.renderer` survives a `mode` change. One forced
6711
+ * layout per call (a hidden box appended to the root, read, removed), which
6712
+ * is why the renderer never calls it for you. The backing store follows on
6713
+ * the next `render`; a root that is not laid out leaves the ratio alone.
6714
+ */
6715
+ syncPixelRatio() {
6716
+ this.pixelRatio = syncedPixelRatio(this.root, this.pixelRatio);
6717
+ return this.pixelRatio;
6718
+ }
6696
6719
  reserve(box) {
6697
6720
  this.growTo(box.x, -(box.y + box.height), box.x + box.width, -box.y);
6698
6721
  }
@@ -7108,6 +7131,10 @@ async function loadModel(options) {
7108
7131
  `${modelUrl} is a Spine export; this player reads a compiled model document (${CORE_DOCUMENT_SPECS[0]}, skeleton.model.json) — build it with \`rigc build\``
7109
7132
  );
7110
7133
  }
7134
+ const regions = options.regions ?? "blob";
7135
+ if (regions !== "blob" && regions !== "data" && regions !== "none") {
7136
+ throw new Error(`regions must be 'blob', 'data' or 'none', got ${String(options.regions)}`);
7137
+ }
7111
7138
  const fetchImpl = options.fetch ?? globalThis.fetch.bind(globalThis);
7112
7139
  const resolvePage = options.resolvePage ?? resolveAgainstModel;
7113
7140
  const read = readModelText(await fetchText(fetchImpl, modelUrl), modelUrl);
@@ -7118,13 +7145,14 @@ async function loadModel(options) {
7118
7145
  pageImages.set(page.name, await loadImage(resolvePage(page.name, modelUrl), crossOrigin));
7119
7146
  })
7120
7147
  );
7121
- const regionImages = await unpackRegions(pages, pageImages);
7148
+ const regionImages = regions === "none" ? /* @__PURE__ */ new Map() : await unpackRegions(pages, pageImages, { encoding: regions });
7122
7149
  let disposed = false;
7123
7150
  return {
7124
7151
  spec: read.spec,
7125
7152
  animations: read.doc.animations.map((a) => ({ name: a.name, duration: a.timelines.duration })),
7126
7153
  skins: read.doc.skins.map((s) => s.name),
7127
7154
  stage: stageOf(read.doc),
7155
+ regions,
7128
7156
  regionImages,
7129
7157
  pageImages,
7130
7158
  document: read,
@@ -7291,6 +7319,8 @@ class RigPlayer {
7291
7319
  meshBackend;
7292
7320
  view;
7293
7321
  source;
7322
+ read;
7323
+ skinName;
7294
7324
  seekStep;
7295
7325
  track = null;
7296
7326
  animation = null;
@@ -7309,6 +7339,8 @@ class RigPlayer {
7309
7339
  };
7310
7340
  constructor(root, assets, options = {}) {
7311
7341
  const read = assets.document;
7342
+ this.read = read;
7343
+ this.skinName = options.skin;
7312
7344
  this.view = options.skin === void 0 ? underNoSkin(read.doc) : underSkin(read.doc, options.skin);
7313
7345
  this.source = frameSource(this.view, read.parsed);
7314
7346
  this.seekStep = options.seekStep ?? 1 / 60;
@@ -7347,6 +7379,11 @@ class RigPlayer {
7347
7379
  return renderer2;
7348
7380
  }
7349
7381
  if (mode !== "dom") throw new Error(`mode must be 'dom' or 'canvas', got ${String(mode)}`);
7382
+ if (this.assets.regions === "none") {
7383
+ throw new Error(
7384
+ "these assets were loaded with regions: 'none' (no region bitmaps, for the canvas mode only); the DOM mode needs a load with regions 'blob' or 'data'"
7385
+ );
7386
+ }
7350
7387
  const renderer = new DomRenderer(this.root, this.assets.regionImages, this.assets.pageImages);
7351
7388
  if (this.meshBackend) renderer.meshBackend = this.meshBackend;
7352
7389
  return renderer;
@@ -7386,6 +7423,10 @@ class RigPlayer {
7386
7423
  if (this.animation === null) throw new Error("seek before play: no animation is open");
7387
7424
  if (!(time >= 0) || !Number.isFinite(time)) throw new Error(`seek time must be a finite time at or above 0, got ${String(time)}`);
7388
7425
  this.open();
7426
+ this.stepTo(time);
7427
+ }
7428
+ /** Steps the freshly opened track forward to `time` in `seekStep` increments, the last one shorter. */
7429
+ stepTo(time) {
7389
7430
  const track = this.track;
7390
7431
  const steps = Math.floor(time / this.seekStep);
7391
7432
  for (let i = 0; i < steps; i++) this.pose = track.step(this.seekStep);
@@ -7425,6 +7466,32 @@ class RigPlayer {
7425
7466
  if (this.pose === null) return;
7426
7467
  this.renderer.render(this.posedFrame());
7427
7468
  }
7469
+ /**
7470
+ * The skin the rig is posed under — `RigPlayerOptions.skin`, or undefined
7471
+ * for no skin set. Setting it re-resolves the pose under the new skin
7472
+ * (which attachment each slot shows, which bones and constraints are
7473
+ * active) and keeps the animation, its loop and its track time: the track
7474
+ * is reopened under the new skin and stepped to the same time the way
7475
+ * `seek` does, so a physics rig's history restarts there and nothing is
7476
+ * fired on the way. The pose is drawn on the next `draw`/`frame`. An
7477
+ * unknown skin is refused by name, and the player is left as it was.
7478
+ */
7479
+ get skin() {
7480
+ return this.skinName;
7481
+ }
7482
+ set skin(name) {
7483
+ if (name === this.skinName) return;
7484
+ const view = name === void 0 ? underNoSkin(this.read.doc) : underSkin(this.read.doc, name);
7485
+ this.view = view;
7486
+ this.source = frameSource(view, this.read.parsed);
7487
+ this.skinName = name;
7488
+ this.setup = null;
7489
+ if (this.animation !== null && this.track !== null) {
7490
+ const time = this.track.trackTime;
7491
+ this.open();
7492
+ this.stepTo(time);
7493
+ }
7494
+ }
7428
7495
  /**
7429
7496
  * The per-pose adjustment of bone locals (`BoneAdjust`), or null for none.
7430
7497
  * It is applied by every track the player opens — `play`, the reopen inside