@pixerate/editor 0.123.0 → 0.124.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.cjs.map +1 -1
- package/dist/agent/index.js +3 -3
- package/dist/animation/index.d.cts +2 -1
- package/dist/animation/index.d.ts +2 -1
- package/dist/animation-editor/index.cjs.map +1 -1
- package/dist/animation-editor/index.d.cts +11 -733
- package/dist/animation-editor/index.d.ts +11 -733
- package/dist/animation-editor/index.js.map +1 -1
- package/dist/{bezier-C-W9LFyD.d.cts → bezier-CBzKKDjl.d.cts} +2 -66
- package/dist/{bezier-DQZE5dxA.d.ts → bezier-CWv6LQal.d.ts} +2 -66
- package/dist/{brushstrokeRender-M4S65DHT.cjs → brushstrokeRender-EELTV7U4.cjs} +8 -8
- package/dist/{brushstrokeRender-M4S65DHT.cjs.map → brushstrokeRender-EELTV7U4.cjs.map} +1 -1
- package/dist/{brushstrokeRender-CDDRSV2W.js → brushstrokeRender-LRV7GXGD.js} +3 -3
- package/dist/canvas/index.cjs +6 -6
- package/dist/canvas/index.d.cts +10 -6
- package/dist/canvas/index.d.ts +10 -6
- package/dist/canvas/index.js +1 -1
- package/dist/{chunk-TVXFHE4T.js → chunk-64PYNEHJ.js} +45 -12
- package/dist/chunk-64PYNEHJ.js.map +1 -0
- package/dist/{chunk-2IW3BWRT.cjs → chunk-7LQO6OHH.cjs} +2 -2
- package/dist/{chunk-2IW3BWRT.cjs.map → chunk-7LQO6OHH.cjs.map} +1 -1
- package/dist/chunk-IOXKXQNW.cjs +178 -0
- package/dist/chunk-IOXKXQNW.cjs.map +1 -0
- package/dist/{chunk-SMJGE7HM.cjs → chunk-IWSMDQE5.cjs} +523 -39
- package/dist/chunk-IWSMDQE5.cjs.map +1 -0
- package/dist/{chunk-KMBC4HNV.js → chunk-J67OLQUQ.js} +2 -2
- package/dist/{chunk-6CJRDJCU.cjs → chunk-PEHYWUVV.cjs} +176 -111
- package/dist/chunk-PEHYWUVV.cjs.map +1 -0
- package/dist/chunk-TGKT5QAB.js +178 -0
- package/dist/chunk-TGKT5QAB.js.map +1 -0
- package/dist/chunk-TK53AB4Q.cjs +215 -0
- package/dist/chunk-TK53AB4Q.cjs.map +1 -0
- package/dist/chunk-VFAKN46Z.js +215 -0
- package/dist/chunk-VFAKN46Z.js.map +1 -0
- package/dist/{chunk-6ZBR4J62.js → chunk-WATAQ2Z2.js} +79 -14
- package/dist/chunk-WATAQ2Z2.js.map +1 -0
- package/dist/{chunk-IPFRWILU.js → chunk-YVVSTD3B.js} +499 -15
- package/dist/chunk-YVVSTD3B.js.map +1 -0
- package/dist/{chunk-RLRUAPBJ.cjs → chunk-Z6OUCS2K.cjs} +47 -14
- package/dist/chunk-Z6OUCS2K.cjs.map +1 -0
- package/dist/effects/index.cjs +588 -0
- package/dist/effects/index.cjs.map +1 -0
- package/dist/effects/index.d.cts +131 -0
- package/dist/effects/index.d.ts +131 -0
- package/dist/effects/index.js +588 -0
- package/dist/effects/index.js.map +1 -0
- package/dist/evaluate-2S3vSPLy.d.cts +55 -0
- package/dist/evaluate-CfmT6xA3.d.ts +55 -0
- package/dist/{export-DfjzHAJI.d.ts → export-8RnlngAv.d.ts} +534 -128
- package/dist/{export-Dv0-ZVrV.d.ts → export-BzSRXyH6.d.cts} +390 -5
- package/dist/{export-Dv0-ZVrV.d.cts → export-DM3h7QEI.d.ts} +390 -5
- package/dist/{export-lU5qSpHE.d.cts → export-TqXMOlDt.d.cts} +534 -128
- package/dist/flair-CxB3fOcU.d.ts +739 -0
- package/dist/flair-D7BxyBwL.d.cts +739 -0
- package/dist/gradient-KDfCvhuj.d.cts +66 -0
- package/dist/gradient-KDfCvhuj.d.ts +66 -0
- package/dist/helpers-777mC6dc.d.cts +253 -0
- package/dist/helpers-DX2_7L5t.d.ts +253 -0
- package/dist/image-editor/index.cjs +1307 -805
- package/dist/image-editor/index.cjs.map +1 -1
- package/dist/image-editor/index.d.cts +39 -182
- package/dist/image-editor/index.d.ts +39 -182
- package/dist/image-editor/index.js +508 -6
- package/dist/image-editor/index.js.map +1 -1
- package/dist/{opentype-p8Xxf-Wh.d.ts → opentype-14PDI5D2.d.ts} +1 -1
- package/dist/{opentype-BV7wWatY.d.cts → opentype-D80AVmMu.d.cts} +1 -1
- package/dist/paint/index.cjs +2 -2
- package/dist/paint/index.d.cts +32 -10
- package/dist/paint/index.d.ts +32 -10
- package/dist/paint/index.js +1 -1
- package/dist/paint-editor/index.cjs +10 -8
- package/dist/paint-editor/index.cjs.map +1 -1
- package/dist/paint-editor/index.d.cts +11 -5
- package/dist/paint-editor/index.d.ts +11 -5
- package/dist/paint-editor/index.js +4 -2
- package/dist/paint-editor/index.js.map +1 -1
- package/dist/parameters/index.d.cts +4 -184
- package/dist/parameters/index.d.ts +4 -184
- package/dist/{pathfinder-IxcyhO00.d.cts → pathfinder-C0UoFypx.d.cts} +1 -1
- package/dist/{pathfinder-StVChJOT.d.ts → pathfinder-DjISxkUv.d.ts} +1 -1
- package/dist/{player-DrcP2xqj.d.cts → player-CBtEoU2x.d.cts} +2 -54
- package/dist/{player-DV4Jujdq.d.ts → player-cGLrngL_.d.ts} +2 -54
- package/dist/{svg-BtND4TJu.d.cts → svg-BDrjRnL1.d.cts} +3 -2
- package/dist/{svg-BfVXG7hL.d.ts → svg-C-vZ3q50.d.ts} +3 -2
- package/dist/tools/index.cjs +2 -2
- package/dist/tools/index.cjs.map +1 -1
- package/dist/tools/index.js +3 -3
- package/dist/tools-C7JtLYzo.d.ts +126 -0
- package/dist/tools-DHKGHiSK.d.cts +126 -0
- package/dist/{types-fuXEme9j.d.ts → types-BcX7sCsn.d.ts} +2 -2
- package/dist/{types-DJF3Ghn5.d.cts → types-CznabG7m.d.cts} +2 -2
- package/dist/types-D3-J3TFf.d.ts +185 -0
- package/dist/types-DMcf_z4J.d.cts +185 -0
- package/dist/vector/index.d.cts +10 -10
- package/dist/vector/index.d.ts +10 -10
- package/dist/video-editor/index.cjs +749 -277
- package/dist/video-editor/index.cjs.map +1 -1
- package/dist/video-editor/index.d.cts +265 -138
- package/dist/video-editor/index.d.ts +265 -138
- package/dist/video-editor/index.js +490 -18
- package/dist/video-editor/index.js.map +1 -1
- package/dist/{warp-BjmAgQwq.d.cts → warp-CH9MM435.d.cts} +1 -1
- package/dist/{warp-Ca5fedzS.d.ts → warp-k_PdtXOG.d.ts} +1 -1
- package/package.json +6 -1
- package/skills/pixerate-image-editor/SKILL.md +7 -0
- package/skills/pixerate-image-editor/references/provider-effects.md +35 -0
- package/skills/pixerate-video-editor/SKILL.md +39 -1
- package/dist/chunk-6CJRDJCU.cjs.map +0 -1
- package/dist/chunk-6ZBR4J62.js.map +0 -1
- package/dist/chunk-IPFRWILU.js.map +0 -1
- package/dist/chunk-RLRUAPBJ.cjs.map +0 -1
- package/dist/chunk-SMJGE7HM.cjs.map +0 -1
- package/dist/chunk-TVXFHE4T.js.map +0 -1
- /package/dist/{brushstrokeRender-CDDRSV2W.js.map → brushstrokeRender-LRV7GXGD.js.map} +0 -0
- /package/dist/{chunk-KMBC4HNV.js.map → chunk-J67OLQUQ.js.map} +0 -0
|
@@ -51,6 +51,7 @@ import {
|
|
|
51
51
|
VIBRANCE_GREY,
|
|
52
52
|
XYZ_D50_TO_SRGB,
|
|
53
53
|
activeAdjustmentLayers,
|
|
54
|
+
activeEffectSmartFilters,
|
|
54
55
|
activeSmartFilters,
|
|
55
56
|
addLayerEffect,
|
|
56
57
|
addLayerFilter,
|
|
@@ -180,6 +181,7 @@ import {
|
|
|
180
181
|
isRenderedLayer,
|
|
181
182
|
isSelectionEmpty,
|
|
182
183
|
isSelectionMode,
|
|
184
|
+
isSmartSurfaceIncomplete,
|
|
183
185
|
isSpatialFilter,
|
|
184
186
|
isTransformAnchor,
|
|
185
187
|
isValidWarp,
|
|
@@ -197,7 +199,7 @@ import {
|
|
|
197
199
|
legacyView,
|
|
198
200
|
levelsValue,
|
|
199
201
|
linearSrgbToXy,
|
|
200
|
-
linearToSrgb,
|
|
202
|
+
linearToSrgb as linearToSrgb2,
|
|
201
203
|
liveComponent,
|
|
202
204
|
liveShapeSubpaths,
|
|
203
205
|
mapCropHandle,
|
|
@@ -360,13 +362,24 @@ import {
|
|
|
360
362
|
writePsd,
|
|
361
363
|
xyToTempTint,
|
|
362
364
|
xyToUv
|
|
363
|
-
} from "../chunk-
|
|
365
|
+
} from "../chunk-WATAQ2Z2.js";
|
|
364
366
|
import {
|
|
365
367
|
parseCssColor
|
|
366
368
|
} from "../chunk-CTTF4KYV.js";
|
|
367
369
|
import {
|
|
368
370
|
loadDocument
|
|
369
371
|
} from "../chunk-U5HZSFCS.js";
|
|
372
|
+
import {
|
|
373
|
+
createEffectToolProvider
|
|
374
|
+
} from "../chunk-VFAKN46Z.js";
|
|
375
|
+
import {
|
|
376
|
+
EffectUnsupportedError,
|
|
377
|
+
EffectsSlot,
|
|
378
|
+
createEffectPixels,
|
|
379
|
+
linearToSrgb,
|
|
380
|
+
resolveEffectValues
|
|
381
|
+
} from "../chunk-TGKT5QAB.js";
|
|
382
|
+
import "../chunk-4FX5ACVG.js";
|
|
370
383
|
import {
|
|
371
384
|
createCaptureTool
|
|
372
385
|
} from "../chunk-REOJJIY3.js";
|
|
@@ -475,7 +488,7 @@ import {
|
|
|
475
488
|
pathStrokeCoverage,
|
|
476
489
|
pathStrokeDabs,
|
|
477
490
|
renderBrushstroke
|
|
478
|
-
} from "../chunk-
|
|
491
|
+
} from "../chunk-64PYNEHJ.js";
|
|
479
492
|
import {
|
|
480
493
|
BODY_SCATTER,
|
|
481
494
|
DEFAULT_FLOW,
|
|
@@ -2772,6 +2785,182 @@ function refineSelection(pixels, mask, params = {}) {
|
|
|
2772
2785
|
return createSelectionMask(mask.width, mask.height, out);
|
|
2773
2786
|
}
|
|
2774
2787
|
|
|
2788
|
+
// src/image-editor/effects.ts
|
|
2789
|
+
var IMAGE_EFFECT_PLACES = {
|
|
2790
|
+
/** A smart filter: the effect changes the pixels below it. */
|
|
2791
|
+
filter: ["filter"],
|
|
2792
|
+
/** An effect layer: drawn at the document's size. */
|
|
2793
|
+
layer: ["generator", "overlay", "title"]
|
|
2794
|
+
};
|
|
2795
|
+
function checkImageEffectPlace(d, place) {
|
|
2796
|
+
const allowed = IMAGE_EFFECT_PLACES[place];
|
|
2797
|
+
if (d.kind === "motion" || !d.uses.some((u) => allowed.includes(u))) {
|
|
2798
|
+
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" : "";
|
|
2799
|
+
throw new EffectUnsupportedError(
|
|
2800
|
+
`"${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}.`,
|
|
2801
|
+
d.id
|
|
2802
|
+
);
|
|
2803
|
+
}
|
|
2804
|
+
}
|
|
2805
|
+
function over(base, top) {
|
|
2806
|
+
const out = new Uint8ClampedArray(base.length);
|
|
2807
|
+
for (let i = 0; i < out.length; i += 4) {
|
|
2808
|
+
const ta = top[i + 3] / 255;
|
|
2809
|
+
const ba = base[i + 3] / 255;
|
|
2810
|
+
const a = ta + ba * (1 - ta);
|
|
2811
|
+
out[i + 3] = Math.round(a * 255);
|
|
2812
|
+
if (a > 0) for (let c = 0; c < 3; c++) out[i + c] = (top[i + c] * ta + base[i + c] * ba * (1 - ta)) / a;
|
|
2813
|
+
}
|
|
2814
|
+
return out;
|
|
2815
|
+
}
|
|
2816
|
+
function effectPixelsToSurface(p, depth) {
|
|
2817
|
+
if (depth === 8 || !p.float) return surfaceFromPixels({ width: p.width, height: p.height, data: p.data }, 4);
|
|
2818
|
+
if (depth === 32) return surfaceFromDeepPixels({ width: p.width, height: p.height, data: p.float }, 4);
|
|
2819
|
+
const f = p.float;
|
|
2820
|
+
const d = new Uint16Array(f.length);
|
|
2821
|
+
const code = (v) => v > 0 ? v < 1 ? Math.round(v * 65535) : 65535 : 0;
|
|
2822
|
+
for (let i = 0; i < f.length; i += 4) {
|
|
2823
|
+
d[i] = code(linearToSrgb(f[i]));
|
|
2824
|
+
d[i + 1] = code(linearToSrgb(f[i + 1]));
|
|
2825
|
+
d[i + 2] = code(linearToSrgb(f[i + 2]));
|
|
2826
|
+
d[i + 3] = code(f[i + 3]);
|
|
2827
|
+
}
|
|
2828
|
+
return surfaceFromDeepPixels({ width: p.width, height: p.height, data: d }, 4);
|
|
2829
|
+
}
|
|
2830
|
+
var ImageEffectsEngine = class {
|
|
2831
|
+
slot;
|
|
2832
|
+
cache = /* @__PURE__ */ new Map();
|
|
2833
|
+
pending = /* @__PURE__ */ new Map();
|
|
2834
|
+
failures = /* @__PURE__ */ new Map();
|
|
2835
|
+
renderers = /* @__PURE__ */ new Map();
|
|
2836
|
+
descriptions = /* @__PURE__ */ new Map();
|
|
2837
|
+
listeners = /* @__PURE__ */ new Set();
|
|
2838
|
+
maxEntries;
|
|
2839
|
+
generation = 0;
|
|
2840
|
+
detach;
|
|
2841
|
+
constructor(options = {}) {
|
|
2842
|
+
this.slot = options.slot ?? new EffectsSlot();
|
|
2843
|
+
this.maxEntries = options.maxEntries ?? 24;
|
|
2844
|
+
this.detach = this.slot.subscribe(() => {
|
|
2845
|
+
this.reset();
|
|
2846
|
+
this.notify();
|
|
2847
|
+
});
|
|
2848
|
+
}
|
|
2849
|
+
get provider() {
|
|
2850
|
+
return this.slot.provider;
|
|
2851
|
+
}
|
|
2852
|
+
/** See `SmartEffectResolver.token`: bumped when the provider changes. */
|
|
2853
|
+
get token() {
|
|
2854
|
+
return this.generation;
|
|
2855
|
+
}
|
|
2856
|
+
/** True while a render is under way. */
|
|
2857
|
+
get busy() {
|
|
2858
|
+
return this.pending.size > 0;
|
|
2859
|
+
}
|
|
2860
|
+
/** See `SmartEffectResolver`. */
|
|
2861
|
+
resolve(filter, input, width, height, key) {
|
|
2862
|
+
const hit = this.cache.get(key);
|
|
2863
|
+
if (hit) {
|
|
2864
|
+
this.cache.delete(key);
|
|
2865
|
+
this.cache.set(key, hit);
|
|
2866
|
+
return { result: hit, pending: false };
|
|
2867
|
+
}
|
|
2868
|
+
if (this.pending.has(key)) return { result: null, pending: true };
|
|
2869
|
+
const provider = this.slot.provider;
|
|
2870
|
+
if (!provider || this.failures.has(key)) return { result: null, pending: false };
|
|
2871
|
+
const generation = this.generation;
|
|
2872
|
+
const source = { width, height, data: new Uint8ClampedArray(input) };
|
|
2873
|
+
const run = this.filter(provider, filter.effect, filter.values, source).then((out) => {
|
|
2874
|
+
if (generation !== this.generation) return;
|
|
2875
|
+
this.cache.set(key, out);
|
|
2876
|
+
while (this.cache.size > this.maxEntries) this.cache.delete(this.cache.keys().next().value);
|
|
2877
|
+
}).catch((error) => {
|
|
2878
|
+
if (generation === this.generation) this.failures.set(key, error instanceof Error ? error : new Error(String(error)));
|
|
2879
|
+
}).finally(() => {
|
|
2880
|
+
if (this.pending.get(key) === run) this.pending.delete(key);
|
|
2881
|
+
if (generation === this.generation) this.notify();
|
|
2882
|
+
});
|
|
2883
|
+
this.pending.set(key, run);
|
|
2884
|
+
return { result: null, pending: true };
|
|
2885
|
+
}
|
|
2886
|
+
/** A filter effect over `source`: its result, or (for an overlay) the effect drawn over it. */
|
|
2887
|
+
async filter(provider, effect, values, source) {
|
|
2888
|
+
const d = await this.describe(provider, effect);
|
|
2889
|
+
const takesSource = d.inputs.some((i) => i.name === "source");
|
|
2890
|
+
const out = await this.render(provider, effect, source.width, source.height, 8, { time: 0, values, ...takesSource ? { inputs: { source } } : {} });
|
|
2891
|
+
return takesSource ? out.data : over(source.data, out.data);
|
|
2892
|
+
}
|
|
2893
|
+
/**
|
|
2894
|
+
* Renders a generator or overlay effect at a size and depth (an effect layer's pixels). Rejects
|
|
2895
|
+
* with the provider's error, or without a provider.
|
|
2896
|
+
*/
|
|
2897
|
+
async renderLayer(effect, values, size) {
|
|
2898
|
+
const provider = this.slot.require();
|
|
2899
|
+
const depth = size.depth ?? 8;
|
|
2900
|
+
const out = await this.render(provider, effect, size.width, size.height, depth, { time: size.time ?? 0, values });
|
|
2901
|
+
return out.width === size.width && out.height === size.height ? out : createEffectPixels(size.width, size.height);
|
|
2902
|
+
}
|
|
2903
|
+
/** Why the result for `key` failed, if it did. */
|
|
2904
|
+
failure(key) {
|
|
2905
|
+
return this.failures.get(key);
|
|
2906
|
+
}
|
|
2907
|
+
/** Every failure so far. */
|
|
2908
|
+
failuresList() {
|
|
2909
|
+
return [...this.failures].map(([key, error]) => ({ key, error }));
|
|
2910
|
+
}
|
|
2911
|
+
/** Resolves when nothing is rendering. */
|
|
2912
|
+
async idle() {
|
|
2913
|
+
while (this.pending.size) await Promise.all([...this.pending.values()]);
|
|
2914
|
+
}
|
|
2915
|
+
/** The provider's description of an effect, fetched once per provider. */
|
|
2916
|
+
describe(provider, id) {
|
|
2917
|
+
let d = this.descriptions.get(id);
|
|
2918
|
+
if (!d) {
|
|
2919
|
+
d = Promise.resolve().then(() => provider.describe(id));
|
|
2920
|
+
d.catch(() => this.descriptions.delete(id));
|
|
2921
|
+
this.descriptions.set(id, d);
|
|
2922
|
+
}
|
|
2923
|
+
return d;
|
|
2924
|
+
}
|
|
2925
|
+
render(provider, id, width, height, depth, request) {
|
|
2926
|
+
const k = `${id}@${width}x${height}x${depth}`;
|
|
2927
|
+
let r = this.renderers.get(k);
|
|
2928
|
+
if (!r) {
|
|
2929
|
+
const renderer = Promise.resolve().then(() => provider.createRenderer(id, { width, height, depth }));
|
|
2930
|
+
renderer.catch(() => this.renderers.delete(k));
|
|
2931
|
+
r = { renderer, queue: Promise.resolve() };
|
|
2932
|
+
this.renderers.set(k, r);
|
|
2933
|
+
}
|
|
2934
|
+
const entry = r;
|
|
2935
|
+
const next = entry.queue.then(() => entry.renderer).then((renderer) => renderer.render(request));
|
|
2936
|
+
entry.queue = next.catch(() => void 0);
|
|
2937
|
+
return next;
|
|
2938
|
+
}
|
|
2939
|
+
/** Forgets every result and failure and disposes the renderers. */
|
|
2940
|
+
reset() {
|
|
2941
|
+
this.generation++;
|
|
2942
|
+
this.cache.clear();
|
|
2943
|
+
this.pending.clear();
|
|
2944
|
+
this.failures.clear();
|
|
2945
|
+
this.descriptions.clear();
|
|
2946
|
+
for (const r of this.renderers.values()) r.renderer.then((x) => x.dispose(), () => {
|
|
2947
|
+
});
|
|
2948
|
+
this.renderers.clear();
|
|
2949
|
+
}
|
|
2950
|
+
subscribe(listener) {
|
|
2951
|
+
this.listeners.add(listener);
|
|
2952
|
+
return () => this.listeners.delete(listener);
|
|
2953
|
+
}
|
|
2954
|
+
notify() {
|
|
2955
|
+
for (const l of [...this.listeners]) l();
|
|
2956
|
+
}
|
|
2957
|
+
destroy() {
|
|
2958
|
+
this.reset();
|
|
2959
|
+
this.detach();
|
|
2960
|
+
this.listeners.clear();
|
|
2961
|
+
}
|
|
2962
|
+
};
|
|
2963
|
+
|
|
2775
2964
|
// src/image-editor/free-transform.ts
|
|
2776
2965
|
var FREE_TRANSFORM_CONTENTS = ["auto", "layer", "selection", "selection-copy", "selection-outline"];
|
|
2777
2966
|
var fail = (message) => {
|
|
@@ -3885,7 +4074,15 @@ var ImageEditorController = class {
|
|
|
3885
4074
|
viewKey = [];
|
|
3886
4075
|
/** For `compositePixels` and mask evaluation (the renderer keeps its own for the viewport). */
|
|
3887
4076
|
compositor = new LayerCompositor();
|
|
4077
|
+
/** The effects provider slot (see `setEffectsProvider`). */
|
|
4078
|
+
effects = new EffectsSlot();
|
|
4079
|
+
/** Renders provider effects: effect smart filter results and effect layers. */
|
|
4080
|
+
effectsEngine = new ImageEffectsEngine({ slot: this.effects });
|
|
4081
|
+
effectRefresh = "none";
|
|
3888
4082
|
constructor(options = {}) {
|
|
4083
|
+
this.effects.subscribe(() => this.queueEffectRefresh("all"));
|
|
4084
|
+
this.effectsEngine.subscribe(() => this.queueEffectRefresh("incomplete"));
|
|
4085
|
+
if (options.effects) this.effects.set(options.effects);
|
|
3889
4086
|
this.maxHistoryDepth = options.maxHistoryDepth ?? 50;
|
|
3890
4087
|
if (options.textShaper !== void 0) this.textShaperValue = options.textShaper;
|
|
3891
4088
|
this.maxHistoryBytes = options.maxHistoryBytes ?? 512 * 1024 * 1024;
|
|
@@ -4019,6 +4216,206 @@ var ImageEditorController = class {
|
|
|
4019
4216
|
const currentState = { ...this.state };
|
|
4020
4217
|
this.onStateChange?.(currentState);
|
|
4021
4218
|
this.listeners.forEach((l) => l(currentState));
|
|
4219
|
+
if (this.effects.provider) this.queueEffectRefresh("incomplete");
|
|
4220
|
+
}
|
|
4221
|
+
// --- Provider effects ---
|
|
4222
|
+
queueEffectRefresh(kind) {
|
|
4223
|
+
const queued = this.effectRefresh !== "none";
|
|
4224
|
+
if (kind === "all" || this.effectRefresh === "none") this.effectRefresh = kind;
|
|
4225
|
+
if (queued) return;
|
|
4226
|
+
queueMicrotask(() => {
|
|
4227
|
+
const all = this.effectRefresh === "all";
|
|
4228
|
+
this.effectRefresh = "none";
|
|
4229
|
+
this.refreshEffectSurfaces(all);
|
|
4230
|
+
});
|
|
4231
|
+
}
|
|
4232
|
+
/**
|
|
4233
|
+
* Renders smart objects with effect filters again: every one (`all`, after the provider changed)
|
|
4234
|
+
* or those drawn while a result was rendering, once it is in. A cache refresh: no undo step.
|
|
4235
|
+
*/
|
|
4236
|
+
refreshEffectSurfaces(all) {
|
|
4237
|
+
let changed = false;
|
|
4238
|
+
const canvas = this.canvasSize;
|
|
4239
|
+
const walk = (layers) => {
|
|
4240
|
+
let out = null;
|
|
4241
|
+
layers.forEach((l, i) => {
|
|
4242
|
+
let next2 = l;
|
|
4243
|
+
if (l.type === "group") {
|
|
4244
|
+
const children = walk(l.children);
|
|
4245
|
+
if (children !== l.children) next2 = { ...l, children };
|
|
4246
|
+
} else if (l.type === "smart" && l.smartFilters?.filters.some((f) => f.kind === "effect") && (all || isSmartSurfaceIncomplete(l.surface))) {
|
|
4247
|
+
try {
|
|
4248
|
+
const r = rerender(l, canvas);
|
|
4249
|
+
if (all || !isSmartSurfaceIncomplete(r.surface)) next2 = r;
|
|
4250
|
+
} catch {
|
|
4251
|
+
}
|
|
4252
|
+
}
|
|
4253
|
+
if (next2 !== l) {
|
|
4254
|
+
out ??= [...layers];
|
|
4255
|
+
out[i] = next2;
|
|
4256
|
+
changed = true;
|
|
4257
|
+
}
|
|
4258
|
+
});
|
|
4259
|
+
return out ?? layers;
|
|
4260
|
+
};
|
|
4261
|
+
const next = walk(this.state.layers);
|
|
4262
|
+
if (!changed) return;
|
|
4263
|
+
this.state.layers = next;
|
|
4264
|
+
this.notify();
|
|
4265
|
+
}
|
|
4266
|
+
/** The registered effects provider, or `null`. */
|
|
4267
|
+
get effectsProvider() {
|
|
4268
|
+
return this.effects.provider;
|
|
4269
|
+
}
|
|
4270
|
+
/** Registers (or with `null` removes) the effects provider. Not an edit: no undo step. */
|
|
4271
|
+
setEffectsProvider(provider) {
|
|
4272
|
+
this.effects.set(provider);
|
|
4273
|
+
}
|
|
4274
|
+
/** Registers `provider` and returns a function that removes it again (if it is still the one set). */
|
|
4275
|
+
useEffects(provider) {
|
|
4276
|
+
return this.effects.use(provider);
|
|
4277
|
+
}
|
|
4278
|
+
/** The provider's description of an effect, checked for `place` (rejects without a provider or for an unknown id). */
|
|
4279
|
+
async describeEffect(effect, place) {
|
|
4280
|
+
const provider = this.effects.require();
|
|
4281
|
+
const d = await this.effectsEngine.describe(provider, effect);
|
|
4282
|
+
if (place) checkImageEffectPlace(d, place);
|
|
4283
|
+
return d;
|
|
4284
|
+
}
|
|
4285
|
+
/** Resolves when every effect render has landed and the surfaces waiting for them are drawn. */
|
|
4286
|
+
async effectsIdle() {
|
|
4287
|
+
const tick = () => new Promise((r) => queueMicrotask(r));
|
|
4288
|
+
for (let round6 = 0; round6 < 32; round6++) {
|
|
4289
|
+
await this.effectsEngine.idle();
|
|
4290
|
+
await tick();
|
|
4291
|
+
await tick();
|
|
4292
|
+
if (!this.effectsEngine.busy && this.effectRefresh === "none") return;
|
|
4293
|
+
}
|
|
4294
|
+
}
|
|
4295
|
+
/**
|
|
4296
|
+
* Applies a provider filter effect as a smart filter (at `index`, default the top), one undo
|
|
4297
|
+
* step. A pixel, shape or type layer (or a group) is first converted to a smart object in the
|
|
4298
|
+
* same step (Filter › Convert for Smart Filters), keeping its id when it is one layer. `values`
|
|
4299
|
+
* are checked against the effect's controls and stored over its defaults.
|
|
4300
|
+
*/
|
|
4301
|
+
async applyEffectFilter(layerId, effect, options = {}) {
|
|
4302
|
+
const d = await this.describeEffect(effect, "filter");
|
|
4303
|
+
const values = resolveEffectValues(d, options.values);
|
|
4304
|
+
const found = findLayer(this.state.layers, layerId);
|
|
4305
|
+
if (!found) throw new LayerOperationError(`There is no layer "${layerId}"; call list_layers for the layer ids.`);
|
|
4306
|
+
let layers = this.state.layers;
|
|
4307
|
+
let id = layerId;
|
|
4308
|
+
let converted = false;
|
|
4309
|
+
if (found.layer.type !== "smart") {
|
|
4310
|
+
if (found.layer.type === "adjustment" || found.layer.type === "annotation") throw new LayerOperationError(`Layer "${found.layer.name || layerId}" is ${found.layer.type === "adjustment" ? "an adjustment layer" : "an annotation"}, which has no pixels to filter; apply the effect to a pixel layer or smart object.`);
|
|
4311
|
+
const res2 = convertToSmartObject(layers, [layerId], this.canvasSize, { surfaceOf: (l) => this.surfaceOf(l) });
|
|
4312
|
+
layers = res2.layers;
|
|
4313
|
+
id = res2.layer.id;
|
|
4314
|
+
converted = true;
|
|
4315
|
+
}
|
|
4316
|
+
const filter = normalizeSmartFilter({ kind: "effect", effect: d.id, provider: this.effects.require().id, name: d.name, values, enabled: options.enabled !== false, blend: options.blend, opacity: options.opacity });
|
|
4317
|
+
const res = addSmartFilter(layers, id, filter, this.canvasSize, options.index);
|
|
4318
|
+
this.commitLayers(res.layers);
|
|
4319
|
+
return { layerId: id, filter: res.filter, converted };
|
|
4320
|
+
}
|
|
4321
|
+
/**
|
|
4322
|
+
* Changes an effect smart filter: `values` merged over its own (`null` resets a control to the
|
|
4323
|
+
* effect's default), `enabled`, `blend`, `opacity` or its place in the stack (`index`). Values
|
|
4324
|
+
* are checked against the provider's controls. One undo step (none with `commitToHistory`
|
|
4325
|
+
* false, during a drag).
|
|
4326
|
+
*/
|
|
4327
|
+
async updateEffectFilter(layerId, filterId, changes, commitToHistory = true) {
|
|
4328
|
+
const layer = requireSmartObject(this.state.layers, layerId);
|
|
4329
|
+
const was = layer.smartFilters?.filters.find((f) => f.id === filterId);
|
|
4330
|
+
if (!was || was.kind !== "effect") throw new LayerOperationError(`Smart object "${layer.name || layerId}" has no effect filter "${filterId}"; its filters are ${(layer.smartFilters?.filters ?? []).map((f) => `${f.id} (${f.kind === "effect" ? f.name ?? f.effect : f.kind})`).join(", ") || "none"}.`);
|
|
4331
|
+
const update = {};
|
|
4332
|
+
if (changes.values) {
|
|
4333
|
+
const d = await this.describeEffect(was.effect);
|
|
4334
|
+
const given = {};
|
|
4335
|
+
const base = { ...was.values };
|
|
4336
|
+
for (const [k, v] of Object.entries(changes.values)) {
|
|
4337
|
+
if (v !== null) given[k] = v;
|
|
4338
|
+
else if (k in d.controls.properties) base[k] = d.defaults[k];
|
|
4339
|
+
else throw new LayerOperationError(`"${k}" is not a control of "${d.id}"; its controls are ${Object.keys(d.controls.properties).join(", ")}.`);
|
|
4340
|
+
}
|
|
4341
|
+
update.values = { ...base, ...resolveEffectValues({ ...d, defaults: base }, given) };
|
|
4342
|
+
}
|
|
4343
|
+
for (const k of ["enabled", "blend", "opacity"]) if (changes[k] !== void 0) update[k] = changes[k];
|
|
4344
|
+
let res = updateSmartFilter(this.state.layers, layerId, filterId, update, this.canvasSize);
|
|
4345
|
+
if (changes.index !== void 0) res = { ...res, layers: moveSmartFilter(res.layers, layerId, filterId, changes.index, this.canvasSize).layers };
|
|
4346
|
+
this.commitLayers(res.layers, void 0, commitToHistory);
|
|
4347
|
+
return requireSmartObject(this.state.layers, layerId).smartFilters.filters.find((f) => f.id === filterId);
|
|
4348
|
+
}
|
|
4349
|
+
/**
|
|
4350
|
+
* Adds an effect layer: a pixel layer drawn by a provider generator, overlay or title effect at
|
|
4351
|
+
* the document's size and bit depth (at `time` ms, default 0), placed like `addPixelLayer`. One
|
|
4352
|
+
* undo step. Masks, blend and opacity apply as to any layer. Returns its id.
|
|
4353
|
+
*/
|
|
4354
|
+
async addEffectLayer(effect, options = {}) {
|
|
4355
|
+
const d = await this.describeEffect(effect, "layer");
|
|
4356
|
+
const values = resolveEffectValues(d, options.values);
|
|
4357
|
+
const { width, height } = this.naturalSize;
|
|
4358
|
+
const pixels = await this.effectsEngine.renderLayer(d.id, values, { width, height, depth: this.bitDepth, time: options.time });
|
|
4359
|
+
const blend = parseBlend(options.blend);
|
|
4360
|
+
if (options.opacity !== void 0 && !(options.opacity >= 0 && options.opacity <= 1)) throw new LayerOperationError("opacity must be from 0 to 1.");
|
|
4361
|
+
const ids = new Set(flattenLayers(this.state.layers).map((l) => l.id));
|
|
4362
|
+
const layer = {
|
|
4363
|
+
type: "pixel",
|
|
4364
|
+
id: options.id && !ids.has(options.id) ? options.id : newLayerId(),
|
|
4365
|
+
name: options.name?.trim() || d.name,
|
|
4366
|
+
visible: true,
|
|
4367
|
+
opacity: options.opacity ?? 1,
|
|
4368
|
+
blend: blend ?? "normal",
|
|
4369
|
+
surface: effectPixelsToSurface(pixels, this.bitDepth),
|
|
4370
|
+
offset: { x: 0, y: 0 },
|
|
4371
|
+
generator: { effect: d.id, provider: this.effects.require().id, name: d.name, values, ...options.time ? { time: options.time } : {} }
|
|
4372
|
+
};
|
|
4373
|
+
const next = placeLayer(this.state.layers, layer, this.newLayerPlacement(options));
|
|
4374
|
+
this.commitLayers(next, () => {
|
|
4375
|
+
this.state.activeLayerId = layer.id;
|
|
4376
|
+
this.state.activeTarget = "pixels";
|
|
4377
|
+
});
|
|
4378
|
+
return layer.id;
|
|
4379
|
+
}
|
|
4380
|
+
/**
|
|
4381
|
+
* Draws an effect layer again with new `values` (merged over its own; `null` resets a control)
|
|
4382
|
+
* or `time`, at the document's size and depth, replacing its pixels (painting on it is lost).
|
|
4383
|
+
* One undo step (none with `commitToHistory` false). Returns its effect settings.
|
|
4384
|
+
*/
|
|
4385
|
+
async updateEffectLayer(layerId, changes, commitToHistory = true) {
|
|
4386
|
+
const found = findLayer(this.state.layers, layerId);
|
|
4387
|
+
const layer = found?.layer;
|
|
4388
|
+
if (!layer || layer.type !== "pixel" || !layer.generator) throw new LayerOperationError(`"${layerId}" is not an effect layer; add one with add_effect_layer.`);
|
|
4389
|
+
requireUnlocked(layer, "pixels", "draw its effect again");
|
|
4390
|
+
const g = layer.generator;
|
|
4391
|
+
const d = await this.describeEffect(g.effect);
|
|
4392
|
+
const base = { ...g.values };
|
|
4393
|
+
const given = {};
|
|
4394
|
+
for (const [k, v] of Object.entries(changes.values ?? {})) {
|
|
4395
|
+
if (v !== null) given[k] = v;
|
|
4396
|
+
else if (k in d.controls.properties) base[k] = d.defaults[k];
|
|
4397
|
+
else throw new LayerOperationError(`"${k}" is not a control of "${d.id}"; its controls are ${Object.keys(d.controls.properties).join(", ")}.`);
|
|
4398
|
+
}
|
|
4399
|
+
const values = { ...base, ...resolveEffectValues({ ...d, defaults: base }, given) };
|
|
4400
|
+
const time = changes.time ?? g.time ?? 0;
|
|
4401
|
+
const { width, height } = this.naturalSize;
|
|
4402
|
+
const pixels = await this.effectsEngine.renderLayer(d.id, values, { width, height, depth: this.bitDepth, time });
|
|
4403
|
+
const generator = { ...g, values, provider: this.effects.require().id, ...time ? { time } : {} };
|
|
4404
|
+
if (!time) delete generator.time;
|
|
4405
|
+
const next = mapLayer(this.state.layers, layerId, (l) => ({ ...l, surface: effectPixelsToSurface(pixels, this.bitDepth), offset: { x: 0, y: 0 }, generator }));
|
|
4406
|
+
this.commitLayers(next, void 0, commitToHistory);
|
|
4407
|
+
return generator;
|
|
4408
|
+
}
|
|
4409
|
+
/** Turns an effect layer into a plain pixel layer keeping its pixels (one undo step). */
|
|
4410
|
+
rasterizeEffectLayer(layerId) {
|
|
4411
|
+
const found = findLayer(this.state.layers, layerId);
|
|
4412
|
+
if (!found || found.layer.type !== "pixel" || !found.layer.generator) throw new LayerOperationError(`"${layerId}" is not an effect layer.`);
|
|
4413
|
+
this.commitLayers(
|
|
4414
|
+
mapLayer(this.state.layers, layerId, (l) => {
|
|
4415
|
+
const { generator: _g, ...rest } = l;
|
|
4416
|
+
return rest;
|
|
4417
|
+
})
|
|
4418
|
+
);
|
|
4022
4419
|
}
|
|
4023
4420
|
displayFor = null;
|
|
4024
4421
|
/** Keeps the renderer's display transform in step with the document profile, mode and Proof Setup. */
|
|
@@ -4918,7 +5315,7 @@ var ImageEditorController = class {
|
|
|
4918
5315
|
}
|
|
4919
5316
|
// --- Smart objects ---
|
|
4920
5317
|
get canvasSize() {
|
|
4921
|
-
return this.naturalSize;
|
|
5318
|
+
return { ...this.naturalSize, effects: this.effectsEngine };
|
|
4922
5319
|
}
|
|
4923
5320
|
/**
|
|
4924
5321
|
* Layer › Smart Objects › Convert to Smart Object (one undo step): sibling layers (default the
|
|
@@ -13440,7 +13837,7 @@ function smartFilterRows(layer) {
|
|
|
13440
13837
|
filter: f,
|
|
13441
13838
|
index,
|
|
13442
13839
|
drawn: f.kind !== "psd",
|
|
13443
|
-
label: f.kind === "psd" ? f.name.replace(/&/g, "").replace(/\.\.\.$/, "") : f.kind === "blur" ? `Blur (${f.radius} px)` : "Develop"
|
|
13840
|
+
label: f.kind === "psd" ? f.name.replace(/&/g, "").replace(/\.\.\.$/, "") : f.kind === "effect" ? f.name ?? f.effect : f.kind === "blur" ? `Blur (${f.radius} px)` : "Develop"
|
|
13444
13841
|
}));
|
|
13445
13842
|
return rows.reverse();
|
|
13446
13843
|
}
|
|
@@ -14295,6 +14692,103 @@ var FreeTransformTool = class {
|
|
|
14295
14692
|
return false;
|
|
14296
14693
|
}
|
|
14297
14694
|
};
|
|
14695
|
+
|
|
14696
|
+
// src/image-editor/provider-effect-tools.ts
|
|
14697
|
+
function imageLayerAppliedEffects(layer) {
|
|
14698
|
+
const out = [];
|
|
14699
|
+
const ref = (r) => ({ effect: r.effect, ...r.name ? { name: r.name } : {}, ...r.provider ? { provider: r.provider } : {} });
|
|
14700
|
+
if (layer.type === "pixel" && layer.generator) out.push({ id: "generator", ...ref(layer.generator), role: "effect layer", enabled: true, values: layer.generator.values, ...layer.generator.time ? { time: layer.generator.time } : {} });
|
|
14701
|
+
if (layer.type === "smart")
|
|
14702
|
+
(layer.smartFilters?.filters ?? []).forEach((f, index) => {
|
|
14703
|
+
if (f.kind === "effect") out.push({ id: f.id, ...ref(f), role: "smart filter", enabled: f.enabled && layer.smartFilters.enabled, opacity: f.opacity, blend: f.blend, values: f.values, index });
|
|
14704
|
+
});
|
|
14705
|
+
return out;
|
|
14706
|
+
}
|
|
14707
|
+
function createImageEffectsToolProvider(controller, options = {}) {
|
|
14708
|
+
const readOnlyReason = () => {
|
|
14709
|
+
const ro = typeof options.readOnly === "function" ? options.readOnly() : options.readOnly;
|
|
14710
|
+
return ro ? typeof ro === "string" ? ro : "The image is read-only." : null;
|
|
14711
|
+
};
|
|
14712
|
+
const layerOf = (id) => {
|
|
14713
|
+
const found = findLayer(controller.getState().layers, id);
|
|
14714
|
+
if (!found) {
|
|
14715
|
+
const ids = flattenLayers(controller.getState().layers).map((l) => `"${l.id}"`);
|
|
14716
|
+
throw new Error(`There is no layer "${id}"; the layers are ${ids.join(", ") || "none"} (list_layers lists them).`);
|
|
14717
|
+
}
|
|
14718
|
+
return found.layer;
|
|
14719
|
+
};
|
|
14720
|
+
const result = (target, instance, extra = {}) => {
|
|
14721
|
+
const effects = imageLayerAppliedEffects(layerOf(target));
|
|
14722
|
+
return { target, effect: effects.find((e) => e.id === instance) ?? effects[effects.length - 1], effects, ...extra };
|
|
14723
|
+
};
|
|
14724
|
+
const plainValues = (v) => v;
|
|
14725
|
+
return createEffectToolProvider({
|
|
14726
|
+
id: options.id ?? "image-effects",
|
|
14727
|
+
namespace: options.namespace,
|
|
14728
|
+
target: { noun: "layer", id: "A layer id from list_layers." },
|
|
14729
|
+
applyUses: ["filter"],
|
|
14730
|
+
applyDetails: "The effect becomes a smart filter: it runs on the layer's pixels after the smart filters below it, with its own blend and opacity, under the smart filter mask, and can be changed or removed later. A pixel, shape or type layer (or a group) is converted to a smart object first, in the same undo step (one layer keeps its id; the result says converted). Generators and overlays go on their own layer through add_effect_layer.",
|
|
14731
|
+
provider: () => controller.effectsProvider,
|
|
14732
|
+
readOnlyReason,
|
|
14733
|
+
list: (target) => {
|
|
14734
|
+
const layers = target ? [layerOf(target)] : flattenLayers(controller.getState().layers);
|
|
14735
|
+
return layers.map((l) => ({ target: l.id, ...l.name ? { name: l.name } : {}, effects: imageLayerAppliedEffects(l) })).filter((t) => target || t.effects.length);
|
|
14736
|
+
},
|
|
14737
|
+
apply: async (target, effect, args) => {
|
|
14738
|
+
const r = await controller.applyEffectFilter(target, effect, { values: plainValues(args.values), index: args.index, enabled: args.enabled, opacity: args.opacity });
|
|
14739
|
+
return result(r.layerId, r.filter.id, { converted: r.converted });
|
|
14740
|
+
},
|
|
14741
|
+
update: async (target, instance, changes) => {
|
|
14742
|
+
const layer = layerOf(target);
|
|
14743
|
+
if (instance === "generator") {
|
|
14744
|
+
const extra = ["enabled", "opacity", "index"].filter((k) => changes[k] !== void 0);
|
|
14745
|
+
if (extra.length) throw new Error(`An effect layer's own effect only takes values; change the layer's ${extra.join(", ")} with update_layer.`);
|
|
14746
|
+
if (layer.type !== "pixel" || !layer.generator) throw new Error(`"${target}" is not an effect layer.`);
|
|
14747
|
+
await controller.updateEffectLayer(target, { values: changes.values });
|
|
14748
|
+
return result(target, "generator");
|
|
14749
|
+
}
|
|
14750
|
+
await controller.updateEffectFilter(target, instance, { values: changes.values, enabled: changes.enabled, opacity: changes.opacity, index: changes.index });
|
|
14751
|
+
return result(target, instance);
|
|
14752
|
+
},
|
|
14753
|
+
remove: async (target, instance) => {
|
|
14754
|
+
const layer = layerOf(target);
|
|
14755
|
+
const was = imageLayerAppliedEffects(layer).find((e) => e.id === instance);
|
|
14756
|
+
if (!was) throw new Error(`Layer "${layer.name || target}" has no provider effect "${instance}"; its effects are ${imageLayerAppliedEffects(layer).map((e) => `"${e.id}"`).join(", ") || "none"} (list_applied_effects).`);
|
|
14757
|
+
if (instance === "generator") controller.rasterizeEffectLayer(target);
|
|
14758
|
+
else controller.removeSmartFilter(target, instance);
|
|
14759
|
+
return { target, effect: was, effects: imageLayerAppliedEffects(layerOf(target)) };
|
|
14760
|
+
},
|
|
14761
|
+
subscribe: (notify) => controller.subscribe(() => notify()),
|
|
14762
|
+
extraTools: (ctx) => [
|
|
14763
|
+
{
|
|
14764
|
+
name: "add_effect_layer",
|
|
14765
|
+
title: "Add an effect layer",
|
|
14766
|
+
description: `Adds a layer drawn by a provider generator, overlay or title effect (search_effects with use generator, overlay or title) at the document's size and bit depth, above the active layer unless placed. Masks, blend mode, opacity and layer styles apply as to any layer; transparent effects show the layers beneath. update_effect with effect "generator" draws it again with new values; remove_effect "generator" keeps its pixels as a plain layer. One undo step. Returns the new layer id and its effect.`,
|
|
14767
|
+
inputSchema: {
|
|
14768
|
+
type: "object",
|
|
14769
|
+
properties: {
|
|
14770
|
+
effect: ctx.effectIdSchema(["generator", "overlay", "title"]),
|
|
14771
|
+
values: ctx.valuesSchema("Control values over the effect's defaults"),
|
|
14772
|
+
time: { type: "number", minimum: 0, description: "Which moment of an animated effect to draw, in ms (default 0)." },
|
|
14773
|
+
name: { type: "string", minLength: 1, description: "The layer name (default: the effect's name)." },
|
|
14774
|
+
aboveId: { type: "string", minLength: 1, description: "Put it directly above this layer (default: above the active layer, else on top)." },
|
|
14775
|
+
belowId: { type: "string", minLength: 1, description: "Put it directly below this layer." },
|
|
14776
|
+
opacity: { type: "number", minimum: 0, maximum: 1, description: "Layer opacity 0\u20131 (default 1)." },
|
|
14777
|
+
blend: { type: "string", enum: BLEND_MODES.map((m) => m.id).filter((b) => b !== "pigment"), description: "Layer blend mode (default normal); screen or add for light effects, multiply for textures." }
|
|
14778
|
+
},
|
|
14779
|
+
required: ["effect"],
|
|
14780
|
+
additionalProperties: false
|
|
14781
|
+
},
|
|
14782
|
+
annotations: { title: "Add an effect layer", ...ctx.annotations.write },
|
|
14783
|
+
handler: async (args) => {
|
|
14784
|
+
ctx.requireProvider();
|
|
14785
|
+
const id = await controller.addEffectLayer(args.effect, { values: plainValues(args.values), time: args.time, name: args.name, aboveId: args.aboveId, belowId: args.belowId, opacity: args.opacity, blend: args.blend });
|
|
14786
|
+
return { layerId: id, effects: imageLayerAppliedEffects(layerOf(id)) };
|
|
14787
|
+
}
|
|
14788
|
+
}
|
|
14789
|
+
]
|
|
14790
|
+
});
|
|
14791
|
+
}
|
|
14298
14792
|
export {
|
|
14299
14793
|
ADJUSTMENT_KEYS,
|
|
14300
14794
|
ADJUSTMENT_KINDS,
|
|
@@ -14372,12 +14866,14 @@ export {
|
|
|
14372
14866
|
IMAGE_CROP_PRESETS,
|
|
14373
14867
|
IMAGE_EDITOR_SCHEMA_VERSION,
|
|
14374
14868
|
IMAGE_EDITOR_STATE_VERSION,
|
|
14869
|
+
IMAGE_EFFECT_PLACES,
|
|
14375
14870
|
IMAGE_FILTER_PRESETS,
|
|
14376
14871
|
INTENTS,
|
|
14377
14872
|
INTENT_OPTIONS,
|
|
14378
14873
|
INTERPOLATIONS,
|
|
14379
14874
|
ImageEditorController,
|
|
14380
14875
|
ImageEditorRenderer,
|
|
14876
|
+
ImageEffectsEngine,
|
|
14381
14877
|
LATEST_PROCESS_VERSION,
|
|
14382
14878
|
LAYERED_SCHEMA_VERSION,
|
|
14383
14879
|
LAYER_EFFECT_KINDS,
|
|
@@ -14522,6 +15018,7 @@ export {
|
|
|
14522
15018
|
XYZ_D50_TO_SRGB,
|
|
14523
15019
|
ZlibError,
|
|
14524
15020
|
activeAdjustmentLayers,
|
|
15021
|
+
activeEffectSmartFilters,
|
|
14525
15022
|
activeSmartFilters,
|
|
14526
15023
|
addLayerEffect,
|
|
14527
15024
|
addLayerFilter,
|
|
@@ -14599,6 +15096,7 @@ export {
|
|
|
14599
15096
|
canvasToImagePoint,
|
|
14600
15097
|
caretAt,
|
|
14601
15098
|
changedTiles,
|
|
15099
|
+
checkImageEffectPlace,
|
|
14602
15100
|
clampControlValue,
|
|
14603
15101
|
clampCropToImage,
|
|
14604
15102
|
cleanMixer,
|
|
@@ -14638,6 +15136,7 @@ export {
|
|
|
14638
15136
|
createImageEditTools,
|
|
14639
15137
|
createImageEditorToolProvider,
|
|
14640
15138
|
createImageEffectTools,
|
|
15139
|
+
createImageEffectsToolProvider,
|
|
14641
15140
|
createImageLayerTools,
|
|
14642
15141
|
createImagePaintTools,
|
|
14643
15142
|
createImagePsdTools,
|
|
@@ -14716,6 +15215,7 @@ export {
|
|
|
14716
15215
|
edgeWidth,
|
|
14717
15216
|
effectColorHex,
|
|
14718
15217
|
effectControlChange,
|
|
15218
|
+
effectPixelsToSurface,
|
|
14719
15219
|
effectReach,
|
|
14720
15220
|
effectiveProof,
|
|
14721
15221
|
effectsFingerprint,
|
|
@@ -14797,6 +15297,7 @@ export {
|
|
|
14797
15297
|
identityTransform,
|
|
14798
15298
|
identityWarpMesh,
|
|
14799
15299
|
imageEditorStateMigrations,
|
|
15300
|
+
imageLayerAppliedEffects,
|
|
14800
15301
|
imageToCanvasPoint,
|
|
14801
15302
|
importPsd,
|
|
14802
15303
|
inflate,
|
|
@@ -14831,6 +15332,7 @@ export {
|
|
|
14831
15332
|
isSelectionEmpty,
|
|
14832
15333
|
isSelectionMode,
|
|
14833
15334
|
isSeparableBlendMode,
|
|
15335
|
+
isSmartSurfaceIncomplete,
|
|
14834
15336
|
isSpatialFilter,
|
|
14835
15337
|
isSrgb,
|
|
14836
15338
|
isTransformAnchor,
|
|
@@ -14866,7 +15368,7 @@ export {
|
|
|
14866
15368
|
lerpPigmentInto,
|
|
14867
15369
|
levelsValue,
|
|
14868
15370
|
linearSrgbToXy,
|
|
14869
|
-
linearToSrgb,
|
|
15371
|
+
linearToSrgb2 as linearToSrgb,
|
|
14870
15372
|
linearToSrgb8,
|
|
14871
15373
|
liveComponent,
|
|
14872
15374
|
liveShapeForDrag,
|