@pixerate/editor 0.252.0 → 0.254.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/animation-editor/index.cjs +53 -53
- package/dist/animation-editor/index.js +2 -2
- package/dist/brushes/index.cjs +229 -2014
- package/dist/brushes/index.d.cts +5 -1
- package/dist/brushes/index.d.ts +5 -1
- package/dist/brushes/index.js +236 -2021
- package/dist/brushes/patch.d.cts +23 -0
- package/dist/brushes/patch.d.ts +23 -0
- package/dist/brushes/ui.d.cts +105 -0
- package/dist/brushes/ui.d.ts +105 -0
- package/dist/canvas/index.cjs +7 -7
- package/dist/canvas/index.js +1 -1
- package/dist/{chunk-QKXO6JPP.cjs → chunk-2OQGPEHH.cjs} +14 -14
- package/dist/chunk-34KLWUH4.cjs +2284 -0
- package/dist/{chunk-Z3BRH4JK.cjs → chunk-557BDWKO.cjs} +6 -6
- package/dist/{chunk-ZBCT5MEA.cjs → chunk-CBVNCWJM.cjs} +7 -7
- package/dist/{chunk-226QDRO3.js → chunk-DHQPRKXF.js} +1 -1
- package/dist/{chunk-ABL7IWFZ.js → chunk-GBEF2D4P.js} +1 -1
- package/dist/{chunk-ISIO63Q5.cjs → chunk-L2JTLW6B.cjs} +150 -25
- package/dist/{chunk-JA57JIT5.js → chunk-MV7CYFMJ.js} +133 -8
- package/dist/{chunk-5E2Q7ZHI.js → chunk-MWUZCEFT.js} +1 -1
- package/dist/chunk-QPHNVZHO.js +2284 -0
- package/dist/effects/conformance.d.cts +1 -0
- package/dist/effects/conformance.d.ts +1 -0
- package/dist/effects/fake.d.cts +7 -1
- package/dist/effects/fake.d.ts +7 -1
- package/dist/effects/helpers.d.cts +36 -4
- package/dist/effects/helpers.d.ts +36 -4
- package/dist/effects/index.cjs +331 -102
- package/dist/effects/index.js +238 -9
- package/dist/effects/types.d.cts +84 -5
- package/dist/effects/types.d.ts +84 -5
- package/dist/image-editor/index.cjs +15 -15
- package/dist/image-editor/index.js +1 -1
- package/dist/paint-editor/brush-controls.d.cts +57 -0
- package/dist/paint-editor/brush-controls.d.ts +57 -0
- package/dist/paint-editor/brush-library-tools.d.cts +15 -0
- package/dist/paint-editor/brush-library-tools.d.ts +15 -0
- package/dist/paint-editor/brush-library.d.cts +29 -0
- package/dist/paint-editor/brush-library.d.ts +29 -0
- package/dist/paint-editor/controller.d.cts +76 -17
- package/dist/paint-editor/controller.d.ts +76 -17
- package/dist/paint-editor/index.cjs +543 -858
- package/dist/paint-editor/index.d.cts +1 -0
- package/dist/paint-editor/index.d.ts +1 -0
- package/dist/paint-editor/index.js +329 -644
- package/dist/paint-editor/serialize.d.cts +7 -4
- package/dist/paint-editor/serialize.d.ts +7 -4
- package/dist/paint-editor/sticker-tools.d.cts +1 -1
- package/dist/paint-editor/sticker-tools.d.ts +1 -1
- package/dist/paint-editor/strokes.d.cts +10 -1
- package/dist/paint-editor/strokes.d.ts +10 -1
- package/dist/paint-editor/tools.d.cts +1 -1
- package/dist/paint-editor/tools.d.ts +1 -1
- package/dist/paint-editor/ui.d.cts +3 -51
- package/dist/paint-editor/ui.d.ts +3 -51
- package/dist/paint-editor/wet-tools.d.cts +1 -1
- package/dist/paint-editor/wet-tools.d.ts +1 -1
- package/dist/vector/index.cjs +6 -6
- package/dist/vector/index.js +1 -1
- package/dist/video-editor/index.cjs +344 -344
- package/dist/video-editor/index.js +4 -4
- package/package.json +1 -1
- package/skills/pixerate-brushes/SKILL.md +5 -3
- package/skills/pixerate-paint-editor/SKILL.md +26 -24
- package/skills/pixerate-paint-editor/references/brush-facets.md +30 -0
- package/skills/pixerate-paint-editor/references/gradient-paint.md +1 -1
- package/skills/pixerate-paint-editor/references/light-paint.md +2 -2
- package/skills/pixerate-paint-editor/references/oils.md +1 -1
- package/skills/pixerate-paint-editor/references/particle-brushes.md +8 -5
- package/skills/pixerate-paint-oil-impasto/SKILL.md +1 -1
- package/skills/pixerate-stickers/SKILL.md +1 -1
- package/skills/pixerate-video-editor/SKILL.md +1 -1
package/dist/effects/types.d.ts
CHANGED
|
@@ -11,9 +11,10 @@ import type { FlairSvgAnimation } from '../animation-editor/flair.js';
|
|
|
11
11
|
import type { EffectAuthoringSession, EffectAuthoringSource } from './authoring.js';
|
|
12
12
|
/**
|
|
13
13
|
* How an effect is drawn: a pixel shader, an animated vector picture, motion applied to someone
|
|
14
|
-
* else's vector art,
|
|
14
|
+
* else's vector art, a camera move, which moves a scene's camera and draws no pixels, or a
|
|
15
|
+
* timing effect, which retimes video footage and draws no pixels of its own.
|
|
15
16
|
*/
|
|
16
|
-
export type EffectKind = 'shader' | 'vector' | 'motion' | 'camera';
|
|
17
|
+
export type EffectKind = 'shader' | 'vector' | 'motion' | 'camera' | 'timing';
|
|
17
18
|
/**
|
|
18
19
|
* What an effect is for. An effect may have several uses.
|
|
19
20
|
*
|
|
@@ -26,8 +27,10 @@ export type EffectKind = 'shader' | 'vector' | 'motion' | 'camera';
|
|
|
26
27
|
* - `interactive`: reacts to pointer, scroll or audio input at run time.
|
|
27
28
|
* - `camera`: moves a scene's camera (`cameraView`). Only `camera` effects have it, and they have
|
|
28
29
|
* no other use, so editors that draw pixels never offer them.
|
|
30
|
+
* - `timing`: retimes video footage (`timingSample`): slow motion, a reverse, a freeze, a long
|
|
31
|
+
* exposure. Only `timing` effects have it, and they have no other use.
|
|
29
32
|
*/
|
|
30
|
-
export type EffectUse = 'filter' | 'transition' | 'generator' | 'overlay' | 'title' | 'motion' | 'interactive' | 'camera';
|
|
33
|
+
export type EffectUse = 'filter' | 'transition' | 'generator' | 'overlay' | 'title' | 'motion' | 'interactive' | 'camera' | 'timing';
|
|
31
34
|
export declare const EFFECT_KINDS: readonly EffectKind[];
|
|
32
35
|
export declare const EFFECT_USES: readonly EffectUse[];
|
|
33
36
|
/**
|
|
@@ -66,7 +69,10 @@ export interface EffectSummary {
|
|
|
66
69
|
transparent: boolean;
|
|
67
70
|
/** True when the effect loops; its `duration` is then one cycle. */
|
|
68
71
|
loop: boolean;
|
|
69
|
-
/**
|
|
72
|
+
/**
|
|
73
|
+
* Length of one play (ms). Absent for effects that do not change over time, and for `timing`
|
|
74
|
+
* effects, whose length depends on their footage (`timingLength`).
|
|
75
|
+
*/
|
|
70
76
|
duration?: number;
|
|
71
77
|
/**
|
|
72
78
|
* The control (a number from 0 to 1) setting how opaque the effect's own background is: at 0
|
|
@@ -300,6 +306,59 @@ export interface CameraMoveSample {
|
|
|
300
306
|
/** Ms of the cut, when the move has one. */
|
|
301
307
|
cut?: number;
|
|
302
308
|
}
|
|
309
|
+
/** The frame rate footage is taken to have when a timing request does not say. */
|
|
310
|
+
export declare const TIMING_DEFAULT_FPS = 30;
|
|
311
|
+
/** Most footage frames one retimed frame blends (`TimingSample.frames`). */
|
|
312
|
+
export declare const TIMING_MAX_FRAMES = 128;
|
|
313
|
+
/** Highest footage frame rate a timing request may give. */
|
|
314
|
+
export declare const TIMING_MAX_FPS = 240;
|
|
315
|
+
/**
|
|
316
|
+
* How a retimed frame's footage frames combine (straight-alpha RGBA in, straight alpha out;
|
|
317
|
+
* `blendTimingFrames` is the reference):
|
|
318
|
+
*
|
|
319
|
+
* - `average`: the weighted mean in premultiplied alpha, so transparent edges do not darken.
|
|
320
|
+
* Colour is Σ(c·a·w) / Σ(a·w) and alpha Σ(a·w) / Σw, with `a` the frame's alpha (0–1).
|
|
321
|
+
* - `lighten`: each channel's brightest. Every frame is first scaled by `weight / maxWeight`
|
|
322
|
+
* (`k`): colour by `a·k`, alpha by `k`. The result keeps the largest scaled colour and the
|
|
323
|
+
* largest scaled alpha, and its colour is not divided back up by its alpha, so a faded trail
|
|
324
|
+
* stays dimmer.
|
|
325
|
+
*
|
|
326
|
+
* A single frame is shown as it is either way.
|
|
327
|
+
*/
|
|
328
|
+
export type TimingBlend = 'average' | 'lighten';
|
|
329
|
+
export declare const TIMING_BLENDS: readonly TimingBlend[];
|
|
330
|
+
/** Where a retimed frame is: what `timingSample` takes. */
|
|
331
|
+
export interface TimingSampleRequest {
|
|
332
|
+
/** Ms into the retimed clip (any finite number; frames are held within the footage). */
|
|
333
|
+
time: number;
|
|
334
|
+
/** Length of the footage (ms); > 0. */
|
|
335
|
+
footage: number;
|
|
336
|
+
/** Footage frames per second, > 0 and at most `TIMING_MAX_FPS`. Default `TIMING_DEFAULT_FPS`. */
|
|
337
|
+
fps?: number;
|
|
338
|
+
}
|
|
339
|
+
/** One footage frame of a retimed frame. */
|
|
340
|
+
export interface TimingFrame {
|
|
341
|
+
/** Footage time (ms): the start of a frame, `index * 1000 / fps`. */
|
|
342
|
+
time: number;
|
|
343
|
+
/** Share of the blend, > 0; a sample's weights add up to 1. */
|
|
344
|
+
weight: number;
|
|
345
|
+
}
|
|
346
|
+
/**
|
|
347
|
+
* The footage frames one retimed frame is made of, and how to blend them. The host decodes the
|
|
348
|
+
* frames from its own footage and blends them (`blendTimingFrames` does it for `EffectPixels`).
|
|
349
|
+
*
|
|
350
|
+
* - Each `time` is a footage frame's start, `index * 1000 / fps` with a whole `index` from 0 to
|
|
351
|
+
* the footage's last frame (`ceil(footage * fps / 1000) - 1`): a time before the footage is held
|
|
352
|
+
* on its first frame and one after it on its last.
|
|
353
|
+
* - Times are distinct (frames that land on the same footage frame are merged, their weights
|
|
354
|
+
* added), sorted earliest first, and at most `TIMING_MAX_FRAMES` (spaced evenly when an effect
|
|
355
|
+
* wants more).
|
|
356
|
+
* - Weights are > 0 and add up to 1.
|
|
357
|
+
*/
|
|
358
|
+
export interface TimingSample {
|
|
359
|
+
frames: readonly TimingFrame[];
|
|
360
|
+
blend: TimingBlend;
|
|
361
|
+
}
|
|
303
362
|
/** A box in a sequence's stage units (pixels of `EffectSequence.stage`). */
|
|
304
363
|
export interface EffectRect {
|
|
305
364
|
x: number;
|
|
@@ -516,6 +575,8 @@ export interface EffectProviderCapabilities {
|
|
|
516
575
|
supportsAuthoring?: boolean;
|
|
517
576
|
/** Lists `camera` effects and implements `cameraView` (and maybe `sampleCameraMove`). */
|
|
518
577
|
supportsCamera?: boolean;
|
|
578
|
+
/** Lists `timing` effects and implements `timingLength` and `timingSample`. */
|
|
579
|
+
supportsTiming?: boolean;
|
|
519
580
|
/** Implements `listTemplates`, `describeTemplate` and `instantiateTemplate`. */
|
|
520
581
|
supportsTemplates?: boolean;
|
|
521
582
|
}
|
|
@@ -539,7 +600,8 @@ export interface EffectProvider {
|
|
|
539
600
|
describe(id: string): MaybePromise<EffectDescription>;
|
|
540
601
|
/**
|
|
541
602
|
* A pixel renderer. A `vector` effect may refuse with `EffectUnsupportedError` where it cannot
|
|
542
|
-
* lay out SVG (a Worker);
|
|
603
|
+
* lay out SVG (a Worker); `camera` and `timing` effects draw no pixels of their own and may
|
|
604
|
+
* refuse with one saying so.
|
|
543
605
|
*/
|
|
544
606
|
createRenderer(id: string, options: EffectRendererOptions): MaybePromise<EffectRenderer>;
|
|
545
607
|
/**
|
|
@@ -573,6 +635,23 @@ export interface EffectProvider {
|
|
|
573
635
|
* `sampleEffectCameraMove` falls back to `cameraView` per frame. Errors as for `cameraView`.
|
|
574
636
|
*/
|
|
575
637
|
sampleCameraMove?(id: string, request: CameraMoveSampleRequest): MaybePromise<CameraMoveSample>;
|
|
638
|
+
/**
|
|
639
|
+
* Ms a timing effect's retimed clip lasts for footage `footageMs` long (a speed change stretches
|
|
640
|
+
* or squeezes it, a freeze adds its hold, a boomerang goes there and back); > 0. Present when
|
|
641
|
+
* `capabilities.supportsTiming`. Synchronous and pure. Unknown ids throw `UnknownEffectError`,
|
|
642
|
+
* invalid values `EffectControlError`, and an effect that is not a timing effect (or a footage
|
|
643
|
+
* length that is not a number of ms > 0) `EffectUnsupportedError`, each with a sentence naming
|
|
644
|
+
* what is valid.
|
|
645
|
+
*/
|
|
646
|
+
timingLength?(id: string, footageMs: number, values?: EffectValues): number;
|
|
647
|
+
/**
|
|
648
|
+
* The footage frames retimed frame `request.time` shows and how to blend them (see
|
|
649
|
+
* `TimingSample` for exactly what it holds). Present when `capabilities.supportsTiming`.
|
|
650
|
+
* Synchronous and pure: a frame can be drawn on its own, in any order, and the same id,
|
|
651
|
+
* request and values always give the same sample. Errors as for `timingLength`; a time that is
|
|
652
|
+
* not finite or an `fps` outside 0–`TIMING_MAX_FPS` also throw `EffectUnsupportedError`.
|
|
653
|
+
*/
|
|
654
|
+
timingSample?(id: string, request: TimingSampleRequest, values?: EffectValues): TimingSample;
|
|
576
655
|
/** Sequence templates, best match first. Present when `capabilities.supportsTemplates`. */
|
|
577
656
|
listTemplates?(options?: EffectTemplateSearchOptions): MaybePromise<readonly EffectTemplateSummary[]>;
|
|
578
657
|
/** A template with its slots. Unknown ids throw `UnknownTemplateError`. */
|
|
@@ -765,7 +765,7 @@ var _chunkPY4GWSE2cjs = require('../chunk-PY4GWSE2.cjs');
|
|
|
765
765
|
|
|
766
766
|
|
|
767
767
|
|
|
768
|
-
var
|
|
768
|
+
var _chunkL2JTLW6Bcjs = require('../chunk-L2JTLW6B.cjs');
|
|
769
769
|
require('../chunk-WOFXZ3XU.cjs');
|
|
770
770
|
|
|
771
771
|
|
|
@@ -4857,10 +4857,10 @@ var IMAGE_EFFECT_PLACES = {
|
|
|
4857
4857
|
};
|
|
4858
4858
|
function checkImageEffectPlace(d, place) {
|
|
4859
4859
|
const allowed = IMAGE_EFFECT_PLACES[place];
|
|
4860
|
-
const takesLayer = place === "filter" &&
|
|
4860
|
+
const takesLayer = place === "filter" && _chunkL2JTLW6Bcjs.layerImageInput.call(void 0, d) !== null;
|
|
4861
4861
|
if (d.kind === "motion" || !takesLayer && !d.uses.some((u) => allowed.includes(u))) {
|
|
4862
4862
|
const instead = place === "filter" && d.uses.some((u) => IMAGE_EFFECT_PLACES.layer.includes(u)) ? "; add it as an effect layer with add_effect_layer instead" : place === "layer" && d.uses.includes("filter") ? "; apply it to a layer as a smart filter with apply_effect instead" : "";
|
|
4863
|
-
throw new (0,
|
|
4863
|
+
throw new (0, _chunkL2JTLW6Bcjs.EffectUnsupportedError)(
|
|
4864
4864
|
`"${d.id}" (${d.name}) is a ${d.uses.join(" / ")} effect, and ${place === "filter" ? "a smart filter takes filter effects" : "an effect layer takes generator, overlay or title effects"}${instead}.`,
|
|
4865
4865
|
d.id
|
|
4866
4866
|
);
|
|
@@ -4884,9 +4884,9 @@ function effectPixelsToSurface(p, depth) {
|
|
|
4884
4884
|
const d = new Uint16Array(f.length);
|
|
4885
4885
|
const code = (v) => v > 0 ? v < 1 ? Math.round(v * 65535) : 65535 : 0;
|
|
4886
4886
|
for (let i = 0; i < f.length; i += 4) {
|
|
4887
|
-
d[i] = code(
|
|
4888
|
-
d[i + 1] = code(
|
|
4889
|
-
d[i + 2] = code(
|
|
4887
|
+
d[i] = code(_chunkL2JTLW6Bcjs.linearToSrgb.call(void 0, f[i]));
|
|
4888
|
+
d[i + 1] = code(_chunkL2JTLW6Bcjs.linearToSrgb.call(void 0, f[i + 1]));
|
|
4889
|
+
d[i + 2] = code(_chunkL2JTLW6Bcjs.linearToSrgb.call(void 0, f[i + 2]));
|
|
4890
4890
|
d[i + 3] = code(f[i + 3]);
|
|
4891
4891
|
}
|
|
4892
4892
|
return _chunk2VX55NFMcjs.surfaceFromDeepPixels.call(void 0, { width: p.width, height: p.height, data: d }, 4);
|
|
@@ -4903,7 +4903,7 @@ var ImageEffectsEngine = (_class9 = class {
|
|
|
4903
4903
|
__init49() {this.generation = 0}
|
|
4904
4904
|
|
|
4905
4905
|
constructor(options = {}) {;_class9.prototype.__init44.call(this);_class9.prototype.__init45.call(this);_class9.prototype.__init46.call(this);_class9.prototype.__init47.call(this);_class9.prototype.__init48.call(this);_class9.prototype.__init49.call(this);
|
|
4906
|
-
this.slot = _nullishCoalesce(options.slot, () => ( new (0,
|
|
4906
|
+
this.slot = _nullishCoalesce(options.slot, () => ( new (0, _chunkL2JTLW6Bcjs.EffectsSlot)()));
|
|
4907
4907
|
this.maxEntries = _nullishCoalesce(options.maxEntries, () => ( 24));
|
|
4908
4908
|
this.pool = new (0, _chunkPY4GWSE2cjs.EffectRendererPool)({ maxRenderers: options.maxRenderers });
|
|
4909
4909
|
this.detach = this.slot.subscribe(() => {
|
|
@@ -4951,7 +4951,7 @@ var ImageEffectsEngine = (_class9 = class {
|
|
|
4951
4951
|
/** A filter effect over `source`: its result, or (for an overlay) the effect drawn over it. */
|
|
4952
4952
|
async filter(provider, effect, values, source) {
|
|
4953
4953
|
const d = await this.describe(provider, effect);
|
|
4954
|
-
const input =
|
|
4954
|
+
const input = _chunkL2JTLW6Bcjs.layerImageInput.call(void 0, d);
|
|
4955
4955
|
const out = await this.render(provider, effect, source.width, source.height, 8, { time: 0, values, ...input ? { inputs: { [input]: source } } : {} });
|
|
4956
4956
|
return input ? out.data : over(source.data, out.data);
|
|
4957
4957
|
}
|
|
@@ -4963,7 +4963,7 @@ var ImageEffectsEngine = (_class9 = class {
|
|
|
4963
4963
|
const provider = this.slot.require();
|
|
4964
4964
|
const depth = _nullishCoalesce(size.depth, () => ( 8));
|
|
4965
4965
|
const out = await this.render(provider, effect, size.width, size.height, depth, { time: _nullishCoalesce(size.time, () => ( 0)), values });
|
|
4966
|
-
return out.width === size.width && out.height === size.height ? out :
|
|
4966
|
+
return out.width === size.width && out.height === size.height ? out : _chunkL2JTLW6Bcjs.createEffectPixels.call(void 0, size.width, size.height);
|
|
4967
4967
|
}
|
|
4968
4968
|
/** Why the result for `key` failed, if it did. */
|
|
4969
4969
|
failure(key2) {
|
|
@@ -6225,7 +6225,7 @@ var ImageEditorController = (_class12 = class {
|
|
|
6225
6225
|
__init84() {this.selectionWorker = null}
|
|
6226
6226
|
__init85() {this.documentOpener = null}
|
|
6227
6227
|
/** The effects provider slot (see `setEffectsProvider`). */
|
|
6228
|
-
__init86() {this.effects = new (0,
|
|
6228
|
+
__init86() {this.effects = new (0, _chunkL2JTLW6Bcjs.EffectsSlot)()}
|
|
6229
6229
|
/** Renders provider effects: effect smart filter results and effect layers. */
|
|
6230
6230
|
__init87() {this.effectsEngine = new ImageEffectsEngine({ slot: this.effects })}
|
|
6231
6231
|
__init88() {this.effectRefresh = "none"}
|
|
@@ -6561,7 +6561,7 @@ var ImageEditorController = (_class12 = class {
|
|
|
6561
6561
|
*/
|
|
6562
6562
|
async applyEffectFilter(layerId, effect, options = {}) {
|
|
6563
6563
|
const d = await this.describeEffect(effect, "filter");
|
|
6564
|
-
const values =
|
|
6564
|
+
const values = _chunkL2JTLW6Bcjs.resolveEffectValues.call(void 0, d, options.values);
|
|
6565
6565
|
const found = _chunkDPYAO5U6cjs.findLayer.call(void 0, this.state.layers, layerId);
|
|
6566
6566
|
if (!found) throw new (0, _chunkJFHBXNR2cjs.LayerOperationError)(`There is no layer "${layerId}"; call list_layers for the layer ids.`);
|
|
6567
6567
|
let layers = this.state.layers;
|
|
@@ -6603,7 +6603,7 @@ var ImageEditorController = (_class12 = class {
|
|
|
6603
6603
|
else if (k in d.controls.properties) base[k] = d.defaults[k];
|
|
6604
6604
|
else throw new (0, _chunkJFHBXNR2cjs.LayerOperationError)(`"${k}" is not a control of "${d.id}"; its controls are ${Object.keys(d.controls.properties).join(", ")}.`);
|
|
6605
6605
|
}
|
|
6606
|
-
update.values = { ...base, ...
|
|
6606
|
+
update.values = { ...base, ..._chunkL2JTLW6Bcjs.resolveEffectValues.call(void 0, { ...d, defaults: base }, given) };
|
|
6607
6607
|
}
|
|
6608
6608
|
for (const k of ["enabled", "blend", "opacity", "preserveTransparency"]) if (changes[k] !== void 0) update[k] = changes[k];
|
|
6609
6609
|
let res = _chunkJFHBXNR2cjs.updateSmartFilter.call(void 0, this.state.layers, layerId, filterId, update, this.canvasSize);
|
|
@@ -6630,7 +6630,7 @@ var ImageEditorController = (_class12 = class {
|
|
|
6630
6630
|
const id = options.id && !ids.has(options.id) ? options.id : _chunkJFHBXNR2cjs.newLayerId.call(void 0, );
|
|
6631
6631
|
const placed = _chunkJFHBXNR2cjs.placeLayer.call(void 0, this.state.layers, { type: "pixel", id, name: "", visible: true, opacity: 1, blend: "normal", surface: _chunk474IQG74cjs.emptySurface.call(void 0, 4), offset: { x: 0, y: 0 } }, this.newLayerPlacement(options));
|
|
6632
6632
|
if (options.clipped) requireClipBase(placed, id);
|
|
6633
|
-
const values =
|
|
6633
|
+
const values = _chunkL2JTLW6Bcjs.resolveEffectValues.call(void 0, d, withBackgroundOpacity(d, options.values, hasLayersBelow(placed, id)));
|
|
6634
6634
|
const { width, height } = this.naturalSize;
|
|
6635
6635
|
const pixels = await this.effectsEngine.renderLayer(d.id, values, { width, height, depth: this.bitDepth, time });
|
|
6636
6636
|
const layer = {
|
|
@@ -6675,7 +6675,7 @@ var ImageEditorController = (_class12 = class {
|
|
|
6675
6675
|
else if (k in d.controls.properties) base[k] = d.defaults[k];
|
|
6676
6676
|
else throw new (0, _chunkJFHBXNR2cjs.LayerOperationError)(`"${k}" is not a control of "${d.id}"; its controls are ${Object.keys(d.controls.properties).join(", ")}.`);
|
|
6677
6677
|
}
|
|
6678
|
-
const values = { ...base, ...
|
|
6678
|
+
const values = { ...base, ..._chunkL2JTLW6Bcjs.resolveEffectValues.call(void 0, { ...d, defaults: base }, given) };
|
|
6679
6679
|
const time = changes.time === void 0 ? _nullishCoalesce(g.time, () => ( 0)) : checkEffectTime(changes.time);
|
|
6680
6680
|
const animate = changes.animate === void 0 ? g.animate : changes.animate === null ? void 0 : checkEffectAnimation(changes.animate);
|
|
6681
6681
|
const { width, height } = this.naturalSize;
|
|
@@ -21492,7 +21492,7 @@ function createImageEffectsToolProvider(controller, options = {}) {
|
|
|
21492
21492
|
return { target, effect: _nullishCoalesce(effects.find((e) => e.id === instance), () => ( effects[effects.length - 1])), effects, ...extra };
|
|
21493
21493
|
};
|
|
21494
21494
|
const plainValues = (v) => v;
|
|
21495
|
-
return withImagePlayback(
|
|
21495
|
+
return withImagePlayback(_chunkL2JTLW6Bcjs.createEffectToolProvider.call(void 0, {
|
|
21496
21496
|
id: _nullishCoalesce(options.id, () => ( "image-effects")),
|
|
21497
21497
|
namespace: options.namespace,
|
|
21498
21498
|
title: "Image effects",
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The brush panel's slider tables and the controls each engine adds (`brushEngineControls`),
|
|
3
|
+
* as plain data: paths into the engine brush. No renderer is imported, so the brush editor
|
|
4
|
+
* descriptor (`@pixerate/editor/brushes` `brushEditorSections`) reuses them without bundling the
|
|
5
|
+
* paint engine.
|
|
6
|
+
*/
|
|
7
|
+
import type { PaintBrush, PaintBrushInput } from './brushes.cjs';
|
|
8
|
+
/** One slider of a panel: a path into the brush (or paper) settings. */
|
|
9
|
+
export interface SliderControl {
|
|
10
|
+
path: string;
|
|
11
|
+
label: string;
|
|
12
|
+
min: number;
|
|
13
|
+
max: number;
|
|
14
|
+
step: number;
|
|
15
|
+
}
|
|
16
|
+
export declare const BRUSH_SLIDERS: readonly SliderControl[];
|
|
17
|
+
export interface BrushEngineControls {
|
|
18
|
+
/** Section heading. */
|
|
19
|
+
title: string;
|
|
20
|
+
sliders: readonly SliderControl[];
|
|
21
|
+
/** Where the RGB / pigment choice is stored, or `null` when the engine has none. */
|
|
22
|
+
mixingPath: 'behavior.mixing' | 'blender.mixing' | 'smudge.mixing' | null;
|
|
23
|
+
/** The dirty-brush toggle (`behavior.dirty`). */
|
|
24
|
+
dirty: boolean;
|
|
25
|
+
/** The multi-colour controls (`behavior.multicolor`; the panels show the brush gradient, `brush.paint`, instead). */
|
|
26
|
+
multicolor: boolean;
|
|
27
|
+
/** Load / Clean buttons and a swatch of what the brush carries. */
|
|
28
|
+
load: boolean;
|
|
29
|
+
/** The wet tool menu (`wet.tool`, `WET_TOOLS`) and medium menu (`wet.medium`, `WET_MEDIA`). */
|
|
30
|
+
wetTool?: boolean;
|
|
31
|
+
/** The impasto tool menu (`impasto.tool`, `IMPASTO_TOOLS`). */
|
|
32
|
+
impastoTool?: boolean;
|
|
33
|
+
/**
|
|
34
|
+
* The particle menus: kind and render (`particle.kind`, `particle.render`), ribbon pairs
|
|
35
|
+
* (`particle.ribbonPairs`), and the textured-dot stamps, order and rotation
|
|
36
|
+
* (`particle.dotTextures` up to `MAX_DOT_TEXTURES` of `STAMP_KINDS`, `dotTextureOrder`, `dotRotation`).
|
|
37
|
+
*/
|
|
38
|
+
particleMenus?: boolean;
|
|
39
|
+
/** The sticker picker for the brush's stickers (`sticker.stamps`, refs from the sticker library; `toggleStickerRef`). */
|
|
40
|
+
stickerStamps?: boolean;
|
|
41
|
+
/** Light paint: the "light follows pressure" toggle (`light.pressure`); the Light slider is in `sliders`. */
|
|
42
|
+
light?: boolean;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* The controls the active brush's engine adds to the brush panel (`null` for the eraser):
|
|
46
|
+
* blender strength and blend distance; smudge strength and length; the Mixer Brush's pick-up,
|
|
47
|
+
* mix and load; a dab brush's pick-up and mix ratios. Every engine but the eraser has an RGB /
|
|
48
|
+
* pigment choice; dab and mixer brushes also have the dirty and multi-colour controls. Pick-up
|
|
49
|
+
* and mix always go through `behavior`, which keeps a Mixer Brush's `mixer` block in sync.
|
|
50
|
+
*/
|
|
51
|
+
export declare function brushEngineControls(brush: Pick<PaintBrush, 'engine'> & {
|
|
52
|
+
wet?: PaintBrush['wet'];
|
|
53
|
+
}): BrushEngineControls | null;
|
|
54
|
+
/** The multi-colour sliders for a mode: softness, and dabs per colour in `dab` mode. */
|
|
55
|
+
export declare function multiColorSliders(mode: string): readonly SliderControl[];
|
|
56
|
+
/** A brush change setting `path` (e.g. `smudge.mixing`, `behavior.dirty`) to any value, keeping the rest of its block. */
|
|
57
|
+
export declare function patchBrushValue(brush: PaintBrush, path: string, value: unknown): PaintBrushInput;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The brush panel's slider tables and the controls each engine adds (`brushEngineControls`),
|
|
3
|
+
* as plain data: paths into the engine brush. No renderer is imported, so the brush editor
|
|
4
|
+
* descriptor (`@pixerate/editor/brushes` `brushEditorSections`) reuses them without bundling the
|
|
5
|
+
* paint engine.
|
|
6
|
+
*/
|
|
7
|
+
import type { PaintBrush, PaintBrushInput } from './brushes.js';
|
|
8
|
+
/** One slider of a panel: a path into the brush (or paper) settings. */
|
|
9
|
+
export interface SliderControl {
|
|
10
|
+
path: string;
|
|
11
|
+
label: string;
|
|
12
|
+
min: number;
|
|
13
|
+
max: number;
|
|
14
|
+
step: number;
|
|
15
|
+
}
|
|
16
|
+
export declare const BRUSH_SLIDERS: readonly SliderControl[];
|
|
17
|
+
export interface BrushEngineControls {
|
|
18
|
+
/** Section heading. */
|
|
19
|
+
title: string;
|
|
20
|
+
sliders: readonly SliderControl[];
|
|
21
|
+
/** Where the RGB / pigment choice is stored, or `null` when the engine has none. */
|
|
22
|
+
mixingPath: 'behavior.mixing' | 'blender.mixing' | 'smudge.mixing' | null;
|
|
23
|
+
/** The dirty-brush toggle (`behavior.dirty`). */
|
|
24
|
+
dirty: boolean;
|
|
25
|
+
/** The multi-colour controls (`behavior.multicolor`; the panels show the brush gradient, `brush.paint`, instead). */
|
|
26
|
+
multicolor: boolean;
|
|
27
|
+
/** Load / Clean buttons and a swatch of what the brush carries. */
|
|
28
|
+
load: boolean;
|
|
29
|
+
/** The wet tool menu (`wet.tool`, `WET_TOOLS`) and medium menu (`wet.medium`, `WET_MEDIA`). */
|
|
30
|
+
wetTool?: boolean;
|
|
31
|
+
/** The impasto tool menu (`impasto.tool`, `IMPASTO_TOOLS`). */
|
|
32
|
+
impastoTool?: boolean;
|
|
33
|
+
/**
|
|
34
|
+
* The particle menus: kind and render (`particle.kind`, `particle.render`), ribbon pairs
|
|
35
|
+
* (`particle.ribbonPairs`), and the textured-dot stamps, order and rotation
|
|
36
|
+
* (`particle.dotTextures` up to `MAX_DOT_TEXTURES` of `STAMP_KINDS`, `dotTextureOrder`, `dotRotation`).
|
|
37
|
+
*/
|
|
38
|
+
particleMenus?: boolean;
|
|
39
|
+
/** The sticker picker for the brush's stickers (`sticker.stamps`, refs from the sticker library; `toggleStickerRef`). */
|
|
40
|
+
stickerStamps?: boolean;
|
|
41
|
+
/** Light paint: the "light follows pressure" toggle (`light.pressure`); the Light slider is in `sliders`. */
|
|
42
|
+
light?: boolean;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* The controls the active brush's engine adds to the brush panel (`null` for the eraser):
|
|
46
|
+
* blender strength and blend distance; smudge strength and length; the Mixer Brush's pick-up,
|
|
47
|
+
* mix and load; a dab brush's pick-up and mix ratios. Every engine but the eraser has an RGB /
|
|
48
|
+
* pigment choice; dab and mixer brushes also have the dirty and multi-colour controls. Pick-up
|
|
49
|
+
* and mix always go through `behavior`, which keeps a Mixer Brush's `mixer` block in sync.
|
|
50
|
+
*/
|
|
51
|
+
export declare function brushEngineControls(brush: Pick<PaintBrush, 'engine'> & {
|
|
52
|
+
wet?: PaintBrush['wet'];
|
|
53
|
+
}): BrushEngineControls | null;
|
|
54
|
+
/** The multi-colour sliders for a mode: softness, and dabs per colour in `dab` mode. */
|
|
55
|
+
export declare function multiColorSliders(mode: string): readonly SliderControl[];
|
|
56
|
+
/** A brush change setting `path` (e.g. `smudge.mixing`, `behavior.dirty`) to any value, keeping the rest of its block. */
|
|
57
|
+
export declare function patchBrushValue(brush: PaintBrush, path: string, value: unknown): PaintBrushInput;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The shared brush library's agent tools for a paint editor (`createPaintBrushLibraryToolProvider`),
|
|
3
|
+
* with `preview_brush` painted by this editor.
|
|
4
|
+
*/
|
|
5
|
+
import { type BrushLibraryToolProviderOptions } from '../brushes/tools.cjs';
|
|
6
|
+
import type { AgentToolProvider } from '../tools/registry.cjs';
|
|
7
|
+
import type { PaintEditorController } from './controller.cjs';
|
|
8
|
+
export type PaintBrushLibraryToolProviderOptions = Omit<BrushLibraryToolProviderOptions, 'renderPreview'>;
|
|
9
|
+
/**
|
|
10
|
+
* The brush library tools for a paint editor (`@pixerate/editor/brushes`' provider on the
|
|
11
|
+
* editor's `brushLibrary`, `PAINT_PROFILE`), with `preview_brush` painting the test stroke on the
|
|
12
|
+
* editor's paper. Register it next to `createPaintEditorToolProvider`: it lists, saves, shares and
|
|
13
|
+
* previews the brushes `set_brush` picks.
|
|
14
|
+
*/
|
|
15
|
+
export declare function createPaintBrushLibraryToolProvider(controller: PaintEditorController, options?: PaintBrushLibraryToolProviderOptions): AgentToolProvider;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The shared brush library's agent tools for a paint editor (`createPaintBrushLibraryToolProvider`),
|
|
3
|
+
* with `preview_brush` painted by this editor.
|
|
4
|
+
*/
|
|
5
|
+
import { type BrushLibraryToolProviderOptions } from '../brushes/tools.js';
|
|
6
|
+
import type { AgentToolProvider } from '../tools/registry.js';
|
|
7
|
+
import type { PaintEditorController } from './controller.js';
|
|
8
|
+
export type PaintBrushLibraryToolProviderOptions = Omit<BrushLibraryToolProviderOptions, 'renderPreview'>;
|
|
9
|
+
/**
|
|
10
|
+
* The brush library tools for a paint editor (`@pixerate/editor/brushes`' provider on the
|
|
11
|
+
* editor's `brushLibrary`, `PAINT_PROFILE`), with `preview_brush` painting the test stroke on the
|
|
12
|
+
* editor's paper. Register it next to `createPaintEditorToolProvider`: it lists, saves, shares and
|
|
13
|
+
* previews the brushes `set_brush` picks.
|
|
14
|
+
*/
|
|
15
|
+
export declare function createPaintBrushLibraryToolProvider(controller: PaintEditorController, options?: PaintBrushLibraryToolProviderOptions): AgentToolProvider;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The paint editor's side of the shared brush library (`@pixerate/editor/brushes`): the
|
|
3
|
+
* controller's synchronous copy of the library (each brush as stored, as facets, and as the
|
|
4
|
+
* engine paints it), and facet brushes for engine brushes edited by their settings.
|
|
5
|
+
*/
|
|
6
|
+
import type { StorageAdapter } from '../persistence/storage.cjs';
|
|
7
|
+
import type { Brush } from '../brushes/types.cjs';
|
|
8
|
+
import { type PaintBrush } from './brushes.cjs';
|
|
9
|
+
/** A library brush as the paint editor keeps it: its facets and the engine form it paints with. */
|
|
10
|
+
export interface LibraryBrush {
|
|
11
|
+
spec: Brush;
|
|
12
|
+
brush: PaintBrush;
|
|
13
|
+
builtin: boolean;
|
|
14
|
+
}
|
|
15
|
+
export declare const copyJSON: <T>(v: T) => T;
|
|
16
|
+
export declare function libraryBrush(spec: Brush, builtin: boolean, resolved?: PaintBrush): LibraryBrush;
|
|
17
|
+
/** The built-in presets, paired with the engine brushes they resolve to (`BUILTIN_BRUSHES`, in the same order). */
|
|
18
|
+
export declare function builtinLibraryBrushes(): readonly LibraryBrush[];
|
|
19
|
+
/** Storage that starts with these brushes (a version 1 file) and keeps the rest in memory; none without brushes. */
|
|
20
|
+
export declare function seededStorage(brushes: readonly PaintBrush[]): Pick<StorageAdapter, 'get' | 'set'> | undefined;
|
|
21
|
+
/**
|
|
22
|
+
* The facet brush an engine brush edited by its settings amounts to (`migrateBrushV1`), keeping
|
|
23
|
+
* what the engine doesn't read from the brush it was edited from: description, tags, medium and
|
|
24
|
+
* variety, suggested colours, light spill and glow, and motion hints. Hints that would change how
|
|
25
|
+
* it paints are left out.
|
|
26
|
+
*/
|
|
27
|
+
export declare function brushSpecOf(next: PaintBrush, from: Brush): Brush;
|
|
28
|
+
/** The engine settings `next` changes on `base`, as `normalizePaintBrush(changes, base)` takes them (lists whole). */
|
|
29
|
+
export declare function engineChanges(base: PaintBrush, next: PaintBrush): Omit<Partial<PaintBrush>, 'id'>;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The paint editor's side of the shared brush library (`@pixerate/editor/brushes`): the
|
|
3
|
+
* controller's synchronous copy of the library (each brush as stored, as facets, and as the
|
|
4
|
+
* engine paints it), and facet brushes for engine brushes edited by their settings.
|
|
5
|
+
*/
|
|
6
|
+
import type { StorageAdapter } from '../persistence/storage.js';
|
|
7
|
+
import type { Brush } from '../brushes/types.js';
|
|
8
|
+
import { type PaintBrush } from './brushes.js';
|
|
9
|
+
/** A library brush as the paint editor keeps it: its facets and the engine form it paints with. */
|
|
10
|
+
export interface LibraryBrush {
|
|
11
|
+
spec: Brush;
|
|
12
|
+
brush: PaintBrush;
|
|
13
|
+
builtin: boolean;
|
|
14
|
+
}
|
|
15
|
+
export declare const copyJSON: <T>(v: T) => T;
|
|
16
|
+
export declare function libraryBrush(spec: Brush, builtin: boolean, resolved?: PaintBrush): LibraryBrush;
|
|
17
|
+
/** The built-in presets, paired with the engine brushes they resolve to (`BUILTIN_BRUSHES`, in the same order). */
|
|
18
|
+
export declare function builtinLibraryBrushes(): readonly LibraryBrush[];
|
|
19
|
+
/** Storage that starts with these brushes (a version 1 file) and keeps the rest in memory; none without brushes. */
|
|
20
|
+
export declare function seededStorage(brushes: readonly PaintBrush[]): Pick<StorageAdapter, 'get' | 'set'> | undefined;
|
|
21
|
+
/**
|
|
22
|
+
* The facet brush an engine brush edited by its settings amounts to (`migrateBrushV1`), keeping
|
|
23
|
+
* what the engine doesn't read from the brush it was edited from: description, tags, medium and
|
|
24
|
+
* variety, suggested colours, light spill and glow, and motion hints. Hints that would change how
|
|
25
|
+
* it paints are left out.
|
|
26
|
+
*/
|
|
27
|
+
export declare function brushSpecOf(next: PaintBrush, from: Brush): Brush;
|
|
28
|
+
/** The engine settings `next` changes on `base`, as `normalizePaintBrush(changes, base)` takes them (lists whole). */
|
|
29
|
+
export declare function engineChanges(base: PaintBrush, next: PaintBrush): Omit<Partial<PaintBrush>, 'id'>;
|
|
@@ -18,7 +18,10 @@ import { type LayerLocks } from '../raster/layers.cjs';
|
|
|
18
18
|
import { type LayerEffect, type LayerEffectKind, type LayerEffects } from '../raster/effects/model.cjs';
|
|
19
19
|
import type { EffectPattern } from '../raster/effects/patterns.cjs';
|
|
20
20
|
import { TiledSurface, type RasterRect, type Tile } from '../raster/surface.cjs';
|
|
21
|
-
import { type PaintBrushInput, type
|
|
21
|
+
import { type PaintBrushInput, type PaintBrush } from './brushes.cjs';
|
|
22
|
+
import { type BrushLibrary, type BrushLibraryFile } from '../brushes/library.cjs';
|
|
23
|
+
import { type BrushPatch, type UnsettableBrushFacet } from '../brushes/patch.cjs';
|
|
24
|
+
import type { Brush } from '../brushes/types.cjs';
|
|
22
25
|
import { type PaintCanvas, type PaintDocumentState, type PaintLayer } from './document.cjs';
|
|
23
26
|
import { type ImageEncoder, type ImageFormat } from './encode.cjs';
|
|
24
27
|
import type { PenPoint } from './engine.cjs';
|
|
@@ -57,7 +60,12 @@ export interface PaintEditorOptions extends Partial<PaintCanvas> {
|
|
|
57
60
|
paper?: Partial<PaperSettings> | string;
|
|
58
61
|
/** Active brush id (default `pencil-6b`). */
|
|
59
62
|
brushId?: string;
|
|
60
|
-
/**
|
|
63
|
+
/**
|
|
64
|
+
* The brush library: built-in presets plus saved brushes, on any storage. Pass one library to
|
|
65
|
+
* every editor so a brush saved in one shows in all of them (default: a library in memory).
|
|
66
|
+
*/
|
|
67
|
+
brushLibrary?: BrushLibrary;
|
|
68
|
+
/** Saved user brushes to start the default library with (or to add to `brushLibrary`). */
|
|
61
69
|
brushes?: readonly PaintBrush[];
|
|
62
70
|
/** Paint colour #rrggbb (default #1f2937). */
|
|
63
71
|
color?: string;
|
|
@@ -296,8 +304,17 @@ export declare class PaintEditorController {
|
|
|
296
304
|
private undoStack;
|
|
297
305
|
private redoStack;
|
|
298
306
|
private readonly historyLimit;
|
|
307
|
+
/** The brush library this editor lists, saves into and paints from (shared with other editors when the host passes one). */
|
|
308
|
+
readonly brushLibrary: BrushLibrary;
|
|
299
309
|
private brush;
|
|
300
|
-
|
|
310
|
+
/** The active brush as facets (what `getBrushSpec` and saving use). */
|
|
311
|
+
private brushSpec;
|
|
312
|
+
/** The library's brushes as last read, each as stored (facets) and as the engine paints it. */
|
|
313
|
+
private brushEntries;
|
|
314
|
+
/** The brush id asked for at construction, while the library had not listed it yet. */
|
|
315
|
+
private pendingBrushId;
|
|
316
|
+
private brushesLoaded;
|
|
317
|
+
private readonly unsubscribeLibrary;
|
|
301
318
|
/** The library list in the state (the same array until the user brushes change, so thumbnails are not redrawn). */
|
|
302
319
|
private library;
|
|
303
320
|
private color;
|
|
@@ -673,39 +690,81 @@ export declare class PaintEditorController {
|
|
|
673
690
|
source: PaintEffectSource;
|
|
674
691
|
amount: number;
|
|
675
692
|
}>;
|
|
693
|
+
private findEntry;
|
|
676
694
|
private findBrush;
|
|
677
|
-
|
|
678
|
-
getBrush(id: string): PaintBrush | undefined;
|
|
695
|
+
private requireEntry;
|
|
679
696
|
private requireBrush;
|
|
697
|
+
/** Reads the library again (after a change anywhere it is shared). */
|
|
698
|
+
private refreshBrushes;
|
|
699
|
+
/** Resolves once the brush library has been read (user brushes kept in storage are listed from then on). */
|
|
700
|
+
whenBrushesLoaded(): Promise<void>;
|
|
701
|
+
/** A library brush by id, as the engine paints it (`undefined` when unknown). */
|
|
702
|
+
getBrush(id: string): PaintBrush | undefined;
|
|
703
|
+
/** A library brush by id as stored (its facets), or without an id the active brush as edited. */
|
|
704
|
+
getBrushSpec(id?: string): Brush | undefined;
|
|
680
705
|
/**
|
|
681
|
-
* Chooses the active brush (`id`: a library brush) and / or changes its settings. Editor
|
|
682
|
-
* not an undo step. Returns the active brush.
|
|
706
|
+
* Chooses the active brush (`id`: a library brush) and / or changes its engine settings. Editor
|
|
707
|
+
* state, not an undo step. Returns the active brush. `setBrushFacets` changes it by facets.
|
|
683
708
|
*/
|
|
684
709
|
setBrush(update?: {
|
|
685
710
|
id?: string;
|
|
686
711
|
} & Omit<PaintBrushInput, 'id'>): PaintBrush;
|
|
687
|
-
/**
|
|
712
|
+
/**
|
|
713
|
+
* Chooses the active brush (`id`: a library brush) and / or lays a facet patch over it (objects
|
|
714
|
+
* merge, lists and values replace, as the library's `update_brush` does). Editor state, not an
|
|
715
|
+
* undo step. `unset` removes whole facets first. Returns the active brush as the engine paints
|
|
716
|
+
* it, its facets, and what was clamped or dropped.
|
|
717
|
+
*/
|
|
718
|
+
setBrushFacets(patch?: BrushPatch, id?: string, unset?: readonly UnsettableBrushFacet[]): {
|
|
719
|
+
brush: PaintBrush;
|
|
720
|
+
spec: Brush;
|
|
721
|
+
problems: string[];
|
|
722
|
+
};
|
|
723
|
+
/**
|
|
724
|
+
* Makes `spec` (a whole facet brush, as the brush editor edits it) the active brush, keeping the
|
|
725
|
+
* active brush's id. Editor state, not an undo step. Returns it as the engine paints it, its
|
|
726
|
+
* facets, and what was clamped or dropped.
|
|
727
|
+
*/
|
|
728
|
+
setBrushSpec(spec: Brush): {
|
|
729
|
+
brush: PaintBrush;
|
|
730
|
+
spec: Brush;
|
|
731
|
+
problems: string[];
|
|
732
|
+
};
|
|
733
|
+
/**
|
|
734
|
+
* The engine settings a facet patch changes on a brush (`from`: a library brush id or a brush as
|
|
735
|
+
* painted; default the active brush), as `paintStroke`'s and `updateStroke`'s `brush` take them.
|
|
736
|
+
*/
|
|
737
|
+
brushChanges(patch: BrushPatch, from?: string | PaintBrush): Omit<PaintBrushInput, 'id'>;
|
|
738
|
+
private readBrushPatch;
|
|
739
|
+
private useBrush;
|
|
740
|
+
/**
|
|
741
|
+
* Saves the active brush (or `brush`, engine settings on top of it) into the brush library under
|
|
742
|
+
* `id` / `name`, replacing a user brush with that id. Built-in ids are refused. Resolves with the
|
|
743
|
+
* saved brush.
|
|
744
|
+
*/
|
|
688
745
|
saveBrush(options: {
|
|
689
746
|
id: string;
|
|
690
747
|
name?: string;
|
|
691
748
|
brush?: PaintBrushInput;
|
|
692
|
-
}): PaintBrush
|
|
693
|
-
|
|
694
|
-
|
|
749
|
+
}): Promise<PaintBrush>;
|
|
750
|
+
/** Removes a user brush from the brush library (built-in brushes can't be removed). */
|
|
751
|
+
removeBrush(id: string): Promise<void>;
|
|
752
|
+
/** The user's saved brushes, as the engine paints them. */
|
|
695
753
|
getUserBrushes(): PaintBrush[];
|
|
696
|
-
/** A brush library file of the given brushes (default: the user's brushes). */
|
|
697
|
-
exportBrushes(ids?: readonly string[]): BrushLibraryFile
|
|
754
|
+
/** A brush library file (version 2) of the given brushes (default: the user's brushes). */
|
|
755
|
+
exportBrushes(ids?: readonly string[]): Promise<BrushLibraryFile>;
|
|
698
756
|
/**
|
|
699
|
-
* Adds the brushes of a library file to the
|
|
700
|
-
* unless `replace` (built-in ids are always skipped).
|
|
757
|
+
* Adds the brushes of a library file (version 1 or 2) to the brush library. A brush whose id is
|
|
758
|
+
* taken is skipped unless `replace` (built-in ids are always skipped). Resolves with the ids
|
|
759
|
+
* added or replaced, the ids skipped and the problems.
|
|
701
760
|
*/
|
|
702
761
|
importBrushes(file: unknown, options?: {
|
|
703
762
|
replace?: boolean;
|
|
704
|
-
}): {
|
|
763
|
+
}): Promise<{
|
|
705
764
|
added: string[];
|
|
706
765
|
skipped: string[];
|
|
707
766
|
problems: string[];
|
|
708
|
-
}
|
|
767
|
+
}>;
|
|
709
768
|
/** A thumbnail of a brush: a test stroke on the current paper. */
|
|
710
769
|
brushThumbnail(id: string, options?: {
|
|
711
770
|
width?: number;
|