@awflow/avatar-export 1.0.1 → 1.1.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
@@ -14,7 +14,14 @@ Every function returns a `Promise<Uint8Array>`. In Node, Bun and Deno the
14
14
  wasm loads on first use; nothing to set up.
15
15
 
16
16
  ```ts
17
- import { toApng, toGif, toPng, toSprite } from '@awflow/avatar-export'
17
+ import {
18
+ svgToPng,
19
+ toApng,
20
+ toGif,
21
+ toPng,
22
+ toSprite,
23
+ } from '@awflow/avatar-export'
24
+ import { frames } from '@awflow/avatar'
18
25
 
19
26
  // a still PNG, 256 px square by default
20
27
  const png = await toPng('Linh', { expression: 'happy' }, { size: 512 })
@@ -25,6 +32,20 @@ const apng = await toApng('Linh', { reaction: 'hop' }, { fps: 30, seconds: 3 })
25
32
  // a GIF with one 256-colour palette, transparent where nothing is drawn
26
33
  const gif = await toGif('Linh', { frame: 'none', activity: 'done' })
27
34
 
35
+ // a script: idle, then working from 1 s, a cheer at 3 s, idle again at 4 s
36
+ const story = await toGif('Linh', {
37
+ timeline: [
38
+ { at: 1, activity: 'working' },
39
+ { at: 3, reaction: 'celebrate' },
40
+ { at: 4, activity: 'idle' },
41
+ ],
42
+ }, { fps: 25, seconds: 5 })
43
+
44
+ // any SVG string, such as one frame of frames(), at a given width
45
+ const still = await svgToPng(frames('Linh', {}, { fps: 1, seconds: 1 })[0], {
46
+ size: 128,
47
+ })
48
+
28
49
  // many avatars on one sheet: 3 columns of 64 px tiles, 4 px apart
29
50
  const sheet = await toSprite(['Ada', 'Linh', 'Sam'], {}, {
30
51
  size: 64,
@@ -65,7 +86,8 @@ return at once.
65
86
  `LIMITS` is
66
87
  `{ maxFrames: 600, maxSize: 1024, maxSprite: 400, maxSheet: 16777216 }`, with
67
88
  `maxFrames` taken from the core's `FRAME_LIMITS` (1 to 120 fps, up to 60
68
- seconds). `maxSheet` caps a sprite sheet's area at 4096 by 4096 pixels. Frames are `round(fps * seconds)`, checked before anything is
89
+ seconds). `maxSheet` caps a sprite sheet's area, and an `svgToPng` image's,
90
+ at 4096 by 4096 pixels. A `timeline` holds at most `maxFrames` cues. Frames are `round(fps * seconds)`, checked before anything is
69
91
  drawn. A value out of range, or not a finite number, throws a `RangeError`
70
92
  that names the limit.
71
93
 
@@ -1,6 +1,12 @@
1
- import { type AvatarOptions, type Reaction } from '@awflow/avatar';
1
+ import { type AvatarOptions, type Cue, type Reaction } from '@awflow/avatar';
2
+ /**
3
+ * What to animate: any avatar options (a registered `character` included),
4
+ * a `reaction` from the first frame, and a `timeline` of later cues: an
5
+ * activity, an expression or a reaction at a time in seconds.
6
+ */
2
7
  export type AnimatedOptions = AvatarOptions & {
3
8
  reaction?: Reaction;
9
+ timeline?: readonly Cue[];
4
10
  };
5
11
  export type AnimationSettings = {
6
12
  /** Side in pixels, 1 to LIMITS.maxSize. Default 256. */
package/dist/index.d.ts CHANGED
@@ -6,6 +6,14 @@ export type { WasmSource } from './raster.js';
6
6
  export { init } from './raster.js';
7
7
  export type { SpriteSettings } from './sprite.js';
8
8
  export { toSprite } from './sprite.js';
9
+ /**
10
+ * Any avatar SVG (one frame of `frames()`, or a sheet of them) as a PNG,
11
+ * `size` pixels wide (default 256, at most LIMITS.maxSize). The height
12
+ * follows the SVG's aspect ratio.
13
+ */
14
+ export declare function svgToPng(svg: string, o?: {
15
+ size?: number;
16
+ }): Promise<Uint8Array>;
9
17
  /** The avatar as a still PNG, `size` pixels square (default 256). */
10
18
  export declare function toPng(name: string, opts?: AvatarOptions, o?: {
11
19
  size?: number;
package/dist/index.js CHANGED
@@ -101,8 +101,13 @@ var options = (size) => ({
101
101
  fitTo: { mode: "width", value: size },
102
102
  font: { loadSystemFonts: false }
103
103
  });
104
- function png(svg, size) {
104
+ function png(svg, size, maxPixels) {
105
105
  const resvg = new Resvg(svg, options(size));
106
+ const area = resvg.width > 0 ? size * size * (resvg.height / resvg.width) : 0;
107
+ if (maxPixels !== undefined && area > maxPixels) {
108
+ resvg.free();
109
+ throw new RangeError(`svgToPng(): ${Math.round(area)} pixels is above maxSheet ${maxPixels}`);
110
+ }
106
111
  const image = resvg.render();
107
112
  try {
108
113
  return image.asPng();
@@ -134,7 +139,9 @@ function rgba(svg, size) {
134
139
  }
135
140
 
136
141
  // src/animated.ts
137
- import { frames } from "@awflow/avatar";
142
+ import {
143
+ frames
144
+ } from "@awflow/avatar";
138
145
  import * as gifenc from "gifenc";
139
146
 
140
147
  // src/png.ts
@@ -392,6 +399,13 @@ async function toSprite(names, opts = {}, o = {}) {
392
399
  }
393
400
 
394
401
  // src/index.ts
402
+ async function svgToPng(svg, o = {}) {
403
+ const size = checkSize("svgToPng()", o.size);
404
+ if (typeof svg !== "string" || !svg.trimStart().startsWith("<svg"))
405
+ throw new TypeError("svgToPng(): svg must be an SVG string");
406
+ await ensure();
407
+ return png(svg, size, LIMITS.maxSheet);
408
+ }
395
409
  async function toPng(name, opts = {}, o = {}) {
396
410
  const size = checkSize("toPng()", o.size);
397
411
  await ensure();
@@ -402,6 +416,7 @@ export {
402
416
  toPng,
403
417
  toGif,
404
418
  toApng,
419
+ svgToPng,
405
420
  init,
406
421
  LIMITS,
407
422
  DEFAULTS
package/dist/raster.d.ts CHANGED
@@ -9,8 +9,12 @@ export type WasmSource = ArrayBuffer | ArrayBufferView | Response | URL | WebAss
9
9
  export declare function init(wasm?: WasmSource | PromiseLike<WasmSource>): Promise<void>;
10
10
  /** The export functions wait on this: init() with no argument when needed. */
11
11
  export declare const ensure: () => Promise<void>;
12
- /** One SVG as a PNG, size pixels wide, encoded by resvg. */
13
- export declare function png(svg: string, size: number): Uint8Array;
12
+ /**
13
+ * One SVG as a PNG, size pixels wide, encoded by resvg. With `maxPixels`,
14
+ * an SVG whose height would take the image past that area is refused before
15
+ * any pixel is drawn.
16
+ */
17
+ export declare function png(svg: string, size: number, maxPixels?: number): Uint8Array;
14
18
  /**
15
19
  * One SVG as straight (not premultiplied) RGBA pixels, size by size. resvg
16
20
  * hands premultiplied pixels back; they are demultiplied the way its own PNG
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@awflow/avatar-export",
3
- "version": "1.0.1",
3
+ "version": "1.1.0",
4
4
  "type": "module",
5
5
  "sideEffects": false,
6
6
  "bin": {
@@ -21,7 +21,7 @@
21
21
  "LICENSE"
22
22
  ],
23
23
  "dependencies": {
24
- "@awflow/avatar": "^1.0.1",
24
+ "@awflow/avatar": "^1.1.0",
25
25
  "@resvg/resvg-wasm": "2.6.2",
26
26
  "fflate": "0.8.3",
27
27
  "gifenc": "1.0.3"