@arraypress/covers 1.1.0 → 1.3.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.
- package/CHANGELOG.md +42 -0
- package/README.md +18 -809
- package/package.json +2 -1
- package/recipes/flyers/afterglow-motion.json +540 -0
- package/recipes/flyers/afterglow.json +70 -0
- package/recipes/flyers/arcfield-mirror-motion.json +397 -0
- package/recipes/flyers/arcfield-mirror.json +364 -0
- package/recipes/flyers/bauhaus-oktav.json +319 -0
- package/recipes/flyers/bauhaus-ritmo.json +735 -0
- package/recipes/flyers/bauhaus-volta.json +394 -0
- package/recipes/flyers/bloomfall.json +99 -0
- package/recipes/flyers/drag-solenne-motion.json +444 -0
- package/recipes/flyers/drag-solenne.json +426 -0
- package/recipes/flyers/fennick-patches.json +874 -0
- package/recipes/flyers/ferro-pass.json +398 -0
- package/recipes/flyers/ferrova.json +812 -0
- package/recipes/flyers/grid-rest-rhythm.json +125 -0
- package/recipes/flyers/groovy-honeycomb.json +816 -0
- package/recipes/flyers/groovy-marigold.json +479 -0
- package/recipes/flyers/groovy-peachfuzz.json +974 -0
- package/recipes/flyers/halden-orbit-motion.json +381 -0
- package/recipes/flyers/halden-orbit.json +356 -0
- package/recipes/flyers/halvard.json +120 -0
- package/recipes/flyers/kairo-weekender.json +696 -0
- package/recipes/flyers/linger-motion.json +518 -0
- package/recipes/flyers/linger.json +67 -0
- package/recipes/flyers/little-orchard.json +105 -0
- package/recipes/flyers/lofi-hexmoth.json +912 -0
- package/recipes/flyers/lofi-lowbit-motion.json +888 -0
- package/recipes/flyers/lofi-lowbit.json +880 -0
- package/recipes/flyers/lofi-nullpoint.json +3013 -0
- package/recipes/flyers/lumio-stack-motion.json +648 -0
- package/recipes/flyers/lumio-stack.json +438 -0
- package/recipes/flyers/magazine-cover.json +168 -0
- package/recipes/flyers/magazine-extended.json +269 -0
- package/recipes/flyers/magazine-knockout.json +40 -0
- package/recipes/flyers/northfold-varsity.json +618 -0
- package/recipes/flyers/open-studio.json +69 -0
- package/recipes/flyers/orrin-merch.json +715 -0
- package/recipes/flyers/oxyde.json +113 -0
- package/recipes/flyers/quiet-fields.json +610 -0
- package/recipes/flyers/scrapbook-halcyon.json +948 -0
- package/recipes/flyers/solenne-ticket.json +730 -0
- package/recipes/flyers/solvane-motion.json +576 -0
- package/recipes/flyers/solvane.json +98 -0
- package/recipes/flyers/subduct.json +99 -0
- package/recipes/flyers/sunvale-lineup.json +584 -0
- package/recipes/flyers/tidewell.json +91 -0
- package/recipes/flyers/tour-kairo.json +535 -0
- package/recipes/flyers/velora-stickers.json +885 -0
- package/recipes/flyers/wheatpaste-black.json +313 -0
- package/recipes/flyers/wheatpaste-mark.json +198 -0
- package/recipes/flyers/wheatpaste-torn.json +114 -0
- package/recipes/flyers/wheatpaste-wall.json +984 -0
- package/recipes/flyers/wheatpaste-words.json +233 -0
- package/recipes/release/aura.json +137 -0
- package/recipes/release/chrome.json +433 -0
- package/recipes/release/collage.json +372 -0
- package/recipes/release/duotone.json +113 -0
- package/recipes/release/film-still.json +102 -0
- package/recipes/release/headline.json +70 -0
- package/recipes/release/instant.json +170 -0
- package/recipes/release/label.json +364 -0
- package/recipes/release/liquid-glass.json +301 -0
- package/recipes/release/odd-tile-dark.json +688 -0
- package/recipes/release/odd-tile-dense.json +1056 -0
- package/recipes/release/odd-tile-shift.json +541 -0
- package/recipes/release/odd-tile-split.json +571 -0
- package/recipes/release/odd-tile.json +633 -0
- package/recipes/release/off-edge.json +96 -0
- package/recipes/release/plinth.json +794 -0
- package/recipes/release/riso.json +108 -0
- package/recipes/release/series/fluid-label.json +561 -0
- package/recipes/release/series/fluid-pixel.json +334 -0
- package/recipes/release/series/hairline.json +305 -0
- package/recipes/release/series/halo.json +282 -0
- package/recipes/release/series/mark-crop.json +135 -0
- package/recipes/release/series/mark-window.json +150 -0
- package/recipes/release/series/photo-label.json +475 -0
- package/recipes/release/series/smear-field.json +345 -0
- package/recipes/release/series/smear.json +202 -0
- package/recipes/release/series/spin.json +172 -0
- package/recipes/release/series/split-ghost.json +230 -0
- package/recipes/release/series/zoom.json +168 -0
- package/recipes/release/small-print.json +114 -0
- package/recipes/release/stack.json +156 -0
- package/recipes/release/swiss.json +117 -0
- package/schema/recipe.schema.json +332 -65
- package/src/animate/index.d.ts +9 -19
- package/src/animate/index.js +6 -111
- package/src/animate/keyframes.d.ts +21 -0
- package/src/animate/keyframes.js +119 -0
- package/src/animate/marquee.d.ts +30 -0
- package/src/animate/marquee.js +93 -0
- package/src/animate/morph.d.ts +8 -0
- package/src/animate/morph.js +99 -0
- package/src/animate/motion.d.ts +59 -0
- package/src/animate/motion.js +63 -0
- package/src/animate/sheet.d.ts +19 -0
- package/src/animate/sheet.js +61 -0
- package/src/animate/timing.d.ts +28 -0
- package/src/animate/timing.js +146 -0
- package/src/animate/tracks.d.ts +18 -0
- package/src/animate/tracks.js +139 -0
- package/src/diagram.js +4 -3
- package/src/extract.js +2 -2
- package/src/fonts.js +1 -2
- package/src/geometry.js +4 -34
- package/src/grid.js +1 -1
- package/src/handmade.js +9 -7
- package/src/icons.js +1 -2
- package/src/index.d.ts +39 -48
- package/src/index.js +5 -5
- package/src/inter-data.js +1 -1
- package/src/json-recipe.js +23 -7
- package/src/layer-options.js +21 -1
- package/src/layers/barcode.js +3 -3
- package/src/layers/geometry.js +5 -5
- package/src/layers/perforation.js +3 -3
- package/src/layers/repeat.js +2 -2
- package/src/layers/shape.d.ts +2 -2
- package/src/layers/shape.js +5 -5
- package/src/layers/soft.js +2 -2
- package/src/layers/svg.js +2 -3
- package/src/layers/table.js +3 -3
- package/src/layers/tear.d.ts +1 -1
- package/src/layers/tear.js +4 -4
- package/src/layers/texture.js +3 -3
- package/src/layers/util.js +15 -2
- package/src/layers.js +10 -26
- package/src/{effects/paint.js → paint/index.js} +5 -5
- package/src/{effects/paint-presets.d.ts → paint/presets.d.ts} +1 -1
- package/src/{effects/paint-presets.js → paint/presets.js} +1 -1
- package/src/{effects → paint}/tile.d.ts +1 -1
- package/src/{effects → paint}/tile.js +4 -4
- package/src/palette.js +1 -1
- package/src/platform/files.d.ts +6 -0
- package/src/platform/files.js +41 -0
- package/src/platform/harfbuzz.js +1 -1
- package/src/platform/index.d.ts +3 -3
- package/src/platform/index.js +6 -4
- package/src/platform/load.js +1 -2
- package/src/print.js +9 -7
- package/src/recipes.js +1 -1
- package/src/render/backdrop.js +2 -2
- package/src/render/grain.js +1 -1
- package/src/render/group.js +3 -3
- package/src/render/key.js +1 -2
- package/src/{effects → render}/transform.d.ts +2 -1
- package/src/{effects → render}/transform.js +17 -5
- package/src/render/use.d.ts +1 -1
- package/src/render/use.js +2 -2
- package/src/render/wrap.js +5 -60
- package/src/render.js +55 -18
- package/src/text.js +1 -1
- package/src/type/auto-color.js +1 -1
- package/src/type/glyphs.js +2 -2
- package/src/type/line.js +1 -1
- package/src/type/live.js +1 -1
- package/src/type/path.js +2 -2
- package/src/type/pixel.d.ts +0 -2
- package/src/type/pixel.js +3 -33
- package/src/type/spans.js +2 -2
- package/src/type/typeset.js +19 -8
- package/src/version.js +1 -1
- package/src/color.js +0 -246
- package/src/effects/ascii.d.ts +0 -7
- package/src/effects/ascii.js +0 -93
- package/src/effects/buffer.d.ts +0 -60
- package/src/effects/buffer.js +0 -472
- package/src/effects/detail.d.ts +0 -12
- package/src/effects/detail.js +0 -124
- package/src/effects/digital.d.ts +0 -14
- package/src/effects/digital.js +0 -216
- package/src/effects/distort.d.ts +0 -20
- package/src/effects/distort.js +0 -314
- package/src/effects/dither.d.ts +0 -8
- package/src/effects/dither.js +0 -147
- package/src/effects/drop-shadow.d.ts +0 -4
- package/src/effects/drop-shadow.js +0 -45
- package/src/effects/extrude.d.ts +0 -5
- package/src/effects/extrude.js +0 -55
- package/src/effects/film.d.ts +0 -11
- package/src/effects/film.js +0 -204
- package/src/effects/glow.d.ts +0 -8
- package/src/effects/glow.js +0 -56
- package/src/effects/grade.d.ts +0 -30
- package/src/effects/grade.js +0 -274
- package/src/effects/halftone.d.ts +0 -8
- package/src/effects/halftone.js +0 -95
- package/src/effects/index.d.ts +0 -125
- package/src/effects/index.js +0 -277
- package/src/effects/ink.d.ts +0 -12
- package/src/effects/ink.js +0 -70
- package/src/effects/lens.d.ts +0 -14
- package/src/effects/lens.js +0 -277
- package/src/effects/light.d.ts +0 -10
- package/src/effects/light.js +0 -59
- package/src/effects/looks.d.ts +0 -9
- package/src/effects/looks.js +0 -74
- package/src/effects/mask.d.ts +0 -52
- package/src/effects/mask.js +0 -314
- package/src/effects/motion.d.ts +0 -6
- package/src/effects/motion.js +0 -60
- package/src/effects/newsprint.d.ts +0 -6
- package/src/effects/newsprint.js +0 -89
- package/src/effects/optics.d.ts +0 -10
- package/src/effects/optics.js +0 -213
- package/src/effects/options.d.ts +0 -6
- package/src/effects/options.js +0 -106
- package/src/effects/pixels.d.ts +0 -24
- package/src/effects/pixels.js +0 -138
- package/src/effects/polar.d.ts +0 -10
- package/src/effects/polar.js +0 -265
- package/src/effects/region.d.ts +0 -8
- package/src/effects/region.js +0 -136
- package/src/effects/round-border.d.ts +0 -5
- package/src/effects/round-border.js +0 -62
- package/src/effects/screen.d.ts +0 -14
- package/src/effects/screen.js +0 -212
- package/src/effects/sort.d.ts +0 -8
- package/src/effects/sort.js +0 -125
- package/src/effects/symmetry.d.ts +0 -6
- package/src/effects/symmetry.js +0 -74
- package/src/effects/tone.d.ts +0 -16
- package/src/effects/tone.js +0 -117
- package/src/effects/util.d.ts +0 -16
- package/src/effects/util.js +0 -85
- package/src/platform/bytes.d.ts +0 -22
- package/src/platform/bytes.js +0 -103
- package/src/platform/compress.d.ts +0 -12
- package/src/platform/compress.js +0 -81
- package/src/platform/node.d.ts +0 -26
- package/src/platform/node.js +0 -86
- package/src/png.js +0 -126
- package/src/raster/index.d.ts +0 -24
- package/src/raster/index.js +0 -171
- package/src/render/content.d.ts +0 -5
- package/src/render/content.js +0 -58
- package/src/render/tiles.d.ts +0 -6
- package/src/render/tiles.js +0 -77
- package/src/rng.js +0 -95
- package/src/svg.js +0 -69
- /package/src/{effects/paint.d.ts → paint/index.d.ts} +0 -0
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
* Tile paints: a pattern repeated over a shape (checker, stripes, dots,
|
|
3
3
|
* grid), or a custom tile of markup.
|
|
4
4
|
*
|
|
5
|
-
* @module
|
|
5
|
+
* @module paint/tile
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { attrs, num } from '
|
|
9
|
-
import { len } from '
|
|
10
|
-
import { toHex } from '
|
|
8
|
+
import { attrs, num } from '@logomarks/image-effects/svg';
|
|
9
|
+
import { len } from '@logomarks/image-effects/length';
|
|
10
|
+
import { toHex } from '@logomarks/image-effects/color';
|
|
11
11
|
import { sanitize } from '../layers/svg.js';
|
|
12
12
|
|
|
13
13
|
/** Tile kinds. */
|
package/src/palette.js
CHANGED
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
22
|
import { relativeLuminance, contrastRatio, readableOn, readableText } from '@arraypress/color-utils';
|
|
23
|
-
import { toHex, toOklch, fromOklch, mixOklab } from '
|
|
23
|
+
import { toHex, toOklch, fromOklch, mixOklab } from '@logomarks/image-effects/color';
|
|
24
24
|
|
|
25
25
|
/**
|
|
26
26
|
* Curated presets. Each is a full palette; the light ones pair with the
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// Files read from disk in Node.
|
|
2
|
+
|
|
3
|
+
/** Read a text file synchronously (Node). */
|
|
4
|
+
export function readTextSync(path: string | URL): string;
|
|
5
|
+
/** A file shipped with the package, from disk (Node), or undefined. */
|
|
6
|
+
export function packageFileSync(rel: string): Uint8Array | undefined;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Files read from disk in Node: a recipe's JSON, and the fonts that ship
|
|
3
|
+
* with this package. Node's built-ins come from the image-effects platform
|
|
4
|
+
* adapter (`process.getBuiltinModule`, or what `@arraypress/covers/node`
|
|
5
|
+
* installs).
|
|
6
|
+
*
|
|
7
|
+
* @module platform/files
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { node } from '@logomarks/image-effects/platform';
|
|
11
|
+
|
|
12
|
+
/** This module was loaded from disk (Node, Deno, Bun), not a bundle or a page. */
|
|
13
|
+
const FROM_DISK = String(import.meta.url).startsWith('file:');
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Read a text file synchronously (Node only; `loadRecipe`).
|
|
17
|
+
*
|
|
18
|
+
* @param {string | URL} path
|
|
19
|
+
* @returns {string}
|
|
20
|
+
*/
|
|
21
|
+
export function readTextSync(path) {
|
|
22
|
+
const fs = node('fs');
|
|
23
|
+
if (!fs) throw new Error('Reading a file synchronously needs Node. In a browser or Worker, fetch the JSON and pass it to defineRecipe().');
|
|
24
|
+
return fs.readFileSync(path, 'utf8');
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Read a file that ships with this package (Node, from disk), or undefined.
|
|
29
|
+
*
|
|
30
|
+
* @param {string} rel - Relative to the package root (`'fonts/inter-latin-400.woff'`).
|
|
31
|
+
* @returns {Uint8Array | undefined}
|
|
32
|
+
*/
|
|
33
|
+
export function packageFileSync(rel) {
|
|
34
|
+
const fs = node('fs');
|
|
35
|
+
if (!fs || !FROM_DISK) return undefined;
|
|
36
|
+
try {
|
|
37
|
+
return fs.readFileSync(new URL(`../../${rel}`, import.meta.url));
|
|
38
|
+
} catch {
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
41
|
+
}
|
package/src/platform/harfbuzz.js
CHANGED
package/src/platform/index.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
// The platform adapter.
|
|
2
|
-
export { node, installNode, isNode,
|
|
3
|
-
export type { NodeModules } from '
|
|
2
|
+
export { node, installNode, isNode, deflateSync, deflate, inflate, unbrotli, unbrotliSync, u8, isBytes, toBase64, fromBase64, latin1, utf8, encodeUtf8, concat, fromDataUri, hexDigest } from '@logomarks/image-effects/platform';
|
|
3
|
+
export type { NodeModules } from '@logomarks/image-effects/platform';
|
|
4
|
+
export { readTextSync, packageFileSync } from './files.js';
|
|
4
5
|
export { settings, configure, registry } from './settings.js';
|
|
5
6
|
export type { Settings } from './settings.js';
|
|
6
7
|
export { loadBytes } from './load.js';
|
|
7
|
-
export { deflateSync, deflate, inflate, unbrotli, unbrotliSync } from './compress.js';
|
|
8
8
|
export { harfbuzz, shaperModule } from './harfbuzz.js';
|
package/src/platform/index.js
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The platform: everything that differs between Node, a browser and a
|
|
3
|
-
* Cloudflare Worker, behind one small adapter.
|
|
4
|
-
*
|
|
3
|
+
* Cloudflare Worker, behind one small adapter. Node's built-ins, zlib and
|
|
4
|
+
* the byte helpers are image-effects' (one registry, so
|
|
5
|
+
* `@arraypress/covers/node` installs for both); settings, loading, files
|
|
6
|
+
* and HarfBuzz are covers' own. Nothing here imports a `node:` module.
|
|
5
7
|
*
|
|
6
8
|
* @module platform
|
|
7
9
|
*/
|
|
8
10
|
|
|
9
|
-
export { node, installNode, isNode,
|
|
11
|
+
export { node, installNode, isNode, deflateSync, deflate, inflate, unbrotli, unbrotliSync, u8, isBytes, toBase64, fromBase64, latin1, utf8, encodeUtf8, concat, fromDataUri, hexDigest } from '@logomarks/image-effects/platform';
|
|
12
|
+
export { readTextSync, packageFileSync } from './files.js';
|
|
10
13
|
export { settings, configure, registry } from './settings.js';
|
|
11
14
|
export { loadBytes } from './load.js';
|
|
12
|
-
export { deflateSync, deflate, inflate, unbrotli, unbrotliSync } from './compress.js';
|
|
13
15
|
export { harfbuzz, shaperModule } from './harfbuzz.js';
|
package/src/platform/load.js
CHANGED
|
@@ -6,8 +6,7 @@
|
|
|
6
6
|
* @module platform/load
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import { isBytes, u8, fromDataUri } from '
|
|
10
|
-
import { node, isNode } from './node.js';
|
|
9
|
+
import { isBytes, u8, fromDataUri, node, isNode } from '@logomarks/image-effects/platform';
|
|
11
10
|
import { settings } from './settings.js';
|
|
12
11
|
|
|
13
12
|
/** @returns {boolean} */
|
package/src/print.js
CHANGED
|
@@ -5,11 +5,13 @@
|
|
|
5
5
|
* @module print
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { attrs, num,
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
8
|
+
import { attrs, num, esc } from '@logomarks/image-effects/svg';
|
|
9
|
+
import { layerAttrs } from './layers/util.js';
|
|
10
|
+
import { len } from '@logomarks/image-effects/length';
|
|
11
|
+
import { regionPx } from './geometry.js';
|
|
12
|
+
import { toHex } from '@logomarks/image-effects/color';
|
|
13
|
+
import { createRng } from '@logomarks/image-effects/rng';
|
|
14
|
+
import { encodePngAsync, pngUri } from '@logomarks/image-effects/png';
|
|
13
15
|
import { text as textLayer } from './layers.js';
|
|
14
16
|
|
|
15
17
|
/** Classic risograph inks, by name (`'riso:pink'`). Approximations, not trademarks. */
|
|
@@ -181,7 +183,7 @@ export async function ink(layer, ctx) {
|
|
|
181
183
|
/**
|
|
182
184
|
* Seeded value noise in 2D, smooth, in [0, 1], with octaves.
|
|
183
185
|
*
|
|
184
|
-
* @param {import('
|
|
186
|
+
* @param {import('@logomarks/image-effects/rng').Rng} rng
|
|
185
187
|
* @param {number} octaves
|
|
186
188
|
* @returns {(x: number, y: number) => number}
|
|
187
189
|
*/
|
|
@@ -457,7 +459,7 @@ function smoothPath(pts) {
|
|
|
457
459
|
* Seeded demo peaks: a smooth loudness envelope with transients. Not audio:
|
|
458
460
|
* pass `peaks` or `audio` for a real episode.
|
|
459
461
|
*
|
|
460
|
-
* @param {import('
|
|
462
|
+
* @param {import('@logomarks/image-effects/rng').Rng} rng
|
|
461
463
|
* @param {number} n
|
|
462
464
|
* @returns {number[]}
|
|
463
465
|
*/
|
package/src/recipes.js
CHANGED
|
@@ -33,7 +33,7 @@ import { BUILTIN_JSON } from './builtins.js';
|
|
|
33
33
|
* @property {number} width - Canvas width.
|
|
34
34
|
* @property {number} height - Canvas height.
|
|
35
35
|
* @property {import('./index.js').Palette} palette - Resolved palette.
|
|
36
|
-
* @property {import('
|
|
36
|
+
* @property {import('@logomarks/image-effects/rng').Rng} rng - The cover's random stream (for recipe-level choices).
|
|
37
37
|
* @property {number} u - The design unit: `sqrt(width × height)`.
|
|
38
38
|
* @property {string} seed - The cover's seed (JSON recipes seed each layer's draws from it).
|
|
39
39
|
*/
|
package/src/render/backdrop.js
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
* @module render/backdrop
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { toHex } from '
|
|
9
|
-
import { pixelsOf } from '
|
|
8
|
+
import { toHex } from '@logomarks/image-effects/color';
|
|
9
|
+
import { pixelsOf } from '@logomarks/image-effects/raster';
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* The average colour under a box, from the layers drawn so far: rendered
|
package/src/render/grain.js
CHANGED
|
@@ -16,7 +16,7 @@ import { VARY_FLOOR, VARY_PEAK } from '../layers.js';
|
|
|
16
16
|
* @param {any} sharp - The sharp module.
|
|
17
17
|
* @param {number} width @param {number} height
|
|
18
18
|
* @param {import('../index.js').GrainLayer} layer
|
|
19
|
-
* @param {import('
|
|
19
|
+
* @param {import('@logomarks/image-effects/rng').Rng} rng - The layer's stream.
|
|
20
20
|
* @returns {Promise<{ data: Buffer, channels: 1 | 3 }>} Full-size noise.
|
|
21
21
|
*/
|
|
22
22
|
export async function noise(sharp, width, height, layer, rng) {
|
package/src/render/group.js
CHANGED
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|
* @module render/group
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import { attrs, esc, num } from '
|
|
7
|
+
import { attrs, esc, num } from '@logomarks/image-effects/svg';
|
|
8
8
|
import { pos } from '../layers/util.js';
|
|
9
9
|
import { scaledReport } from './report.js';
|
|
10
|
-
import { IDENTITY, compose } from '
|
|
10
|
+
import { IDENTITY, compose } from './transform.js';
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* `group` — a stack of layers drawn as one: in a box (`x`, `y`, `width`,
|
|
@@ -40,7 +40,7 @@ export async function renderGroup(layer, ctx, decorate) {
|
|
|
40
40
|
const tx = bx + (bw - vw * sx) * ax;
|
|
41
41
|
const ty = by + (bh - vh * sy) * ay;
|
|
42
42
|
const k = Math.min(sx, sy);
|
|
43
|
-
/** @type {import('
|
|
43
|
+
/** @type {import('./transform.js').Affine} Grid units to the box. */
|
|
44
44
|
const grid = [sx, 0, 0, sy, tx - vx * sx, ty - vy * sy];
|
|
45
45
|
const parts = await ctx.stack(layer.layers ?? [], { tag: 'g', width: vw, height: vh, scale: k, res: Math.max(sx, sy), matrix: grid });
|
|
46
46
|
let defs = parts.map((/** @type {any} */ p) => p.defs).join('');
|
package/src/render/key.js
CHANGED
|
@@ -4,8 +4,7 @@
|
|
|
4
4
|
* @module render/key
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import { loadBytes } from '../platform/index.js';
|
|
8
|
-
import { hexDigest, u8, isBytes } from '../platform/bytes.js';
|
|
7
|
+
import { loadBytes, hexDigest, u8, isBytes } from '../platform/index.js';
|
|
9
8
|
import { fontBytes } from '../fonts.js';
|
|
10
9
|
import { cover } from '../render.js';
|
|
11
10
|
import { VERSION } from '../version.js';
|
|
@@ -15,7 +15,8 @@ export interface LayerTransform {
|
|
|
15
15
|
origin?: [Length, Length] | { x?: Length; y?: Length } | 'center';
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
/** A transform's `origin` in px (`'center'`, the default, is the canvas centre). */
|
|
19
|
+
export function originOf(origin: LayerTransform['origin'], canvas: { width: number; height: number }): [number, number];
|
|
19
20
|
/** A `transform` option as an SVG transform string (a string is used as written). */
|
|
20
21
|
export function transformAttr(t: LayerTransform | string | undefined, canvas: { width: number; height: number }): string | undefined;
|
|
21
22
|
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* A layer's `transform` option as an SVG transform.
|
|
3
3
|
*
|
|
4
|
-
* @module
|
|
4
|
+
* @module render/transform
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import { num } from '
|
|
7
|
+
import { num } from '@logomarks/image-effects/svg';
|
|
8
8
|
import { pos } from '../layers/util.js';
|
|
9
9
|
|
|
10
10
|
/**
|
|
@@ -25,9 +25,7 @@ export function transformAttr(t, canvas) {
|
|
|
25
25
|
const parts = [];
|
|
26
26
|
const tr = Array.isArray(t.translate) ? { x: t.translate[0], y: t.translate[1] } : t.translate;
|
|
27
27
|
if (tr) parts.push(`translate(${num(pos(tr.x ?? 0, 'x', canvas))} ${num(pos(tr.y ?? 0, 'y', canvas))})`);
|
|
28
|
-
const o = t.origin
|
|
29
|
-
? [canvas.width / 2, canvas.height / 2]
|
|
30
|
-
: Array.isArray(t.origin) ? [pos(t.origin[0], 'x', canvas), pos(t.origin[1], 'y', canvas)] : [pos(t.origin.x, 'x', canvas), pos(t.origin.y, 'y', canvas)];
|
|
28
|
+
const o = originOf(t.origin, canvas);
|
|
31
29
|
const inner = [];
|
|
32
30
|
if (t.rotate) inner.push(`rotate(${num(t.rotate, 3)})`);
|
|
33
31
|
let [sx, sy] = t.scale == null ? [1, 1] : Array.isArray(t.scale) ? t.scale : [t.scale, t.scale];
|
|
@@ -40,6 +38,20 @@ export function transformAttr(t, canvas) {
|
|
|
40
38
|
return parts.length ? parts.join(' ') : undefined;
|
|
41
39
|
}
|
|
42
40
|
|
|
41
|
+
/**
|
|
42
|
+
* A transform's `origin` in px: `[x, y]` or `{ x, y }` (px, `'%'` or
|
|
43
|
+
* lengths), or `'center'` (the default).
|
|
44
|
+
*
|
|
45
|
+
* @param {unknown} origin
|
|
46
|
+
* @param {{ width: number, height: number }} canvas
|
|
47
|
+
* @returns {[number, number]}
|
|
48
|
+
*/
|
|
49
|
+
export function originOf(origin, canvas) {
|
|
50
|
+
if (origin === undefined || origin === 'center') return [canvas.width / 2, canvas.height / 2];
|
|
51
|
+
const o = /** @type {any} */ (origin);
|
|
52
|
+
return Array.isArray(o) ? [pos(o[0], 'x', canvas), pos(o[1], 'y', canvas)] : [pos(o.x, 'x', canvas), pos(o.y, 'y', canvas)];
|
|
53
|
+
}
|
|
54
|
+
|
|
43
55
|
/** @typedef {[number, number, number, number, number, number]} Affine - `[a, b, c, d, e, f]`, as SVG's `matrix()`. */
|
|
44
56
|
|
|
45
57
|
/** @param {Affine} m @param {Affine} n @returns {Affine} `m × n`. */
|
package/src/render/use.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Reusable stacks: `defs` and `use`.
|
|
2
2
|
import type { Layer, BaseLayer, Length } from '../index.js';
|
|
3
|
-
import type { LayerTransform } from '
|
|
3
|
+
import type { LayerTransform } from './transform.js';
|
|
4
4
|
import type { RenderContext, Markup } from './context.js';
|
|
5
5
|
|
|
6
6
|
/** `defs`: named layers (each with an `id`) that `use` draws; nothing is drawn here. */
|
package/src/render/use.js
CHANGED
|
@@ -8,9 +8,9 @@
|
|
|
8
8
|
* @module render/use
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
import { attrs, num } from '
|
|
11
|
+
import { attrs, num } from '@logomarks/image-effects/svg';
|
|
12
12
|
import { pos, wrap } from '../layers/util.js';
|
|
13
|
-
import { transformAttr } from '
|
|
13
|
+
import { transformAttr } from './transform.js';
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
16
|
* `defs`: register named layers for `use`.
|
package/src/render/wrap.js
CHANGED
|
@@ -5,12 +5,11 @@
|
|
|
5
5
|
* @module render/wrap
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { attrs, esc, num } from '
|
|
9
|
-
import {
|
|
10
|
-
import { transformMatrix, stretchOf, compose, IDENTITY } from '
|
|
8
|
+
import { attrs, esc, num } from '@logomarks/image-effects/svg';
|
|
9
|
+
import { drawEffects, contentBox } from '@logomarks/image-effects';
|
|
10
|
+
import { transformAttr, transformMatrix, stretchOf, compose, IDENTITY } from './transform.js';
|
|
11
11
|
import { renderGroup } from './group.js';
|
|
12
12
|
import { scaledReport } from './report.js';
|
|
13
|
-
import { contentBox } from './content.js';
|
|
14
13
|
|
|
15
14
|
/** Layer options handled here, for every layer type. */
|
|
16
15
|
export const WRAP_KEYS = ['effects', 'mask', 'knockout', 'clip', 'area'];
|
|
@@ -64,7 +63,7 @@ export async function renderWrapped(layer, ctx, scope, renderOne) {
|
|
|
64
63
|
|
|
65
64
|
let { defs, body } = grid
|
|
66
65
|
? await renderGroup(inner, lctx, (/** @type {any} */ part, /** @type {any} */ gctx) => decorate(part, layer, gctx))
|
|
67
|
-
: await
|
|
66
|
+
: await drawEffects(await renderOne(inner, lctx), layer.effects, lctx);
|
|
68
67
|
if (geometric) {
|
|
69
68
|
const t = transformAttr(layer.transform, lctx.canvas);
|
|
70
69
|
if (t) body = `<g transform="${t}">${body}</g>`;
|
|
@@ -104,61 +103,7 @@ function areaBox(a, ctx, scope) {
|
|
|
104
103
|
* @returns {Promise<{ defs: string, body: string }>}
|
|
105
104
|
*/
|
|
106
105
|
export async function decorate(m, layer, ctx) {
|
|
107
|
-
return decorateMasks(await
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* Effects, in order. Pixel effects are computed on pixels where something
|
|
112
|
-
* can draw them (sharp, a canvas) and copy effects drawn as copies; the
|
|
113
|
-
* rest are SVG filters. An effect's layer `mask` is drawn first (as
|
|
114
|
-
* markup, and as an image for a filter to mix through).
|
|
115
|
-
*
|
|
116
|
-
* @param {{ defs: string, body: string }} m
|
|
117
|
-
* @param {any} list
|
|
118
|
-
* @param {any} ctx
|
|
119
|
-
* @returns {Promise<{ defs: string, body: string }>}
|
|
120
|
-
*/
|
|
121
|
-
async function decorateEffects(m, list, ctx) {
|
|
122
|
-
if (!list) return m;
|
|
123
|
-
const effects = await Promise.all((Array.isArray(list) ? list : [list]).map((e, i) => withMask(typeof e === 'string' ? { kind: e } : e, ctx, i)));
|
|
124
|
-
let { defs, body } = m;
|
|
125
|
-
const filters = async (/** @type {number} */ from, /** @type {number} */ to) => {
|
|
126
|
-
if (to <= from) return;
|
|
127
|
-
// In renderImage, filters work over what the layer paints, not the whole canvas.
|
|
128
|
-
const out = applyEffects(body, effects.slice(from, to), ctx, from, await contentBox({ defs, body }, ctx), defs);
|
|
129
|
-
defs += out.defs;
|
|
130
|
-
body = out.body;
|
|
131
|
-
};
|
|
132
|
-
let from = 0;
|
|
133
|
-
for (let i = 0; i < effects.length; i++) {
|
|
134
|
-
const fx = effects[i];
|
|
135
|
-
if (!isRaster(fx, ctx)) continue;
|
|
136
|
-
await filters(from, i);
|
|
137
|
-
from = i + 1;
|
|
138
|
-
const raster = await rasterEffect(fx, { defs, body }, ctx, i);
|
|
139
|
-
if (raster) ({ defs, body } = raster);
|
|
140
|
-
else await filters(i, i + 1);
|
|
141
|
-
}
|
|
142
|
-
await filters(from, effects.length);
|
|
143
|
-
return { defs, body };
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
/**
|
|
147
|
-
* An effect with a layer `mask`: the mask's layers drawn (kept on the
|
|
148
|
-
* mask), and for a filter effect drawn as an image too, where something
|
|
149
|
-
* can draw pixels. Other effects come back as they are.
|
|
150
|
-
*
|
|
151
|
-
* @param {any} fx
|
|
152
|
-
* @param {any} ctx
|
|
153
|
-
* @param {number} i
|
|
154
|
-
* @returns {Promise<any>}
|
|
155
|
-
*/
|
|
156
|
-
async function withMask(fx, ctx, i) {
|
|
157
|
-
if (!fx?.mask?.layers || !ctx.stack) return fx;
|
|
158
|
-
const parts = await ctx.stack(fx.mask.layers, { tag: `fm${i}` });
|
|
159
|
-
const mask = { ...fx.mask, [RESOLVED]: { defs: parts.map((/** @type {any} */ p) => p.defs).join(''), body: parts.map((/** @type {any} */ p) => p.body).join('') } };
|
|
160
|
-
if (!isRaster(fx, ctx)) mask[RESOLVED].image = await maskImage(mask, ctx, fx.pad ?? 0.3, fx.kind === 'motion' ? fx.angle ?? 0 : 0);
|
|
161
|
-
return { ...fx, mask };
|
|
106
|
+
return decorateMasks(await drawEffects(m, layer.effects, ctx), layer, ctx);
|
|
162
107
|
}
|
|
163
108
|
|
|
164
109
|
/**
|
package/src/render.js
CHANGED
|
@@ -5,12 +5,12 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import { node } from './platform/index.js';
|
|
8
|
-
import { createRng } from '
|
|
9
|
-
import { resolveSize, unit
|
|
8
|
+
import { createRng } from '@logomarks/image-effects/rng';
|
|
9
|
+
import { resolveSize, unit } from './geometry.js';
|
|
10
|
+
import { len } from '@logomarks/image-effects/length';
|
|
10
11
|
import { resolveFormat, printMarks, zonesOverlay, safeBox } from './formats/index.js';
|
|
11
|
-
import { animate, isAnimated } from './animate/index.js';
|
|
12
|
+
import { animate, isAnimated, renderMotion, createSheet, nameSheet } from './animate/index.js';
|
|
12
13
|
import { preloadIcons } from './icons.js';
|
|
13
|
-
import { rasterize } from './raster/index.js';
|
|
14
14
|
import { resolvePalette } from './palette.js';
|
|
15
15
|
import { fontPool } from './fonts.js';
|
|
16
16
|
import { LAYERS } from './layers.js';
|
|
@@ -20,15 +20,15 @@ import { registerDefs, renderUse } from './render/use.js';
|
|
|
20
20
|
import { expandRepeat } from './layers/repeat.js';
|
|
21
21
|
import { scaledReport } from './render/report.js';
|
|
22
22
|
import { hasLayoutRef, resolveLayoutRefs } from './type/layout-ref.js';
|
|
23
|
-
import { svgPixels } from '
|
|
24
|
-
import { IDENTITY, compose } from './
|
|
25
|
-
import { needsPixels } from '
|
|
23
|
+
import { rasterize, svgPixels, webpImages } from '@logomarks/image-effects/raster';
|
|
24
|
+
import { IDENTITY, compose } from './render/transform.js';
|
|
25
|
+
import { needsPixels } from '@logomarks/image-effects';
|
|
26
26
|
import { backdropSampler } from './render/backdrop.js';
|
|
27
27
|
import { noise, applyGrain } from './render/grain.js';
|
|
28
28
|
import { extractPalette } from './extract.js';
|
|
29
29
|
import { recipes, applyOverrides } from './recipes.js';
|
|
30
30
|
import { defineRecipe } from './json-recipe.js';
|
|
31
|
-
import { attrs, esc, num } from '
|
|
31
|
+
import { attrs, esc, num } from '@logomarks/image-effects/svg';
|
|
32
32
|
|
|
33
33
|
export { VERSION } from './version.js';
|
|
34
34
|
export { coverKey } from './render/key.js';
|
|
@@ -39,7 +39,7 @@ export { safeBox } from './formats/index.js';
|
|
|
39
39
|
const built = new WeakSet();
|
|
40
40
|
|
|
41
41
|
/** Options that belong to the cover itself, never to a recipe layer. */
|
|
42
|
-
const COVER_KEYS = new Set(['width', 'height', 'aspect', 'seed', 'palette', 'recipe', 'layers', 'fonts', 'alt', 'radius', 'output', 'format', 'dpi', 'bleed', 'safe', 'cropMarks', 'cutLine', 'cut', 'textRender', 'overlay', 'loop']);
|
|
42
|
+
const COVER_KEYS = new Set(['width', 'height', 'aspect', 'seed', 'palette', 'recipe', 'layers', 'fonts', 'alt', 'radius', 'output', 'format', 'dpi', 'bleed', 'safe', 'cropMarks', 'cutLine', 'cut', 'textRender', 'overlay', 'loop', 'animate', 'poster']);
|
|
43
43
|
|
|
44
44
|
/**
|
|
45
45
|
* Build a cover: resolve its size and palette and expand its recipe into a
|
|
@@ -120,6 +120,8 @@ export function cover(options) {
|
|
|
120
120
|
if (fmt?.zones) /** @type {any} */ (result).zones = fmt.zones;
|
|
121
121
|
if (o.overlay) /** @type {any} */ (result).overlay = o.overlay;
|
|
122
122
|
if (o.loop != null) /** @type {any} */ (result).loop = o.loop;
|
|
123
|
+
if (o.animate === false) /** @type {any} */ (result).animate = false;
|
|
124
|
+
if (o.poster != null) /** @type {any} */ (result).poster = o.poster;
|
|
123
125
|
built.add(result);
|
|
124
126
|
return result;
|
|
125
127
|
}
|
|
@@ -136,7 +138,9 @@ export function cover(options) {
|
|
|
136
138
|
* @property {((box: { x: number, y: number, width: number, height: number }) => Promise<string | undefined>) | undefined} backdrop
|
|
137
139
|
* @property {((entry: any) => void) | undefined} report
|
|
138
140
|
* @property {object} doc - Identity of the document being rendered.
|
|
139
|
-
* @property {number} [t] - The time being drawn (seconds), for animated layers.
|
|
141
|
+
* @property {number} [t] - The time being drawn (seconds), for animated layers: the frame stills show.
|
|
142
|
+
* @property {boolean} [live] - Motion plays in the SVG (CSS keyframes in `sheet`).
|
|
143
|
+
* @property {import('./animate/sheet.js').Sheet} [sheet]
|
|
140
144
|
*/
|
|
141
145
|
|
|
142
146
|
/**
|
|
@@ -172,12 +176,13 @@ async function renderStack(layers, scope) {
|
|
|
172
176
|
if (!layer || !Object.hasOwn(LAYERS, layer.type)) throw new TypeError(`Unknown layer type "${layer?.type}". Types: ${Object.keys(LAYERS).join(', ')}`);
|
|
173
177
|
const n = (seen[layer.type] = (seen[layer.type] ?? -1) + 1);
|
|
174
178
|
const key = layer.seed ?? layer.id ?? `${layer.type}#${n}`;
|
|
179
|
+
const stream = scope.seedPrefix ? `${c.seed}:${scope.seedPrefix}/${key}` : `${c.seed}:${key}`;
|
|
175
180
|
const ctx = /** @type {any} */ ({
|
|
176
181
|
width: scope.width,
|
|
177
182
|
height: scope.height,
|
|
178
183
|
canvas: { width: scope.width, height: scope.height },
|
|
179
184
|
palette: c.palette,
|
|
180
|
-
rng: createRng(
|
|
185
|
+
rng: createRng(stream),
|
|
181
186
|
id: top ? `l${i}` : `${scope.prefix}_${i}`,
|
|
182
187
|
fonts: scope.fonts,
|
|
183
188
|
sharp: scope.sharp,
|
|
@@ -214,7 +219,16 @@ async function renderStack(layers, scope) {
|
|
|
214
219
|
height: sub.height ?? self.height,
|
|
215
220
|
});
|
|
216
221
|
};
|
|
217
|
-
|
|
222
|
+
// The layer drawn with its stream from the start (motion draws it more than once, and its frames with `extra` context).
|
|
223
|
+
const draw = (/** @type {any} */ l, /** @type {any} */ extra) => {
|
|
224
|
+
ctx.rng = createRng(stream);
|
|
225
|
+
const x = extra ? { ...ctx, ...extra } : ctx;
|
|
226
|
+
return wrapped(l) ? renderWrapped(l, x, scope, renderOne) : renderOne(l, x);
|
|
227
|
+
};
|
|
228
|
+
// Motion is drawn when it plays, or at a time a still asks for; otherwise the layer is at rest.
|
|
229
|
+
const markup = layer.motion != null && (scope.live || scope.t != null)
|
|
230
|
+
? await renderMotion(layer, ctx, { live: Boolean(scope.live), t: scope.t, sheet: /** @type {any} */ (scope.sheet), draw })
|
|
231
|
+
: await draw(layer);
|
|
218
232
|
out.push({ layer, ...markup, ctx });
|
|
219
233
|
}
|
|
220
234
|
return out;
|
|
@@ -244,7 +258,7 @@ async function renderOne(layer, ctx) {
|
|
|
244
258
|
*
|
|
245
259
|
* @param {import('./index.js').Cover} c
|
|
246
260
|
* @param {any} sharp
|
|
247
|
-
* @param {{ report?: (entry: any) => void, t?: number, raster?: boolean, reach?: { px: number } }} [extra] - `raster`: drawn for `renderImage` (librsvg), so effects may take its faster forms; `reach` collects how far filters reach, output px.
|
|
261
|
+
* @param {{ report?: (entry: any) => void, t?: number, live?: boolean, sheet?: import('./animate/sheet.js').Sheet, raster?: boolean, reach?: { px: number } }} [extra] - `t`: the time to draw (default the cover's `poster`); `live`: motion plays (into `sheet`); `raster`: drawn for `renderImage` (librsvg), so effects may take its faster forms; `reach` collects how far filters reach, output px.
|
|
248
262
|
*/
|
|
249
263
|
async function renderLayers(c, sharp, extra = {}) {
|
|
250
264
|
// Icon sets named anywhere in the stack load first (from node_modules, or configure({ icons }) elsewhere).
|
|
@@ -255,7 +269,7 @@ async function renderLayers(c, sharp, extra = {}) {
|
|
|
255
269
|
scan(c.layers);
|
|
256
270
|
if (names.size) await preloadIcons(names);
|
|
257
271
|
const auto = c.layers.some(usesAuto);
|
|
258
|
-
const t = /** @type {any} */ (
|
|
272
|
+
const t = extra.t ?? /** @type {any} */ (c).poster;
|
|
259
273
|
return renderStack(c.layers, {
|
|
260
274
|
c,
|
|
261
275
|
sharp,
|
|
@@ -270,6 +284,8 @@ async function renderLayers(c, sharp, extra = {}) {
|
|
|
270
284
|
layout: new Map(),
|
|
271
285
|
defs: new Map(),
|
|
272
286
|
t,
|
|
287
|
+
live: extra.live,
|
|
288
|
+
sheet: extra.sheet,
|
|
273
289
|
raster: extra.raster,
|
|
274
290
|
reach: extra.reach,
|
|
275
291
|
});
|
|
@@ -316,8 +332,12 @@ function documentFor(c, parts) {
|
|
|
316
332
|
* needs no fonts; images are embedded as data URIs; nothing is fetched.
|
|
317
333
|
* The same cover always gives byte-identical output.
|
|
318
334
|
*
|
|
335
|
+
* Layers with `motion` play in the SVG (CSS keyframes, stopped by
|
|
336
|
+
* `prefers-reduced-motion`), unless the cover has `animate: false` or a
|
|
337
|
+
* time is asked for: then it's a still.
|
|
338
|
+
*
|
|
319
339
|
* @param {import('./index.js').CoverOptions | import('./index.js').Cover} input - The cover.
|
|
320
|
-
* @param {{ t?: number }} [options] - `t`:
|
|
340
|
+
* @param {{ t?: number, images?: 'webp', quality?: number }} [options] - `t`: a still at this time, in seconds, for layers with keyframes or motion (static covers ignore it). `images: 'webp'`: embedded photos and pixel effects re-encoded as WebP where smaller (`quality` for photos, default 82; needs sharp), for SVGs shown in browsers (librsvg can't draw WebP).
|
|
321
341
|
* @returns {Promise<string>} The SVG document.
|
|
322
342
|
*
|
|
323
343
|
* @example
|
|
@@ -325,8 +345,24 @@ function documentFor(c, parts) {
|
|
|
325
345
|
*/
|
|
326
346
|
export async function renderSvg(input, options = {}) {
|
|
327
347
|
const c = cover(input);
|
|
348
|
+
return svgDocument(c, { ...options, live: options.t == null && /** @type {any} */ (c).animate !== false });
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* A cover's SVG document, playing its motion or still.
|
|
353
|
+
*
|
|
354
|
+
* @param {import('./index.js').Cover} c
|
|
355
|
+
* @param {{ t?: number, live?: boolean, images?: 'webp', quality?: number }} options
|
|
356
|
+
* @returns {Promise<string>}
|
|
357
|
+
*/
|
|
358
|
+
async function svgDocument(c, options) {
|
|
328
359
|
const sharp = c.layers.some(hasImage) ? await loadSharp(false) : undefined;
|
|
329
|
-
|
|
360
|
+
const sheet = options.live ? createSheet() : undefined;
|
|
361
|
+
const parts = await renderLayers(c, sharp, { t: options.t, live: options.live, sheet });
|
|
362
|
+
const svg = sheet && !sheet.empty ? nameSheet(documentFor(c, [{ defs: `<style>${sheet.css()}</style>`, body: '' }, ...parts])) : documentFor(c, parts);
|
|
363
|
+
if (!options.images) return svg;
|
|
364
|
+
if (options.images !== 'webp') throw new TypeError(`Unknown images "${options.images}" (webp)`);
|
|
365
|
+
return webpImages(svg, sharp ?? await loadSharp(false), { quality: options.quality });
|
|
330
366
|
}
|
|
331
367
|
|
|
332
368
|
/**
|
|
@@ -383,14 +419,15 @@ export async function renderReport(input) {
|
|
|
383
419
|
}
|
|
384
420
|
|
|
385
421
|
/**
|
|
386
|
-
* Render a cover to an image in the browser (a canvas; no sharp)
|
|
422
|
+
* Render a cover to an image in the browser (a canvas; no sharp): a still,
|
|
423
|
+
* the cover's `poster` frame or its layers at rest.
|
|
387
424
|
*
|
|
388
425
|
* @param {import('./index.js').CoverOptions | import('./index.js').Cover} input
|
|
389
426
|
* @param {{ format?: 'png' | 'jpeg' | 'webp', quality?: number, scale?: number, width?: number }} [options]
|
|
390
427
|
* @returns {Promise<Blob>}
|
|
391
428
|
*/
|
|
392
429
|
export async function renderBlob(input, options = {}) {
|
|
393
|
-
return rasterize(await
|
|
430
|
+
return rasterize(await svgDocument(cover(input), {}), options);
|
|
394
431
|
}
|
|
395
432
|
|
|
396
433
|
/** @type {Promise<any> | undefined} */
|
package/src/text.js
CHANGED
package/src/type/auto-color.js
CHANGED
package/src/type/glyphs.js
CHANGED
|
@@ -12,8 +12,8 @@
|
|
|
12
12
|
* @module type/glyphs
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
|
-
import { len } from '
|
|
16
|
-
import { toHex } from '
|
|
15
|
+
import { len } from '@logomarks/image-effects/length';
|
|
16
|
+
import { toHex } from '@logomarks/image-effects/color';
|
|
17
17
|
|
|
18
18
|
/** @param {any} list @param {number} i @param {any} fallback @returns {any} The i-th of a cycled list (or a single value). */
|
|
19
19
|
const at = (list, i, fallback) => (list == null ? fallback : Array.isArray(list) ? (list.length ? list[i % list.length] : fallback) : list);
|
package/src/type/line.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* @module type/line
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { num } from '
|
|
8
|
+
import { num } from '@logomarks/image-effects/svg';
|
|
9
9
|
import { loadFont, pickFont, variationsFor } from '../fonts.js';
|
|
10
10
|
import { shape, outline } from '../text.js';
|
|
11
11
|
import { pixelFace } from './pixel.js';
|