@pixerate/editor 0.266.2 → 0.268.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/dist/agent/index.cjs +2 -2
- package/dist/agent/index.js +1 -1
- package/dist/animation-editor/controller.d.cts +8 -0
- package/dist/animation-editor/controller.d.ts +8 -0
- package/dist/animation-editor/effect-tools.d.cts +1 -1
- package/dist/animation-editor/effect-tools.d.ts +1 -1
- package/dist/animation-editor/index.cjs +175 -108
- package/dist/animation-editor/index.js +94 -27
- package/dist/brushes/index.cjs +201 -75
- package/dist/brushes/index.d.cts +4 -3
- package/dist/brushes/index.d.ts +4 -3
- package/dist/brushes/index.js +138 -12
- package/dist/brushes/library.d.cts +27 -1
- package/dist/brushes/library.d.ts +27 -1
- package/dist/brushes/profiles.d.cts +10 -1
- package/dist/brushes/profiles.d.ts +10 -1
- package/dist/brushes/schema.d.cts +4 -0
- package/dist/brushes/schema.d.ts +4 -0
- package/dist/brushes/sources.d.cts +114 -0
- package/dist/brushes/sources.d.ts +114 -0
- package/dist/brushes/types.d.cts +37 -5
- package/dist/brushes/types.d.ts +37 -5
- package/dist/brushes/ui.d.cts +40 -4
- package/dist/brushes/ui.d.ts +40 -4
- package/dist/{brushstrokeRender-KD453OBX.js → brushstrokeRender-A727TTNQ.js} +7 -7
- package/dist/{brushstrokeRender-FSUWXUZY.cjs → brushstrokeRender-WR2N5HHY.cjs} +23 -23
- package/dist/canvas/brushTypes.d.cts +12 -0
- package/dist/canvas/brushTypes.d.ts +12 -0
- package/dist/canvas/brushstroke.d.cts +1 -1
- package/dist/canvas/brushstroke.d.ts +1 -1
- package/dist/canvas/drawController.d.cts +19 -4
- package/dist/canvas/drawController.d.ts +19 -4
- package/dist/canvas/index.cjs +144 -105
- package/dist/canvas/index.js +46 -7
- package/dist/{chunk-TOID45I2.cjs → chunk-2WOGXEDX.cjs} +1 -1
- package/dist/{chunk-XORJ7UZO.cjs → chunk-3VHH6V67.cjs} +13 -12
- package/dist/{chunk-3C33YTNN.js → chunk-3XDSNIGF.js} +73 -6
- package/dist/chunk-62MFUHSI.cjs +802 -0
- package/dist/{chunk-R44Y3EFS.js → chunk-74ODPZLX.js} +17 -345
- package/dist/{chunk-GCVFEPJG.js → chunk-ACTHSQOI.js} +182 -0
- package/dist/chunk-ADMRNWR7.js +340 -0
- package/dist/{chunk-MVUQSK5J.cjs → chunk-B3G7BKUV.cjs} +111 -439
- package/dist/{chunk-DXYHQ2VN.js → chunk-BAZMCFVL.js} +1 -1
- package/dist/{chunk-VRK322ZC.cjs → chunk-BPJSI4NT.cjs} +2 -2
- package/dist/{chunk-RFJZGTHA.cjs → chunk-EU2ZZA6L.cjs} +400 -69
- package/dist/{chunk-XC6DPRYI.js → chunk-GSTSZPSR.js} +154 -949
- package/dist/{chunk-EZOJBP4D.js → chunk-HFY5LG5J.js} +1 -1
- package/dist/chunk-HVO6XCI4.js +802 -0
- package/dist/{chunk-YHDHFJUC.cjs → chunk-J5VQYK7T.cjs} +217 -1012
- package/dist/{chunk-XRXFVRGY.cjs → chunk-JBB3GDBB.cjs} +2483 -208
- package/dist/{chunk-ACFSFUQH.js → chunk-JL6YH5WD.js} +4 -0
- package/dist/{chunk-CIJBSUM5.js → chunk-KZI3YWKW.js} +353 -22
- package/dist/{chunk-4ODRXQRW.js → chunk-LETF75CA.js} +1 -1
- package/dist/{chunk-FQPUF2W2.cjs → chunk-NIBFKXMD.cjs} +217 -35
- package/dist/{chunk-AQZQWXEU.cjs → chunk-NPWWSCWT.cjs} +35 -35
- package/dist/{chunk-DOZ6F2K2.js → chunk-NSETJI74.js} +3 -2
- package/dist/{chunk-PE6YJMQT.cjs → chunk-PFHJS4HE.cjs} +20 -19
- package/dist/{chunk-7KNW3WXZ.cjs → chunk-QEVLRLUN.cjs} +63 -0
- package/dist/{chunk-LK4SXL3V.js → chunk-QNY7BDFP.js} +1 -1
- package/dist/{chunk-OTN6VB5S.cjs → chunk-RDFHWAHL.cjs} +5 -1
- package/dist/{chunk-PXDIAYB6.cjs → chunk-STLTOISW.cjs} +34 -214
- package/dist/{chunk-T2LDTEXK.js → chunk-UVTO3D4E.js} +2333 -58
- package/dist/{chunk-6JL2D5SG.js → chunk-VE6UKYQ2.js} +3 -2
- package/dist/{chunk-TXFWZ5ZJ.js → chunk-W6JDUXP3.js} +4 -184
- package/dist/{chunk-3SBLVGOM.cjs → chunk-YOVXQHA3.cjs} +23 -23
- package/dist/{chunk-WLUVZKVD.cjs → chunk-YXP6X3SY.cjs} +103 -36
- package/dist/chunk-YZWM6ZIP.cjs +340 -0
- package/dist/{chunk-472INMWH.js → chunk-ZA7L5YJB.js} +63 -0
- package/dist/commands/builtins.d.cts +12 -1
- package/dist/commands/builtins.d.ts +12 -1
- package/dist/commands/index.cjs +2 -2
- package/dist/commands/index.js +1 -1
- package/dist/image-editor/controller.d.cts +1 -1
- package/dist/image-editor/controller.d.ts +1 -1
- package/dist/image-editor/index.cjs +868 -864
- package/dist/image-editor/index.js +123 -119
- package/dist/paint/index.cjs +8 -6
- package/dist/paint/index.js +56 -54
- package/dist/paint-editor/brush-library-tools.d.cts +12 -2
- package/dist/paint-editor/brush-library-tools.d.ts +12 -2
- package/dist/paint-editor/brush-model.d.cts +29 -1
- package/dist/paint-editor/brush-model.d.ts +29 -1
- package/dist/paint-editor/brush-source-art.d.cts +32 -0
- package/dist/paint-editor/brush-source-art.d.ts +32 -0
- package/dist/paint-editor/controller.d.cts +11 -4
- package/dist/paint-editor/controller.d.ts +11 -4
- package/dist/paint-editor/dab-stroke.d.cts +2 -0
- package/dist/paint-editor/dab-stroke.d.ts +2 -0
- package/dist/paint-editor/engine.d.cts +11 -2
- package/dist/paint-editor/engine.d.ts +11 -2
- package/dist/paint-editor/index.cjs +967 -2335
- package/dist/paint-editor/index.d.cts +3 -1
- package/dist/paint-editor/index.d.ts +3 -1
- package/dist/paint-editor/index.js +394 -1762
- package/dist/paint-editor/path-samples.d.cts +2 -0
- package/dist/paint-editor/path-samples.d.ts +2 -0
- package/dist/paint-editor/presence.d.cts +20 -1
- package/dist/paint-editor/presence.d.ts +20 -1
- package/dist/paint-editor/stamp-tip.d.cts +8 -5
- package/dist/paint-editor/stamp-tip.d.ts +8 -5
- package/dist/paint-editor/thumbnails.d.cts +6 -0
- package/dist/paint-editor/thumbnails.d.ts +6 -0
- package/dist/paint-editor/tip-grain.d.cts +34 -0
- package/dist/paint-editor/tip-grain.d.ts +34 -0
- package/dist/raster/index.cjs +2 -2
- package/dist/raster/index.js +9 -9
- package/dist/stickers/helpers.d.cts +2 -0
- package/dist/stickers/helpers.d.ts +2 -0
- package/dist/stickers/index.cjs +41 -35
- package/dist/stickers/index.js +22 -16
- package/dist/stickers/types.d.cts +4 -2
- package/dist/stickers/types.d.ts +4 -2
- package/dist/vector/backdrop.d.cts +13 -0
- package/dist/vector/backdrop.d.ts +13 -0
- package/dist/vector/brushstroke.d.cts +54 -7
- package/dist/vector/brushstroke.d.ts +54 -7
- package/dist/vector/brushstrokeMedia.d.cts +96 -0
- package/dist/vector/brushstrokeMedia.d.ts +96 -0
- package/dist/vector/controller.d.cts +17 -3
- package/dist/vector/controller.d.ts +17 -3
- package/dist/vector/index.cjs +1850 -1390
- package/dist/vector/index.d.cts +2 -0
- package/dist/vector/index.d.ts +2 -0
- package/dist/vector/index.js +520 -60
- package/dist/vector/render.d.cts +6 -0
- package/dist/vector/render.d.ts +6 -0
- package/dist/vector/types.d.cts +14 -0
- package/dist/vector/types.d.ts +14 -0
- package/package.json +1 -1
- package/skills/pixerate-animation-editor/SKILL.md +1 -0
- package/skills/pixerate-brushes/SKILL.md +37 -10
- package/skills/pixerate-brushes-design/SKILL.md +15 -5
- package/skills/pixerate-brushes-design/evals/port-to-vector.json +8 -8
- package/skills/pixerate-canvas/SKILL.md +11 -1
- package/skills/pixerate-image-editor/SKILL.md +19 -64
- package/skills/pixerate-image-editor/references/color-management.md +2 -1
- package/skills/pixerate-image-editor/references/free-transform.md +12 -0
- package/skills/pixerate-image-editor/references/layer-effects.md +10 -0
- package/skills/pixerate-image-editor/references/painting.md +17 -0
- package/skills/pixerate-image-editor/references/photoshop-files.md +1 -1
- package/skills/pixerate-image-editor/references/shapes-and-type.md +14 -0
- package/skills/pixerate-image-editor/references/smart-objects.md +10 -0
- package/skills/pixerate-paint-editor/SKILL.md +14 -4
- package/skills/pixerate-paint-editor/references/brush-facets.md +3 -1
- package/skills/pixerate-paint-editor/references/wet-media.md +9 -0
- package/skills/pixerate-paint-sketch-to-finish/SKILL.md +1 -0
- package/skills/pixerate-vector/SKILL.md +16 -6
- package/skills/pixerate-vector/references/best-practices.md +1 -0
- package/skills/pixerate-vector/references/commands.md +19 -0
package/dist/stickers/index.js
CHANGED
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
editorStickers,
|
|
4
4
|
stampOutline,
|
|
5
5
|
stampPixels
|
|
6
|
-
} from "../chunk-
|
|
6
|
+
} from "../chunk-NSETJI74.js";
|
|
7
7
|
import {
|
|
8
8
|
createInsertStickerTool,
|
|
9
9
|
createStickerToolProvider,
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
describeStickerRef,
|
|
12
12
|
insertSticker,
|
|
13
13
|
stickerForAgent
|
|
14
|
-
} from "../chunk-
|
|
14
|
+
} from "../chunk-VE6UKYQ2.js";
|
|
15
15
|
import {
|
|
16
16
|
DEFAULT_STICKER_PAGE,
|
|
17
17
|
EDITOR_STICKERS_ID,
|
|
@@ -25,6 +25,7 @@ import {
|
|
|
25
25
|
editorStampOf,
|
|
26
26
|
normalizeStickerRef,
|
|
27
27
|
parseStickerRef,
|
|
28
|
+
providerCollections,
|
|
28
29
|
stampCoverage,
|
|
29
30
|
stickerCollections,
|
|
30
31
|
stickerIdFor,
|
|
@@ -32,7 +33,7 @@ import {
|
|
|
32
33
|
stickerRefOf,
|
|
33
34
|
stickerVariantFor,
|
|
34
35
|
stickerVariantsOf
|
|
35
|
-
} from "../chunk-
|
|
36
|
+
} from "../chunk-JL6YH5WD.js";
|
|
36
37
|
import {
|
|
37
38
|
StickerError
|
|
38
39
|
} from "../chunk-SZTI6EER.js";
|
|
@@ -121,7 +122,9 @@ function createFakeStickerProvider(options = {}) {
|
|
|
121
122
|
if (!s) return void 0;
|
|
122
123
|
return { ...summaryOf(s), silhouette: s.shape === "badge" ? "M60 6A54 54 0 1 1 59.9 6Z" : "M10 10H110V110H10Z", colours: s.shape === "badge" ? [s.colours[0]] : [...s.colours] };
|
|
123
124
|
},
|
|
125
|
+
collections: () => stickerCollections(summaries, COLLECTIONS),
|
|
124
126
|
listCollections: () => stickerCollections(summaries, COLLECTIONS),
|
|
127
|
+
// the older name, kept for callers that use it
|
|
125
128
|
categories: () => [...new Set(CATALOGUE.map((s) => s.category))].map((c) => ({ id: c, name: c, count: CATALOGUE.filter((s) => s.category === c).length })),
|
|
126
129
|
async load(id, o = {}) {
|
|
127
130
|
if (options.loadDelayMs) await new Promise((r) => setTimeout(r, options.loadDelayMs));
|
|
@@ -157,7 +160,7 @@ function combineStickerProviders(providers, options = {}) {
|
|
|
157
160
|
const collectionOwner = (c) => {
|
|
158
161
|
const i = c.indexOf(":");
|
|
159
162
|
const p = i > 0 ? byId.get(c.slice(0, i)) : void 0;
|
|
160
|
-
if (!p) throw new StickerError("invalid_argument", `No collection "${c}"; collections here are "provider:collection" (
|
|
163
|
+
if (!p) throw new StickerError("invalid_argument", `No collection "${c}"; collections here are "provider:collection" (collections() lists them).`);
|
|
161
164
|
return { p, collection: p.namespaced ? c : c.slice(i + 1) };
|
|
162
165
|
};
|
|
163
166
|
const page = async (query, o = {}) => {
|
|
@@ -196,6 +199,11 @@ function combineStickerProviders(providers, options = {}) {
|
|
|
196
199
|
if (!next && items.length >= limit && i < providers.length) next = `${i}|`;
|
|
197
200
|
return { items, total, ...next ? { next } : {} };
|
|
198
201
|
};
|
|
202
|
+
const collections = async () => {
|
|
203
|
+
const out = [];
|
|
204
|
+
for (const p of providers) for (const c of await providerCollections(p) ?? []) out.push({ ...c, id: collectionOf(p, c.id) });
|
|
205
|
+
return out;
|
|
206
|
+
};
|
|
199
207
|
return {
|
|
200
208
|
id: options.id ?? "stickers",
|
|
201
209
|
name: options.name ?? providers.map((p) => p.name).join(" + "),
|
|
@@ -232,11 +240,8 @@ function combineStickerProviders(providers, options = {}) {
|
|
|
232
240
|
}
|
|
233
241
|
return [...counts.values()];
|
|
234
242
|
},
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
for (const p of providers) for (const c of await p.listCollections?.() ?? []) out.push({ ...c, id: collectionOf(p, c.id) });
|
|
238
|
-
return out;
|
|
239
|
-
}
|
|
243
|
+
collections,
|
|
244
|
+
listCollections: collections
|
|
240
245
|
};
|
|
241
246
|
}
|
|
242
247
|
|
|
@@ -263,7 +268,7 @@ function checkSummary(s, provider, issues) {
|
|
|
263
268
|
else if (!v.includes("light")) at("variants must include light (every sticker has its light art)");
|
|
264
269
|
}
|
|
265
270
|
if (s.collection !== void 0 && (typeof s.collection !== "string" || !s.collection)) at("collection must be a non-empty string when present");
|
|
266
|
-
if (s.collection !== void 0 && !provider.listCollections) at(`is in collection "${s.collection}" but the provider has no
|
|
271
|
+
if (s.collection !== void 0 && !provider.collections && !provider.listCollections) at(`is in collection "${s.collection}" but the provider has no collections()`);
|
|
267
272
|
}
|
|
268
273
|
async function checkStickerProvider(provider, options = {}) {
|
|
269
274
|
const issues = [];
|
|
@@ -322,24 +327,24 @@ async function checkStickerProvider(provider, options = {}) {
|
|
|
322
327
|
if (d?.colours?.some((c) => !HEX.test(c))) top(`describe("${first.id}").colours must be #rrggbb`);
|
|
323
328
|
}
|
|
324
329
|
if (await provider.describe("__no_such_sticker__") !== void 0) top("describe of an unknown id returned a sticker");
|
|
325
|
-
if (provider.listCollections) {
|
|
330
|
+
if (provider.collections || provider.listCollections) {
|
|
326
331
|
try {
|
|
327
|
-
const collections = await provider
|
|
332
|
+
const collections = await providerCollections(provider) ?? [];
|
|
328
333
|
const known = /* @__PURE__ */ new Set();
|
|
329
334
|
for (const c of collections) {
|
|
330
335
|
if (typeof c?.id !== "string" || !c.id || typeof c.name !== "string" || !c.name || !(Number.isInteger(c.count) && c.count >= 0)) {
|
|
331
|
-
top(`
|
|
336
|
+
top(`collections() returned ${JSON.stringify(c)}; each needs id, name and a count`);
|
|
332
337
|
continue;
|
|
333
338
|
}
|
|
334
|
-
if (known.has(c.id)) top(`
|
|
339
|
+
if (known.has(c.id)) top(`collections() lists "${c.id}" twice`);
|
|
335
340
|
known.add(c.id);
|
|
336
341
|
const page = await provider.list({ collection: c.id, limit: 100 });
|
|
337
342
|
if (page.total !== c.count) top(`collection "${c.id}" says count ${c.count} but list({ collection }) has ${page.total}`);
|
|
338
343
|
if (page.items.some((x) => x.collection !== c.id)) top(`list({ collection: "${c.id}" }) returned stickers of other collections`);
|
|
339
344
|
}
|
|
340
|
-
for (const x of all) if (x.collection !== void 0 && !known.has(x.collection)) issues.push({ stickerId: x.id, message: `collection "${x.collection}" is not in
|
|
345
|
+
for (const x of all) if (x.collection !== void 0 && !known.has(x.collection)) issues.push({ stickerId: x.id, message: `collection "${x.collection}" is not in collections()` });
|
|
341
346
|
} catch (e) {
|
|
342
|
-
top(`
|
|
347
|
+
top(`collections() rejected: ${e.message}`);
|
|
343
348
|
}
|
|
344
349
|
}
|
|
345
350
|
let rejected = false;
|
|
@@ -413,6 +418,7 @@ export {
|
|
|
413
418
|
insertSticker,
|
|
414
419
|
normalizeStickerRef,
|
|
415
420
|
parseStickerRef,
|
|
421
|
+
providerCollections,
|
|
416
422
|
stampCoverage,
|
|
417
423
|
stampOutline,
|
|
418
424
|
stampPixels,
|
|
@@ -61,7 +61,7 @@ export interface StickerSummary {
|
|
|
61
61
|
* picker or brush can pick the dark one for a dark background without calling `describe` first.
|
|
62
62
|
*/
|
|
63
63
|
variants?: StickerVariant[];
|
|
64
|
-
/** The collection it belongs to (`
|
|
64
|
+
/** The collection it belongs to (`collections()`), when the provider groups its stickers. */
|
|
65
65
|
collection?: string;
|
|
66
66
|
}
|
|
67
67
|
/** Everything about one sticker. */
|
|
@@ -93,7 +93,7 @@ export interface StickerPage {
|
|
|
93
93
|
}
|
|
94
94
|
export interface StickerSearchOptions {
|
|
95
95
|
category?: string;
|
|
96
|
-
/** Only stickers of this collection (`
|
|
96
|
+
/** Only stickers of this collection (`collections()`). */
|
|
97
97
|
collection?: string;
|
|
98
98
|
/** Every tag must match. */
|
|
99
99
|
tags?: string[];
|
|
@@ -144,6 +144,8 @@ export interface StickerProvider {
|
|
|
144
144
|
count: number;
|
|
145
145
|
}>>;
|
|
146
146
|
/** Its collections with counts, when it groups its stickers. Ids stay unique across collections, so `load(id)` needs none. */
|
|
147
|
+
collections?(): MaybePromise<StickerCollection[]>;
|
|
148
|
+
/** The older name for `collections()`, still read when a provider has no `collections()`. */
|
|
147
149
|
listCollections?(): MaybePromise<StickerCollection[]>;
|
|
148
150
|
}
|
|
149
151
|
/** `provider:id`, the name of a sticker across providers. A bare editor stamp (`"star"`) means `editor:star`. */
|
package/dist/stickers/types.d.ts
CHANGED
|
@@ -61,7 +61,7 @@ export interface StickerSummary {
|
|
|
61
61
|
* picker or brush can pick the dark one for a dark background without calling `describe` first.
|
|
62
62
|
*/
|
|
63
63
|
variants?: StickerVariant[];
|
|
64
|
-
/** The collection it belongs to (`
|
|
64
|
+
/** The collection it belongs to (`collections()`), when the provider groups its stickers. */
|
|
65
65
|
collection?: string;
|
|
66
66
|
}
|
|
67
67
|
/** Everything about one sticker. */
|
|
@@ -93,7 +93,7 @@ export interface StickerPage {
|
|
|
93
93
|
}
|
|
94
94
|
export interface StickerSearchOptions {
|
|
95
95
|
category?: string;
|
|
96
|
-
/** Only stickers of this collection (`
|
|
96
|
+
/** Only stickers of this collection (`collections()`). */
|
|
97
97
|
collection?: string;
|
|
98
98
|
/** Every tag must match. */
|
|
99
99
|
tags?: string[];
|
|
@@ -144,6 +144,8 @@ export interface StickerProvider {
|
|
|
144
144
|
count: number;
|
|
145
145
|
}>>;
|
|
146
146
|
/** Its collections with counts, when it groups its stickers. Ids stay unique across collections, so `load(id)` needs none. */
|
|
147
|
+
collections?(): MaybePromise<StickerCollection[]>;
|
|
148
|
+
/** The older name for `collections()`, still read when a provider has no `collections()`. */
|
|
147
149
|
listCollections?(): MaybePromise<StickerCollection[]>;
|
|
148
150
|
}
|
|
149
151
|
/** `provider:id`, the name of a sticker across providers. A bare editor stamp (`"star"`) means `editor:star`. */
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { RasterRect } from '../raster/surface.cjs';
|
|
2
|
+
import { type VectorPaintDef } from './gradient.cjs';
|
|
3
|
+
import type { VectorRenderNode } from './render.cjs';
|
|
4
|
+
import type { VectorMatrix } from './types.cjs';
|
|
5
|
+
type M = VectorMatrix;
|
|
6
|
+
/** An SVG `transform` attribute (`matrix`, `translate`, `scale`, `rotate`, in order) as a matrix. */
|
|
7
|
+
export declare function parseVectorTransform(value: string | undefined): M;
|
|
8
|
+
/**
|
|
9
|
+
* The render data's `nodes` (document space) drawn over `rect` (pixels) through `toPixels`, the
|
|
10
|
+
* map from document space to those pixels: straight-alpha RGBA, `rect.width × rect.height`.
|
|
11
|
+
*/
|
|
12
|
+
export declare function rasterizeVectorBackdrop(nodes: readonly VectorRenderNode[], defs: readonly VectorPaintDef[], toPixels: VectorMatrix, rect: RasterRect): Uint8ClampedArray;
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { RasterRect } from '../raster/surface.js';
|
|
2
|
+
import { type VectorPaintDef } from './gradient.js';
|
|
3
|
+
import type { VectorRenderNode } from './render.js';
|
|
4
|
+
import type { VectorMatrix } from './types.js';
|
|
5
|
+
type M = VectorMatrix;
|
|
6
|
+
/** An SVG `transform` attribute (`matrix`, `translate`, `scale`, `rotate`, in order) as a matrix. */
|
|
7
|
+
export declare function parseVectorTransform(value: string | undefined): M;
|
|
8
|
+
/**
|
|
9
|
+
* The render data's `nodes` (document space) drawn over `rect` (pixels) through `toPixels`, the
|
|
10
|
+
* map from document space to those pixels: straight-alpha RGBA, `rect.width × rect.height`.
|
|
11
|
+
*/
|
|
12
|
+
export declare function rasterizeVectorBackdrop(nodes: readonly VectorRenderNode[], defs: readonly VectorPaintDef[], toPixels: VectorMatrix, rect: RasterRect): Uint8ClampedArray;
|
|
13
|
+
export {};
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import { type PaintBrush } from '../paint-editor/brushes.cjs';
|
|
2
2
|
import type { Brush } from '../brushes/types.cjs';
|
|
3
|
-
import
|
|
3
|
+
import { type VectorBrushstrokeBackdrop } from './brushstrokeMedia.cjs';
|
|
4
|
+
import type { VectorBrushstroke, VectorBrushstrokePaint, VectorBrushstrokePaper, VectorDocument, VectorPathNode, VectorStrokeLayer } from './types.cjs';
|
|
4
5
|
/** Image pixels per document unit brushstrokes are rendered at. */
|
|
5
6
|
export declare const VECTOR_BRUSHSTROKE_SCALE = 2;
|
|
7
|
+
/** Whether a brush's engine paints vector brushstrokes: dry dab brushes, wet paint, the Mixer Brush, smudge and blender. */
|
|
8
|
+
export declare function isVectorBrushstrokeEngine(brush: PaintBrush): boolean;
|
|
6
9
|
/**
|
|
7
10
|
* The brushes a brushstroke can use, as the engine paints them: the presets `VECTOR_PROFILE`
|
|
8
|
-
* admits (dry
|
|
11
|
+
* admits (dry, wet paint, mixer, smudge and blender brushes), then the saved brushes given (facet brushes, e.g. a brush
|
|
9
12
|
* library's) it admits.
|
|
10
13
|
*/
|
|
11
14
|
export declare function vectorBrushstrokeBrushes(user?: readonly Brush[]): PaintBrush[];
|
|
@@ -30,6 +33,15 @@ export declare function vectorBrushstrokeDefaultColour(id: string, user?: readon
|
|
|
30
33
|
* kept in `spec`.
|
|
31
34
|
*/
|
|
32
35
|
export declare function sanitizeVectorBrushstroke(value: unknown, user?: readonly Brush[]): VectorBrushstroke | null;
|
|
36
|
+
/**
|
|
37
|
+
* What is wrong with a brushstroke's gradient `paint` (from an agent or a caller), or null when it
|
|
38
|
+
* is valid. Paint is `{ type: 'color', color }` or `{ type: 'gradient', stops, space, map, softness }`
|
|
39
|
+
* (the paint engine's `PaintSource`, lengths in document units). `velocity` and `time` mappings
|
|
40
|
+
* are refused: a vector path has no timing.
|
|
41
|
+
*/
|
|
42
|
+
export declare function vectorBrushstrokePaintError(paint: unknown): string | null;
|
|
43
|
+
/** Reads brushstroke paint from untrusted data (a stored layer); null when it is not valid paint. */
|
|
44
|
+
export declare function readVectorBrushstrokePaint(value: unknown): VectorBrushstrokePaint | null;
|
|
33
45
|
/** The largest brushstroke seed. */
|
|
34
46
|
export declare const VECTOR_BRUSHSTROKE_MAX_SEED = 2147483647;
|
|
35
47
|
/** Reads paper settings from untrusted data; null when there are none (the default paper). */
|
|
@@ -59,10 +71,45 @@ export interface VectorBrushstrokeImage {
|
|
|
59
71
|
width: number;
|
|
60
72
|
height: number;
|
|
61
73
|
}
|
|
74
|
+
/** Where and how a brushstroke renders. */
|
|
75
|
+
export interface VectorBrushstrokeContext {
|
|
76
|
+
/**
|
|
77
|
+
* The document the path is in. A Mixer Brush, smudge or blender acts on what lies beneath the
|
|
78
|
+
* stroke there; without it, only on the path's own fills and lower strokes.
|
|
79
|
+
*/
|
|
80
|
+
document?: VectorDocument | null;
|
|
81
|
+
/**
|
|
82
|
+
* `async` (default): a wet brushstroke shows the paint as laid until its simulation has settled
|
|
83
|
+
* (`onVectorBrushstrokesSettled` says when). `now`: settle it first (exports, agents).
|
|
84
|
+
*/
|
|
85
|
+
settle?: 'async' | 'now';
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* What lies beneath stroke layer `index` of `node`: the objects it depends on (compared by
|
|
89
|
+
* identity: any change beneath gives a new one), and the backdrop renderer. The scene renderer
|
|
90
|
+
* registers it (`setVectorBackdropSource`), as it is the one that can draw the scene.
|
|
91
|
+
*/
|
|
92
|
+
export type VectorBackdropSource = (document: VectorDocument | null, node: VectorPathNode, index: number, settle: 'async' | 'now') => {
|
|
93
|
+
deps: readonly unknown[];
|
|
94
|
+
backdrop: VectorBrushstrokeBackdrop;
|
|
95
|
+
};
|
|
96
|
+
/** Registers what draws the backdrop of mixer, smudge and blender brushstrokes (done by `render.ts`). */
|
|
97
|
+
export declare function setVectorBackdropSource(source: VectorBackdropSource | null): void;
|
|
98
|
+
/**
|
|
99
|
+
* Calls `listener` whenever a wet brushstroke has settled, so its final image replaces the one
|
|
100
|
+
* shown while it settled. Re-render the scene then (the controller does: it notifies its
|
|
101
|
+
* subscribers). Returns the unsubscribe function.
|
|
102
|
+
*/
|
|
103
|
+
export declare function onVectorBrushstrokesSettled(listener: () => void): () => void;
|
|
104
|
+
/** How many wet brushstrokes are still settling. */
|
|
105
|
+
export declare function vectorBrushstrokesSettling(): number;
|
|
106
|
+
/** Settles every wet brushstroke still settling, now (slow for many long strokes). Returns how many there were. */
|
|
107
|
+
export declare function settleVectorBrushstrokes(): number;
|
|
62
108
|
/**
|
|
63
|
-
* The images of a brushstroke layer on a path, one per subpath, in the path's own space
|
|
64
|
-
* per path and layer object,
|
|
65
|
-
* paper)
|
|
66
|
-
* own space (paper point =
|
|
109
|
+
* The images of a brushstroke layer on a path, one per subpath, in the path's own space. Cached
|
|
110
|
+
* per path and layer object, by the shared renderer's cache (whose key includes the seed and
|
|
111
|
+
* paper), and for mixer, smudge and blender strokes by what lies beneath them. A layer with a
|
|
112
|
+
* seed renders with the brush's character, the grain laid in the path's own space (paper point =
|
|
113
|
+
* path point). See `VectorBrushstrokeContext` for wet strokes and backdrops.
|
|
67
114
|
*/
|
|
68
|
-
export declare function vectorBrushstrokeImages(node: VectorPathNode, layer: VectorStrokeLayer): VectorBrushstrokeImage[];
|
|
115
|
+
export declare function vectorBrushstrokeImages(node: VectorPathNode, layer: VectorStrokeLayer, context?: VectorBrushstrokeContext): VectorBrushstrokeImage[];
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import { type PaintBrush } from '../paint-editor/brushes.js';
|
|
2
2
|
import type { Brush } from '../brushes/types.js';
|
|
3
|
-
import
|
|
3
|
+
import { type VectorBrushstrokeBackdrop } from './brushstrokeMedia.js';
|
|
4
|
+
import type { VectorBrushstroke, VectorBrushstrokePaint, VectorBrushstrokePaper, VectorDocument, VectorPathNode, VectorStrokeLayer } from './types.js';
|
|
4
5
|
/** Image pixels per document unit brushstrokes are rendered at. */
|
|
5
6
|
export declare const VECTOR_BRUSHSTROKE_SCALE = 2;
|
|
7
|
+
/** Whether a brush's engine paints vector brushstrokes: dry dab brushes, wet paint, the Mixer Brush, smudge and blender. */
|
|
8
|
+
export declare function isVectorBrushstrokeEngine(brush: PaintBrush): boolean;
|
|
6
9
|
/**
|
|
7
10
|
* The brushes a brushstroke can use, as the engine paints them: the presets `VECTOR_PROFILE`
|
|
8
|
-
* admits (dry
|
|
11
|
+
* admits (dry, wet paint, mixer, smudge and blender brushes), then the saved brushes given (facet brushes, e.g. a brush
|
|
9
12
|
* library's) it admits.
|
|
10
13
|
*/
|
|
11
14
|
export declare function vectorBrushstrokeBrushes(user?: readonly Brush[]): PaintBrush[];
|
|
@@ -30,6 +33,15 @@ export declare function vectorBrushstrokeDefaultColour(id: string, user?: readon
|
|
|
30
33
|
* kept in `spec`.
|
|
31
34
|
*/
|
|
32
35
|
export declare function sanitizeVectorBrushstroke(value: unknown, user?: readonly Brush[]): VectorBrushstroke | null;
|
|
36
|
+
/**
|
|
37
|
+
* What is wrong with a brushstroke's gradient `paint` (from an agent or a caller), or null when it
|
|
38
|
+
* is valid. Paint is `{ type: 'color', color }` or `{ type: 'gradient', stops, space, map, softness }`
|
|
39
|
+
* (the paint engine's `PaintSource`, lengths in document units). `velocity` and `time` mappings
|
|
40
|
+
* are refused: a vector path has no timing.
|
|
41
|
+
*/
|
|
42
|
+
export declare function vectorBrushstrokePaintError(paint: unknown): string | null;
|
|
43
|
+
/** Reads brushstroke paint from untrusted data (a stored layer); null when it is not valid paint. */
|
|
44
|
+
export declare function readVectorBrushstrokePaint(value: unknown): VectorBrushstrokePaint | null;
|
|
33
45
|
/** The largest brushstroke seed. */
|
|
34
46
|
export declare const VECTOR_BRUSHSTROKE_MAX_SEED = 2147483647;
|
|
35
47
|
/** Reads paper settings from untrusted data; null when there are none (the default paper). */
|
|
@@ -59,10 +71,45 @@ export interface VectorBrushstrokeImage {
|
|
|
59
71
|
width: number;
|
|
60
72
|
height: number;
|
|
61
73
|
}
|
|
74
|
+
/** Where and how a brushstroke renders. */
|
|
75
|
+
export interface VectorBrushstrokeContext {
|
|
76
|
+
/**
|
|
77
|
+
* The document the path is in. A Mixer Brush, smudge or blender acts on what lies beneath the
|
|
78
|
+
* stroke there; without it, only on the path's own fills and lower strokes.
|
|
79
|
+
*/
|
|
80
|
+
document?: VectorDocument | null;
|
|
81
|
+
/**
|
|
82
|
+
* `async` (default): a wet brushstroke shows the paint as laid until its simulation has settled
|
|
83
|
+
* (`onVectorBrushstrokesSettled` says when). `now`: settle it first (exports, agents).
|
|
84
|
+
*/
|
|
85
|
+
settle?: 'async' | 'now';
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* What lies beneath stroke layer `index` of `node`: the objects it depends on (compared by
|
|
89
|
+
* identity: any change beneath gives a new one), and the backdrop renderer. The scene renderer
|
|
90
|
+
* registers it (`setVectorBackdropSource`), as it is the one that can draw the scene.
|
|
91
|
+
*/
|
|
92
|
+
export type VectorBackdropSource = (document: VectorDocument | null, node: VectorPathNode, index: number, settle: 'async' | 'now') => {
|
|
93
|
+
deps: readonly unknown[];
|
|
94
|
+
backdrop: VectorBrushstrokeBackdrop;
|
|
95
|
+
};
|
|
96
|
+
/** Registers what draws the backdrop of mixer, smudge and blender brushstrokes (done by `render.ts`). */
|
|
97
|
+
export declare function setVectorBackdropSource(source: VectorBackdropSource | null): void;
|
|
98
|
+
/**
|
|
99
|
+
* Calls `listener` whenever a wet brushstroke has settled, so its final image replaces the one
|
|
100
|
+
* shown while it settled. Re-render the scene then (the controller does: it notifies its
|
|
101
|
+
* subscribers). Returns the unsubscribe function.
|
|
102
|
+
*/
|
|
103
|
+
export declare function onVectorBrushstrokesSettled(listener: () => void): () => void;
|
|
104
|
+
/** How many wet brushstrokes are still settling. */
|
|
105
|
+
export declare function vectorBrushstrokesSettling(): number;
|
|
106
|
+
/** Settles every wet brushstroke still settling, now (slow for many long strokes). Returns how many there were. */
|
|
107
|
+
export declare function settleVectorBrushstrokes(): number;
|
|
62
108
|
/**
|
|
63
|
-
* The images of a brushstroke layer on a path, one per subpath, in the path's own space
|
|
64
|
-
* per path and layer object,
|
|
65
|
-
* paper)
|
|
66
|
-
* own space (paper point =
|
|
109
|
+
* The images of a brushstroke layer on a path, one per subpath, in the path's own space. Cached
|
|
110
|
+
* per path and layer object, by the shared renderer's cache (whose key includes the seed and
|
|
111
|
+
* paper), and for mixer, smudge and blender strokes by what lies beneath them. A layer with a
|
|
112
|
+
* seed renders with the brush's character, the grain laid in the path's own space (paper point =
|
|
113
|
+
* path point). See `VectorBrushstrokeContext` for wet strokes and backdrops.
|
|
67
114
|
*/
|
|
68
|
-
export declare function vectorBrushstrokeImages(node: VectorPathNode, layer: VectorStrokeLayer): VectorBrushstrokeImage[];
|
|
115
|
+
export declare function vectorBrushstrokeImages(node: VectorPathNode, layer: VectorStrokeLayer, context?: VectorBrushstrokeContext): VectorBrushstrokeImage[];
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Brushstrokes on vector paths with media that need more than an empty image (#519): wet paint, and
|
|
3
|
+
* the Mixer Brush, smudge and blender, which act on the paint under them. The path stays the data
|
|
4
|
+
* and the pixels a cache, as for dry brushstrokes (`renderBrushstroke`).
|
|
5
|
+
*
|
|
6
|
+
* - **Wet** (`engine: 'wet'`, wet paint brushes: watercolour, gouache, wet oils): the stroke gets its
|
|
7
|
+
* own small `WetWorld` over its bounds, the paint editor's `WetStrokeDriver` lays it, the
|
|
8
|
+
* simulation runs `VECTOR_WET_STEPS` steps (time is counted in steps, never wall time) and the
|
|
9
|
+
* paint is dried where it is. The bytes depend only on the path, brush, colour and seed. The
|
|
10
|
+
* simulation costs about 9 ms per 256 px tile per step on the TypeScript kernel, so
|
|
11
|
+
* `WetBrushstrokeJob` can run it in slices: hosts show the bake of the paint as laid (step 0) at
|
|
12
|
+
* once, and swap in the settled image when it is done.
|
|
13
|
+
* - **Backdrop media** (`mixer`, `smudge`, `blender`): the stroke runs the paint editor's dab-by-dab
|
|
14
|
+
* engine (`PaintDabStroke`) over a local copy of what lies beneath it (the backdrop, rendered by
|
|
15
|
+
* the caller), and keeps only the pixels it changed. Drawn over the content it was made from,
|
|
16
|
+
* that is the stroke's result. A smudge can drag paint over bare paper, but it can't take paint
|
|
17
|
+
* away from the content underneath (the image is drawn over it).
|
|
18
|
+
*
|
|
19
|
+
* Coordinates: like `renderBrushstroke`, inputs are in path units, rendered at `scale` image pixels
|
|
20
|
+
* per unit, and the result's `bounds` place the image back in path units.
|
|
21
|
+
*/
|
|
22
|
+
import { type BrushstrokeImage, type BrushstrokeInput } from '../paint/brushstroke.cjs';
|
|
23
|
+
import { type PaintSource } from '../paint/gradient-paint.cjs';
|
|
24
|
+
import type { PaintBrush } from '../paint-editor/brushes.cjs';
|
|
25
|
+
import { type RasterRect } from '../raster/surface.cjs';
|
|
26
|
+
/** Simulation steps a wet brushstroke runs before it is dried (enough to bleed, bloom and granulate). */
|
|
27
|
+
export declare const VECTOR_WET_STEPS = 120;
|
|
28
|
+
/** Engines a vector brushstroke paints through this module. */
|
|
29
|
+
export type VectorMediaEngine = 'wet' | 'mixer' | 'smudge' | 'blender';
|
|
30
|
+
/** Whether a brush paints through this module (and which way), or null for a dry dab brush. */
|
|
31
|
+
export declare function vectorMediaEngine(brush: Pick<PaintBrush, 'engine' | 'wet'>): VectorMediaEngine | null;
|
|
32
|
+
/** Whether a media engine acts on the paint beneath the stroke (and needs a backdrop). */
|
|
33
|
+
export declare const usesVectorBackdrop: (engine: VectorMediaEngine | null) => boolean;
|
|
34
|
+
export interface VectorMediaOptions {
|
|
35
|
+
/** Image pixels per path unit (default 1). */
|
|
36
|
+
scale?: number;
|
|
37
|
+
/** RGBA 0..255 of the paint (the alpha scales the result). */
|
|
38
|
+
color: readonly [number, number, number, number];
|
|
39
|
+
/** Gradient paint sampled dab by dab (lengths in path units), or null for `color`. */
|
|
40
|
+
paint?: PaintSource | null;
|
|
41
|
+
/** Jitter seed (default 0). */
|
|
42
|
+
seed?: number;
|
|
43
|
+
closed?: boolean;
|
|
44
|
+
/** The largest image to render, in pixels (default 4096²); a bigger stroke renders at a lower scale. */
|
|
45
|
+
maxPixels?: number;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* One wet brushstroke's simulation: the paint is laid when the job is made; `run` advances it
|
|
49
|
+
* (within a time budget, for hosts that slice the work), `image` bakes it as it is now and
|
|
50
|
+
* `settled` once all `VECTOR_WET_STEPS` steps have run. Call `destroy` when done with it.
|
|
51
|
+
*/
|
|
52
|
+
export declare class WetBrushstrokeJob {
|
|
53
|
+
readonly steps: number;
|
|
54
|
+
private readonly world;
|
|
55
|
+
private readonly placed;
|
|
56
|
+
private readonly dabs;
|
|
57
|
+
private done;
|
|
58
|
+
private final;
|
|
59
|
+
constructor(input: BrushstrokeInput, brush: PaintBrush, options: VectorMediaOptions, steps?: number);
|
|
60
|
+
/** Steps run so far. */
|
|
61
|
+
get progress(): number;
|
|
62
|
+
get finished(): boolean;
|
|
63
|
+
/** Runs steps until `budgetMs` has passed (at least one) or all have run. Returns whether it is finished. */
|
|
64
|
+
run(budgetMs?: number): boolean;
|
|
65
|
+
/** The paint dried where it is now (step 0: as laid). */
|
|
66
|
+
image(): BrushstrokeImage;
|
|
67
|
+
/** The settled image: runs the remaining steps now. */
|
|
68
|
+
settled(): BrushstrokeImage;
|
|
69
|
+
destroy(): void;
|
|
70
|
+
private bake;
|
|
71
|
+
}
|
|
72
|
+
/** A wet brushstroke rendered and settled at once (`VECTOR_WET_STEPS` steps; slow for long strokes). */
|
|
73
|
+
export declare function renderWetBrushstroke(input: BrushstrokeInput, brush: PaintBrush, options: VectorMediaOptions): BrushstrokeImage;
|
|
74
|
+
/**
|
|
75
|
+
* What lies beneath a stroke: straight-alpha RGBA of `rect` (image pixels at the stroke's scale,
|
|
76
|
+
* path units × scale), row by row. Called once per render.
|
|
77
|
+
*/
|
|
78
|
+
export type VectorBrushstrokeBackdrop = (rect: RasterRect, scale: number) => Uint8ClampedArray;
|
|
79
|
+
/** The image pixels a backdrop brushstroke reads, in path units × the scale it renders at (for callers that render backdrops). */
|
|
80
|
+
export declare function vectorBackdropRect(input: BrushstrokeInput, brush: PaintBrush, options: VectorMediaOptions): {
|
|
81
|
+
rect: RasterRect;
|
|
82
|
+
scale: number;
|
|
83
|
+
} | null;
|
|
84
|
+
/**
|
|
85
|
+
* A Mixer Brush, smudge or blender stroke over `backdrop`: the pixels it changed (the rest are
|
|
86
|
+
* transparent). Without a backdrop it acts on bare paper: a Mixer Brush still paints from its
|
|
87
|
+
* reservoir; a smudge or blender changes nothing.
|
|
88
|
+
*/
|
|
89
|
+
export declare function renderBackdropBrushstroke(input: BrushstrokeInput, brush: PaintBrush, options: VectorMediaOptions & {
|
|
90
|
+
backdrop?: VectorBrushstrokeBackdrop | null;
|
|
91
|
+
/** A cache for the result, given the backdrop's bytes (the result depends only on them and the stroke). */
|
|
92
|
+
cache?: (backdrop: Uint8ClampedArray) => {
|
|
93
|
+
get(): BrushstrokeImage | undefined;
|
|
94
|
+
set(image: BrushstrokeImage): void;
|
|
95
|
+
};
|
|
96
|
+
}): BrushstrokeImage;
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Brushstrokes on vector paths with media that need more than an empty image (#519): wet paint, and
|
|
3
|
+
* the Mixer Brush, smudge and blender, which act on the paint under them. The path stays the data
|
|
4
|
+
* and the pixels a cache, as for dry brushstrokes (`renderBrushstroke`).
|
|
5
|
+
*
|
|
6
|
+
* - **Wet** (`engine: 'wet'`, wet paint brushes: watercolour, gouache, wet oils): the stroke gets its
|
|
7
|
+
* own small `WetWorld` over its bounds, the paint editor's `WetStrokeDriver` lays it, the
|
|
8
|
+
* simulation runs `VECTOR_WET_STEPS` steps (time is counted in steps, never wall time) and the
|
|
9
|
+
* paint is dried where it is. The bytes depend only on the path, brush, colour and seed. The
|
|
10
|
+
* simulation costs about 9 ms per 256 px tile per step on the TypeScript kernel, so
|
|
11
|
+
* `WetBrushstrokeJob` can run it in slices: hosts show the bake of the paint as laid (step 0) at
|
|
12
|
+
* once, and swap in the settled image when it is done.
|
|
13
|
+
* - **Backdrop media** (`mixer`, `smudge`, `blender`): the stroke runs the paint editor's dab-by-dab
|
|
14
|
+
* engine (`PaintDabStroke`) over a local copy of what lies beneath it (the backdrop, rendered by
|
|
15
|
+
* the caller), and keeps only the pixels it changed. Drawn over the content it was made from,
|
|
16
|
+
* that is the stroke's result. A smudge can drag paint over bare paper, but it can't take paint
|
|
17
|
+
* away from the content underneath (the image is drawn over it).
|
|
18
|
+
*
|
|
19
|
+
* Coordinates: like `renderBrushstroke`, inputs are in path units, rendered at `scale` image pixels
|
|
20
|
+
* per unit, and the result's `bounds` place the image back in path units.
|
|
21
|
+
*/
|
|
22
|
+
import { type BrushstrokeImage, type BrushstrokeInput } from '../paint/brushstroke.js';
|
|
23
|
+
import { type PaintSource } from '../paint/gradient-paint.js';
|
|
24
|
+
import type { PaintBrush } from '../paint-editor/brushes.js';
|
|
25
|
+
import { type RasterRect } from '../raster/surface.js';
|
|
26
|
+
/** Simulation steps a wet brushstroke runs before it is dried (enough to bleed, bloom and granulate). */
|
|
27
|
+
export declare const VECTOR_WET_STEPS = 120;
|
|
28
|
+
/** Engines a vector brushstroke paints through this module. */
|
|
29
|
+
export type VectorMediaEngine = 'wet' | 'mixer' | 'smudge' | 'blender';
|
|
30
|
+
/** Whether a brush paints through this module (and which way), or null for a dry dab brush. */
|
|
31
|
+
export declare function vectorMediaEngine(brush: Pick<PaintBrush, 'engine' | 'wet'>): VectorMediaEngine | null;
|
|
32
|
+
/** Whether a media engine acts on the paint beneath the stroke (and needs a backdrop). */
|
|
33
|
+
export declare const usesVectorBackdrop: (engine: VectorMediaEngine | null) => boolean;
|
|
34
|
+
export interface VectorMediaOptions {
|
|
35
|
+
/** Image pixels per path unit (default 1). */
|
|
36
|
+
scale?: number;
|
|
37
|
+
/** RGBA 0..255 of the paint (the alpha scales the result). */
|
|
38
|
+
color: readonly [number, number, number, number];
|
|
39
|
+
/** Gradient paint sampled dab by dab (lengths in path units), or null for `color`. */
|
|
40
|
+
paint?: PaintSource | null;
|
|
41
|
+
/** Jitter seed (default 0). */
|
|
42
|
+
seed?: number;
|
|
43
|
+
closed?: boolean;
|
|
44
|
+
/** The largest image to render, in pixels (default 4096²); a bigger stroke renders at a lower scale. */
|
|
45
|
+
maxPixels?: number;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* One wet brushstroke's simulation: the paint is laid when the job is made; `run` advances it
|
|
49
|
+
* (within a time budget, for hosts that slice the work), `image` bakes it as it is now and
|
|
50
|
+
* `settled` once all `VECTOR_WET_STEPS` steps have run. Call `destroy` when done with it.
|
|
51
|
+
*/
|
|
52
|
+
export declare class WetBrushstrokeJob {
|
|
53
|
+
readonly steps: number;
|
|
54
|
+
private readonly world;
|
|
55
|
+
private readonly placed;
|
|
56
|
+
private readonly dabs;
|
|
57
|
+
private done;
|
|
58
|
+
private final;
|
|
59
|
+
constructor(input: BrushstrokeInput, brush: PaintBrush, options: VectorMediaOptions, steps?: number);
|
|
60
|
+
/** Steps run so far. */
|
|
61
|
+
get progress(): number;
|
|
62
|
+
get finished(): boolean;
|
|
63
|
+
/** Runs steps until `budgetMs` has passed (at least one) or all have run. Returns whether it is finished. */
|
|
64
|
+
run(budgetMs?: number): boolean;
|
|
65
|
+
/** The paint dried where it is now (step 0: as laid). */
|
|
66
|
+
image(): BrushstrokeImage;
|
|
67
|
+
/** The settled image: runs the remaining steps now. */
|
|
68
|
+
settled(): BrushstrokeImage;
|
|
69
|
+
destroy(): void;
|
|
70
|
+
private bake;
|
|
71
|
+
}
|
|
72
|
+
/** A wet brushstroke rendered and settled at once (`VECTOR_WET_STEPS` steps; slow for long strokes). */
|
|
73
|
+
export declare function renderWetBrushstroke(input: BrushstrokeInput, brush: PaintBrush, options: VectorMediaOptions): BrushstrokeImage;
|
|
74
|
+
/**
|
|
75
|
+
* What lies beneath a stroke: straight-alpha RGBA of `rect` (image pixels at the stroke's scale,
|
|
76
|
+
* path units × scale), row by row. Called once per render.
|
|
77
|
+
*/
|
|
78
|
+
export type VectorBrushstrokeBackdrop = (rect: RasterRect, scale: number) => Uint8ClampedArray;
|
|
79
|
+
/** The image pixels a backdrop brushstroke reads, in path units × the scale it renders at (for callers that render backdrops). */
|
|
80
|
+
export declare function vectorBackdropRect(input: BrushstrokeInput, brush: PaintBrush, options: VectorMediaOptions): {
|
|
81
|
+
rect: RasterRect;
|
|
82
|
+
scale: number;
|
|
83
|
+
} | null;
|
|
84
|
+
/**
|
|
85
|
+
* A Mixer Brush, smudge or blender stroke over `backdrop`: the pixels it changed (the rest are
|
|
86
|
+
* transparent). Without a backdrop it acts on bare paper: a Mixer Brush still paints from its
|
|
87
|
+
* reservoir; a smudge or blender changes nothing.
|
|
88
|
+
*/
|
|
89
|
+
export declare function renderBackdropBrushstroke(input: BrushstrokeInput, brush: PaintBrush, options: VectorMediaOptions & {
|
|
90
|
+
backdrop?: VectorBrushstrokeBackdrop | null;
|
|
91
|
+
/** A cache for the result, given the backdrop's bytes (the result depends only on them and the stroke). */
|
|
92
|
+
cache?: (backdrop: Uint8ClampedArray) => {
|
|
93
|
+
get(): BrushstrokeImage | undefined;
|
|
94
|
+
set(image: BrushstrokeImage): void;
|
|
95
|
+
};
|
|
96
|
+
}): BrushstrokeImage;
|
|
@@ -29,7 +29,7 @@ import { type VectorSnapGuides, type VectorTrackingPoint } from './snap.cjs';
|
|
|
29
29
|
import { type VectorBlendMode, type VectorFilterType, type VectorWarpMesh } from './types.cjs';
|
|
30
30
|
import { EffectsSlot } from '../effects/helpers.cjs';
|
|
31
31
|
import type { EffectProvider, EffectValue } from '../effects/types.cjs';
|
|
32
|
-
import type { Vec2, VectorEffectType, VectorAlign, VectorAnchor, VectorAnchorRef, VectorAnchorType, VectorArrange, VectorAverageAxis, VectorBlendOptions, VectorBooleanOperation, VectorBounds, VectorCornerKind, VectorDefaults, VectorDistribute, VectorDocument, VectorGroupNode, VectorProviderEffect, VectorHandleRef, VectorMask, VectorMaskType, VectorModifiers, VectorNode, VectorPathfinderOperation, VectorPathNode, VectorSegmentRef, VectorStyle, VectorSubpath, VectorTool, VectorTransformHandle, VectorViewport, VectorWidthPoint, VectorCharacterStyle, VectorParagraphStyle, VectorTextAlign, VectorTextFrame, VectorTextNode, VectorMatrix, VectorSymbol, VectorPaint, VectorConstraint, VectorConstraintKind, VectorParameter, VectorBrushstroke, VectorBrushstrokePaper } from './types.cjs';
|
|
32
|
+
import type { Vec2, VectorEffectType, VectorAlign, VectorAnchor, VectorAnchorRef, VectorAnchorType, VectorArrange, VectorAverageAxis, VectorBlendOptions, VectorBooleanOperation, VectorBounds, VectorCornerKind, VectorDefaults, VectorDistribute, VectorDocument, VectorGroupNode, VectorProviderEffect, VectorHandleRef, VectorMask, VectorMaskType, VectorModifiers, VectorNode, VectorPathfinderOperation, VectorPathNode, VectorSegmentRef, VectorStyle, VectorSubpath, VectorTool, VectorTransformHandle, VectorViewport, VectorWidthPoint, VectorCharacterStyle, VectorParagraphStyle, VectorTextAlign, VectorTextFrame, VectorTextNode, VectorMatrix, VectorSymbol, VectorPaint, VectorConstraint, VectorConstraintKind, VectorParameter, VectorBrushstroke, VectorBrushstrokePaint, VectorBrushstrokePaper } from './types.cjs';
|
|
33
33
|
export interface VectorSnapOptions {
|
|
34
34
|
/** Grid size in document units (0 = off, default 0). Points and boxes that snap to no object snap to it. */
|
|
35
35
|
grid: number;
|
|
@@ -113,6 +113,8 @@ export interface VectorEditorOptions {
|
|
|
113
113
|
brushPreview?: BrushPreviewRenderer;
|
|
114
114
|
/** The app's colour scheme, which picks the side of a brush's light / dark default colour (default light). */
|
|
115
115
|
colorScheme?: 'light' | 'dark';
|
|
116
|
+
/** Start with the colour locked: new and rebrushed brushstrokes keep their colour (see `setColorLocked`). */
|
|
117
|
+
colorLocked?: boolean;
|
|
116
118
|
/** History to record into, e.g. one shared with the rest of the app (default: a new one). */
|
|
117
119
|
history?: HistoryManager;
|
|
118
120
|
/** Starting tool (default `select`). */
|
|
@@ -408,6 +410,10 @@ export interface VectorEditorState {
|
|
|
408
410
|
* a side skews along it (Alt: about the centre).
|
|
409
411
|
*/
|
|
410
412
|
freeDistort: boolean;
|
|
413
|
+
/** The colour scheme that picks the side of a brush's light / dark default colour. */
|
|
414
|
+
colorScheme: 'light' | 'dark';
|
|
415
|
+
/** "Keep my colour": brushstrokes don't take a brush's default colour (`setColorLocked`). */
|
|
416
|
+
colorLocked: boolean;
|
|
411
417
|
/** The text being edited on the canvas, or null. Typing goes to `textInput`, keys to `keyDown`. */
|
|
412
418
|
textEdit: VectorTextEditState | null;
|
|
413
419
|
/** Font and alignment new text gets. */
|
|
@@ -799,6 +805,7 @@ export declare class VectorEditorController {
|
|
|
799
805
|
private revision;
|
|
800
806
|
private state;
|
|
801
807
|
private colorScheme;
|
|
808
|
+
private colorLocked;
|
|
802
809
|
constructor(options?: VectorEditorOptions);
|
|
803
810
|
/** Listens to the history (once; again after `destroy`). */
|
|
804
811
|
private connect;
|
|
@@ -1133,7 +1140,7 @@ export declare class VectorEditorController {
|
|
|
1133
1140
|
* Adds a brushstroke to the selected (or given) paths: a stroke layer that paints the path with
|
|
1134
1141
|
* brush `brushId` (a preset, or a saved brush of the brush library: `listBrushstrokeBrushes`), `size` document
|
|
1135
1142
|
* units across (default the brush's own), an optional `pressure` profile along the length, in
|
|
1136
|
-
* `color` (default the brush's own default colour, unless `keepColour
|
|
1143
|
+
* `color` (default the brush's own default colour, unless `keepColour` or the colour is locked; else the top stroke's
|
|
1137
1144
|
* paint, else black) at `opacity`. At the top of the stroke
|
|
1138
1145
|
* stack or at `index`. A `seed` gives it the brush's character (jitter and the grain of `paper`,
|
|
1139
1146
|
* `null` for none); without one it renders plain. One undo step.
|
|
@@ -1150,7 +1157,7 @@ export declare class VectorEditorController {
|
|
|
1150
1157
|
* given) paths: another brush, size, pressure profile (null removes it), colour or opacity, the
|
|
1151
1158
|
* character `seed` (null removes it: plain dabs) or the `paper` (replaced whole: `null` for no
|
|
1152
1159
|
* grain, `{}` for the default paper). Another brush without a `color` takes that brush's default
|
|
1153
|
-
* colour, if it has one, unless `keepColour
|
|
1160
|
+
* colour, if it has one, unless `keepColour` or the colour is locked. Fails, changing nothing, when that layer is not a
|
|
1154
1161
|
* brushstroke. With `merge`, consecutive edits fold into one undo step.
|
|
1155
1162
|
*/
|
|
1156
1163
|
setBrushstroke(index: number, patch: {
|
|
@@ -1162,12 +1169,19 @@ export declare class VectorEditorController {
|
|
|
1162
1169
|
opacity?: number;
|
|
1163
1170
|
seed?: number | null;
|
|
1164
1171
|
paper?: VectorBrushstrokePaper | null;
|
|
1172
|
+
paint?: VectorBrushstrokePaint | null;
|
|
1165
1173
|
}, options?: {
|
|
1166
1174
|
ids?: readonly string[];
|
|
1167
1175
|
merge?: boolean | string;
|
|
1168
1176
|
}): VectorActionResult;
|
|
1169
1177
|
/** Sets the colour scheme that picks the side of a brush's light / dark default colour. Existing strokes keep their colour. */
|
|
1170
1178
|
setColorScheme(scheme: 'light' | 'dark'): void;
|
|
1179
|
+
/**
|
|
1180
|
+
* Locks the brushstroke colour ("Keep my colour"): `addBrushstroke` and `setBrushstroke` with
|
|
1181
|
+
* another brush keep the colour instead of taking the brush's default; an explicit `color` still
|
|
1182
|
+
* applies. Existing strokes are unchanged. Editor state, not an undo step.
|
|
1183
|
+
*/
|
|
1184
|
+
setColorLocked(locked: boolean): void;
|
|
1171
1185
|
/** Removes layer `index` of the fill or stroke stack of the selected (or given) paths. */
|
|
1172
1186
|
removePaintLayer(kind: 'fill' | 'stroke', index: number, options?: {
|
|
1173
1187
|
ids?: readonly string[];
|