@pixerate/editor 0.200.0 → 0.202.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/{chunk-GLQQ2VIL.cjs → chunk-53GOGBTM.cjs} +23 -1
- package/dist/{chunk-3TNA7JKI.js → chunk-ZPJEH456.js} +22 -0
- package/dist/commands/builtins.d.cts +6 -0
- package/dist/commands/builtins.d.ts +6 -0
- package/dist/commands/index.cjs +4 -2
- package/dist/commands/index.js +4 -2
- package/dist/image-editor/controller.d.cts +18 -0
- package/dist/image-editor/controller.d.ts +18 -0
- package/dist/image-editor/index.cjs +180 -19
- package/dist/image-editor/index.js +179 -18
- package/dist/image-editor/selection/segment/index.d.cts +1 -0
- package/dist/image-editor/selection/segment/index.d.ts +1 -0
- package/dist/image-editor/selection/segment/sky.d.cts +21 -0
- package/dist/image-editor/selection/segment/sky.d.ts +21 -0
- package/dist/paint-editor/guide-tools.d.cts +2 -2
- package/dist/paint-editor/guide-tools.d.ts +2 -2
- package/dist/paint-editor/hosted.d.cts +34 -0
- package/dist/paint-editor/hosted.d.ts +34 -0
- package/dist/paint-editor/index.cjs +101 -10
- package/dist/paint-editor/index.d.cts +1 -0
- package/dist/paint-editor/index.d.ts +1 -0
- package/dist/paint-editor/index.js +96 -5
- package/dist/paint-editor/sticker-tools.d.cts +2 -2
- package/dist/paint-editor/sticker-tools.d.ts +2 -2
- package/dist/paint-editor/thick-tools.d.cts +2 -2
- package/dist/paint-editor/thick-tools.d.ts +2 -2
- package/dist/paint-editor/tools.d.cts +19 -2
- package/dist/paint-editor/tools.d.ts +19 -2
- package/dist/paint-editor/wet-tools.d.cts +2 -2
- package/dist/paint-editor/wet-tools.d.ts +2 -2
- package/package.json +1 -1
- package/skills/pixerate-image-editor/SKILL.md +1 -0
- package/skills/pixerate-image-selection-masking/SKILL.md +4 -2
- package/skills/pixerate-paint-editor/SKILL.md +9 -0
|
@@ -3692,14 +3692,14 @@ function toLab(r, g, b) {
|
|
|
3692
3692
|
function slicSuperpixels(work, target = 400, compactness = 10, rounds = 5) {
|
|
3693
3693
|
const { width: w, height: h, rgb } = work;
|
|
3694
3694
|
const n = w * h;
|
|
3695
|
-
const
|
|
3696
|
-
for (let i = 0; i < n; i++)
|
|
3695
|
+
const lab2 = new Float32Array(n * 3);
|
|
3696
|
+
for (let i = 0; i < n; i++) lab2.set(toLab(rgb[i * 3], rgb[i * 3 + 1], rgb[i * 3 + 2]), i * 3);
|
|
3697
3697
|
const S = Math.max(2, Math.round(Math.sqrt(n / target)));
|
|
3698
3698
|
const seeds = [];
|
|
3699
3699
|
for (let y = Math.floor(S / 2); y < h; y += S) {
|
|
3700
3700
|
for (let x = Math.floor(S / 2); x < w; x += S) {
|
|
3701
3701
|
const i = y * w + x;
|
|
3702
|
-
seeds.push([
|
|
3702
|
+
seeds.push([lab2[i * 3], lab2[i * 3 + 1], lab2[i * 3 + 2], x, y]);
|
|
3703
3703
|
}
|
|
3704
3704
|
}
|
|
3705
3705
|
const k = seeds.length;
|
|
@@ -3717,7 +3717,7 @@ function slicSuperpixels(work, target = 400, compactness = 10, rounds = 5) {
|
|
|
3717
3717
|
for (let y = y0; y <= y1; y++) {
|
|
3718
3718
|
for (let x = x0; x <= x1; x++) {
|
|
3719
3719
|
const i = y * w + x;
|
|
3720
|
-
const d = (
|
|
3720
|
+
const d = (lab2[i * 3] - L) ** 2 + (lab2[i * 3 + 1] - A) ** 2 + (lab2[i * 3 + 2] - B) ** 2 + m2 * ((x - cx) ** 2 + (y - cy) ** 2);
|
|
3721
3721
|
if (d < best[i]) {
|
|
3722
3722
|
best[i] = d;
|
|
3723
3723
|
label[i] = c;
|
|
@@ -3728,9 +3728,9 @@ function slicSuperpixels(work, target = 400, compactness = 10, rounds = 5) {
|
|
|
3728
3728
|
const sum = new Float64Array(k * 6);
|
|
3729
3729
|
for (let i = 0; i < n; i++) {
|
|
3730
3730
|
const c = label[i];
|
|
3731
|
-
sum[c * 6] +=
|
|
3732
|
-
sum[c * 6 + 1] +=
|
|
3733
|
-
sum[c * 6 + 2] +=
|
|
3731
|
+
sum[c * 6] += lab2[i * 3];
|
|
3732
|
+
sum[c * 6 + 1] += lab2[i * 3 + 1];
|
|
3733
|
+
sum[c * 6 + 2] += lab2[i * 3 + 2];
|
|
3734
3734
|
sum[c * 6 + 3] += i % w;
|
|
3735
3735
|
sum[c * 6 + 4] += Math.floor(i / w);
|
|
3736
3736
|
sum[c * 6 + 5]++;
|
|
@@ -3748,9 +3748,9 @@ function slicSuperpixels(work, target = 400, compactness = 10, rounds = 5) {
|
|
|
3748
3748
|
const size = new Int32Array(count);
|
|
3749
3749
|
for (let i = 0; i < n; i++) {
|
|
3750
3750
|
const c = label[i] = remap[label[i]];
|
|
3751
|
-
meanLab[c * 3] +=
|
|
3752
|
-
meanLab[c * 3 + 1] +=
|
|
3753
|
-
meanLab[c * 3 + 2] +=
|
|
3751
|
+
meanLab[c * 3] += lab2[i * 3];
|
|
3752
|
+
meanLab[c * 3 + 1] += lab2[i * 3 + 1];
|
|
3753
|
+
meanLab[c * 3 + 2] += lab2[i * 3 + 2];
|
|
3754
3754
|
centre[c * 2] += i % w;
|
|
3755
3755
|
centre[c * 2 + 1] += Math.floor(i / w);
|
|
3756
3756
|
size[c]++;
|
|
@@ -3763,8 +3763,8 @@ function slicSuperpixels(work, target = 400, compactness = 10, rounds = 5) {
|
|
|
3763
3763
|
return { label, count, lab: meanLab, centre, size };
|
|
3764
3764
|
}
|
|
3765
3765
|
function superpixelSaliency(sp, width, height) {
|
|
3766
|
-
const { count, lab, centre, size, label } = sp;
|
|
3767
|
-
const dist = (a, b) => Math.hypot(
|
|
3766
|
+
const { count, lab: lab2, centre, size, label } = sp;
|
|
3767
|
+
const dist = (a, b) => Math.hypot(lab2[a * 3] - lab2[b * 3], lab2[a * 3 + 1] - lab2[b * 3 + 1], lab2[a * 3 + 2] - lab2[b * 3 + 2]);
|
|
3768
3768
|
const onBorder = new Uint8Array(count);
|
|
3769
3769
|
for (let x = 0; x < width; x++) {
|
|
3770
3770
|
onBorder[label[x]] = 1;
|
|
@@ -4213,6 +4213,127 @@ function focusArea(pixels, options = {}) {
|
|
|
4213
4213
|
return { selection: softMaskToSelection(work, mask), noiseLevel: Math.round(sigma * 255 * 10) / 10 };
|
|
4214
4214
|
}
|
|
4215
4215
|
|
|
4216
|
+
// src/image-editor/selection/segment/sky.ts
|
|
4217
|
+
var SKY_SIZE = 768;
|
|
4218
|
+
function lab(r, g, b) {
|
|
4219
|
+
const lin = (v) => {
|
|
4220
|
+
v /= 255;
|
|
4221
|
+
return v <= 0.04045 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4);
|
|
4222
|
+
};
|
|
4223
|
+
const R = lin(r);
|
|
4224
|
+
const G = lin(g);
|
|
4225
|
+
const B = lin(b);
|
|
4226
|
+
const f = (t) => t > 8856e-6 ? Math.cbrt(t) : 7.787 * t + 16 / 116;
|
|
4227
|
+
const x = f((0.4124 * R + 0.3576 * G + 0.1805 * B) / 0.95047);
|
|
4228
|
+
const y = f(0.2126 * R + 0.7152 * G + 0.0722 * B);
|
|
4229
|
+
const z = f((0.0193 * R + 0.1192 * G + 0.9505 * B) / 1.08883);
|
|
4230
|
+
return [116 * y - 16, 500 * (x - y), 200 * (y - z)];
|
|
4231
|
+
}
|
|
4232
|
+
var sigmoid = (v) => 1 / (1 + Math.exp(-v));
|
|
4233
|
+
function findSky(pixels, options = {}) {
|
|
4234
|
+
const work = workImage(pixels, SKY_SIZE);
|
|
4235
|
+
const { width: w, height: h, rgb, scale } = work;
|
|
4236
|
+
const n = w * h;
|
|
4237
|
+
const L = new Float32Array(n);
|
|
4238
|
+
const A = new Float32Array(n);
|
|
4239
|
+
const B = new Float32Array(n);
|
|
4240
|
+
for (let i = 0; i < n; i++) {
|
|
4241
|
+
const [l, a, b] = lab(rgb[i * 3], rgb[i * 3 + 1], rgb[i * 3 + 2]);
|
|
4242
|
+
L[i] = l;
|
|
4243
|
+
A[i] = a;
|
|
4244
|
+
B[i] = b;
|
|
4245
|
+
}
|
|
4246
|
+
const luma2 = boxMean(workLuma(work), w, h, 1);
|
|
4247
|
+
const grad = new Float32Array(n);
|
|
4248
|
+
for (let y = 0; y < h; y++) {
|
|
4249
|
+
for (let x = 0; x < w; x++) {
|
|
4250
|
+
const i = y * w + x;
|
|
4251
|
+
const gx = x + 1 < w ? luma2[i + 1] - luma2[i] : 0;
|
|
4252
|
+
const gy = y + 1 < h ? luma2[i + w] - luma2[i] : 0;
|
|
4253
|
+
grad[i] = Math.hypot(gx, gy);
|
|
4254
|
+
}
|
|
4255
|
+
}
|
|
4256
|
+
const roughR = Math.max(1, Math.round(6e-3 * Math.max(w, h)));
|
|
4257
|
+
const rough = boxMean(grad, w, h, roughR);
|
|
4258
|
+
const score = new Float32Array(n);
|
|
4259
|
+
for (let i = 0; i < n; i++) {
|
|
4260
|
+
const chroma = Math.hypot(A[i], B[i]);
|
|
4261
|
+
const blue = sigmoid((-B[i] - 6) / 4) * sigmoid((L[i] - 35) / 6);
|
|
4262
|
+
const grey = sigmoid((L[i] - 68) / 5) * sigmoid((18 - chroma) / 4);
|
|
4263
|
+
const smooth = Math.exp(-rough[i] / 0.02);
|
|
4264
|
+
const y = Math.floor(i / w) / h;
|
|
4265
|
+
const high = 1 - 0.35 * y;
|
|
4266
|
+
score[i] = Math.max(blue, grey) * smooth * high;
|
|
4267
|
+
}
|
|
4268
|
+
const threshold = Math.min(100, Math.max(0, options.threshold ?? 50));
|
|
4269
|
+
const t = 0.55 - (threshold - 50) * 6e-3;
|
|
4270
|
+
const sky = new Uint8Array(n);
|
|
4271
|
+
const stack = [];
|
|
4272
|
+
const seedRows = Math.max(1, Math.round(h * 0.04));
|
|
4273
|
+
for (let y = 0; y < seedRows; y++) {
|
|
4274
|
+
for (let x = 0; x < w; x++) {
|
|
4275
|
+
const i = y * w + x;
|
|
4276
|
+
if (score[i] >= t) {
|
|
4277
|
+
sky[i] = 1;
|
|
4278
|
+
stack.push(i);
|
|
4279
|
+
}
|
|
4280
|
+
}
|
|
4281
|
+
}
|
|
4282
|
+
if (!stack.length) return { selection: createSelectionMask(pixels.width, pixels.height), reason: "No sky was found along the top of the image." };
|
|
4283
|
+
const low = t * 0.7;
|
|
4284
|
+
while (stack.length) {
|
|
4285
|
+
const p = stack.pop();
|
|
4286
|
+
const x = p % w;
|
|
4287
|
+
for (const q of [x + 1 < w ? p + 1 : -1, x > 0 ? p - 1 : -1, p + w < n ? p + w : -1, p - w]) {
|
|
4288
|
+
if (q < 0 || sky[q] || score[q] < low) continue;
|
|
4289
|
+
if (Math.hypot(L[q] - L[p], A[q] - A[p], B[q] - B[p]) > 6) continue;
|
|
4290
|
+
sky[q] = 1;
|
|
4291
|
+
stack.push(q);
|
|
4292
|
+
}
|
|
4293
|
+
}
|
|
4294
|
+
let count = 0;
|
|
4295
|
+
const mean = [0, 0, 0];
|
|
4296
|
+
for (let i = 0; i < n; i++) {
|
|
4297
|
+
if (!sky[i]) continue;
|
|
4298
|
+
mean[0] += L[i];
|
|
4299
|
+
mean[1] += A[i];
|
|
4300
|
+
mean[2] += B[i];
|
|
4301
|
+
count++;
|
|
4302
|
+
}
|
|
4303
|
+
for (let k = 0; k < 3; k++) mean[k] /= count;
|
|
4304
|
+
let spread = 0;
|
|
4305
|
+
for (let i = 0; i < n; i++) if (sky[i]) spread += (L[i] - mean[0]) ** 2 + (A[i] - mean[1]) ** 2 + (B[i] - mean[2]) ** 2;
|
|
4306
|
+
const limit = Math.max(8, 2.5 * Math.sqrt(spread / count));
|
|
4307
|
+
for (let i = 0; i < n; i++) {
|
|
4308
|
+
if (sky[i] || score[i] < t) continue;
|
|
4309
|
+
if (Math.hypot(L[i] - mean[0], A[i] - mean[1], B[i] - mean[2]) <= limit) sky[i] = 1;
|
|
4310
|
+
}
|
|
4311
|
+
let front = [];
|
|
4312
|
+
for (let i = 0; i < n; i++) if (sky[i]) front.push(i);
|
|
4313
|
+
for (let step = 0; step <= roughR + 1 && front.length; step++) {
|
|
4314
|
+
const next = [];
|
|
4315
|
+
for (const p of front) {
|
|
4316
|
+
const x = p % w;
|
|
4317
|
+
for (const q of [x + 1 < w ? p + 1 : -1, x > 0 ? p - 1 : -1, p + w < n ? p + w : -1, p - w]) {
|
|
4318
|
+
if (q < 0 || sky[q]) continue;
|
|
4319
|
+
if (Math.hypot(L[q] - mean[0], A[q] - mean[1], B[q] - mean[2]) > limit) continue;
|
|
4320
|
+
sky[q] = 1;
|
|
4321
|
+
next.push(q);
|
|
4322
|
+
}
|
|
4323
|
+
}
|
|
4324
|
+
front = next;
|
|
4325
|
+
}
|
|
4326
|
+
if (count < n * 5e-3) return { selection: createSelectionMask(pixels.width, pixels.height), reason: "No sky was found along the top of the image." };
|
|
4327
|
+
let mask = Float32Array.from(sky);
|
|
4328
|
+
const softness = Math.max(0, options.softness ?? 4) * scale;
|
|
4329
|
+
if (softness > 0) {
|
|
4330
|
+
const fitted = guidedFilterColor(workPlanes(work), mask, w, h, Math.max(1, Math.round(softness * 2)), 1e-3);
|
|
4331
|
+
mask = new Float32Array(n);
|
|
4332
|
+
for (let i = 0; i < n; i++) mask[i] = Math.min(1, Math.max(0, fitted[i]));
|
|
4333
|
+
}
|
|
4334
|
+
return { selection: softMaskToSelection(work, mask) };
|
|
4335
|
+
}
|
|
4336
|
+
|
|
4216
4337
|
// src/image-editor/layers/shape-type-ui.ts
|
|
4217
4338
|
var DEFAULT_SHAPE_TOOL_OPTIONS = {
|
|
4218
4339
|
kind: "rect",
|
|
@@ -5942,10 +6063,10 @@ function colorModePlanes(doc, mode) {
|
|
|
5942
6063
|
}
|
|
5943
6064
|
const t = Transform.create(src, builtinProfile("lab-d50"), { intent: "relative", bpc: false });
|
|
5944
6065
|
return (rgba, n) => {
|
|
5945
|
-
const
|
|
5946
|
-
t.convert8(rgba, 4,
|
|
6066
|
+
const lab2 = new Uint8Array(n * 4);
|
|
6067
|
+
t.convert8(rgba, 4, lab2, 4, false);
|
|
5947
6068
|
const planes = [0, 1, 2].map(() => new Uint8Array(n));
|
|
5948
|
-
for (let i = 0; i < n; i++) for (let c = 0; c < 3; c++) planes[c][i] =
|
|
6069
|
+
for (let i = 0; i < n; i++) for (let c = 0; c < 3; c++) planes[c][i] = lab2[i * 4 + c];
|
|
5949
6070
|
return planes;
|
|
5950
6071
|
};
|
|
5951
6072
|
}
|
|
@@ -9983,6 +10104,19 @@ var ImageEditorController = class {
|
|
|
9983
10104
|
this.setSelection(selection, options.mode ?? "replace");
|
|
9984
10105
|
return { selection: this.state.selection ?? null, noiseLevel };
|
|
9985
10106
|
}
|
|
10107
|
+
/**
|
|
10108
|
+
* Select › Sky: selects the sky (blue, or bright and grey when overcast; smooth; grown down from
|
|
10109
|
+
* the top of the image, with patches seen through gaps), fitted to its edges, combined with the
|
|
10110
|
+
* selection by `mode` as one undo step. Resolves to the selection and, when no sky was found
|
|
10111
|
+
* (the selection is then unchanged), why.
|
|
10112
|
+
*/
|
|
10113
|
+
async selectSky(options = {}) {
|
|
10114
|
+
const pixels = await this.requirePixels({ sample: options.sampleAllLayers === false ? "active-layer" : "composite" });
|
|
10115
|
+
const { selection, reason } = findSky(pixels, options);
|
|
10116
|
+
if (!selection.bounds) return { selection: this.state.selection ?? null, note: reason };
|
|
10117
|
+
this.setSelection(selection, options.mode ?? "replace");
|
|
10118
|
+
return { selection: this.state.selection ?? null };
|
|
10119
|
+
}
|
|
9986
10120
|
/**
|
|
9987
10121
|
* Supplies the pixels the selection tools sample (natural size RGBA), e.g. decoded in a worker
|
|
9988
10122
|
* or in a test; `null` goes back to reading the source image. Without a canvas (Node, workers)
|
|
@@ -11500,8 +11634,8 @@ var ImageEditorController = class {
|
|
|
11500
11634
|
const values = Array.from(out.subarray(0, t.outputs), (v) => Math.round(Math.min(1, Math.max(0, v)) * 1e4) / 1e4);
|
|
11501
11635
|
const l = new Float32Array(3);
|
|
11502
11636
|
toLab2.eval(rgb, l);
|
|
11503
|
-
const
|
|
11504
|
-
const res = { input, values, lab };
|
|
11637
|
+
const lab2 = [Math.round(l[0] * 1e4) / 100, Math.round((l[1] * 255 - 128) * 100) / 100, Math.round((l[2] * 255 - 128) * 100) / 100];
|
|
11638
|
+
const res = { input, values, lab: lab2 };
|
|
11505
11639
|
if (dst.colorSpace === "rgb") res.hex = `#${values.map((v) => Math.round(v * 255).toString(16).padStart(2, "0")).join("")}`;
|
|
11506
11640
|
return res;
|
|
11507
11641
|
});
|
|
@@ -12883,7 +13017,7 @@ var DEPTH_MASK_CONTROLS = [
|
|
|
12883
13017
|
// src/image-editor/playback.ts
|
|
12884
13018
|
var IMAGE_PLAYBACK_KIND = "layer";
|
|
12885
13019
|
var PAINT = /^(paint_stroke|clone_stamp|heal_brush|spot_heal|dodge|burn|sponge|smudge|blur_stroke|sharpen_stroke|history_brush|set_clone_source|set_clone_transform|fill_pixels|clear_pixels|set_paint|set_retouch_options|load_mixer_brush|clean_mixer_brush)$/;
|
|
12886
|
-
var SELECT = /^(select_region|select_subject|select_by_color|select_all|deselect|invert_selection|select_rect|select_ellipse|select_polygon|quick_select|select_object|select_focus_area|select_line|magic_wand|grow_selection|select_similar|reselect|select_layer_transparency|select_color_range|load_selection_from_image|load_selection_from_mask|set_selection_display|modify_selection|refine_selection|transform_selection|apply_selection_as_mask|layer_via_copy|stroke_selection|content_aware_fill|copy_pixels|cut_pixels|paste_pixels|save_selection|load_selection_from_channel|set_channel_properties|duplicate_channel|delete_channel|load_channel_from_image|set_quick_mask|path_to_selection|select_text_shape)$/;
|
|
13020
|
+
var SELECT = /^(select_region|select_subject|select_by_color|select_all|deselect|invert_selection|select_rect|select_ellipse|select_polygon|quick_select|select_object|select_focus_area|select_sky|select_line|magic_wand|grow_selection|select_similar|reselect|select_layer_transparency|select_color_range|load_selection_from_image|load_selection_from_mask|set_selection_display|modify_selection|refine_selection|transform_selection|apply_selection_as_mask|layer_via_copy|stroke_selection|content_aware_fill|copy_pixels|cut_pixels|paste_pixels|save_selection|load_selection_from_channel|set_channel_properties|duplicate_channel|delete_channel|load_channel_from_image|set_quick_mask|path_to_selection|select_text_shape)$/;
|
|
12887
13021
|
var ANNOTATE = /^(add_shape_layer|update_shape|add_shape_component|set_shape_component|remove_shape_component|merge_shape_components|add_text_layer|set_text|set_text_style|set_paragraph_style|set_text_frame|set_text_orientation|convert_text_to_shape)$/;
|
|
12888
13022
|
var CROP = /^(set_crop|crop_to_selection|reset_crop|set_rotation|flip_image|transform_layer|warp_layer|flip_layer|offset_layer)$/;
|
|
12889
13023
|
var ADJUST = /^(set_adjustments|add_adjustment_layer|update_adjustment_layer|remove_adjustment_layer|move_adjustment_layer)$/;
|
|
@@ -13547,6 +13681,31 @@ function createImageSelectionTools(controller) {
|
|
|
13547
13681
|
return { ...summary(controller), noiseLevel };
|
|
13548
13682
|
}
|
|
13549
13683
|
};
|
|
13684
|
+
const selectSkyTool = {
|
|
13685
|
+
name: "select_sky",
|
|
13686
|
+
title: "Select the sky",
|
|
13687
|
+
description: "Select \u203A Sky: selects the sky (blue, or bright and grey when overcast, smooth, reaching down from the top of the image, including patches seen through branches or gaps), with the edge fitted to the skyline, combined with the selection by mode. Use it to replace or recolour a sky or to adjust the land without it (invert_selection). Raise threshold to take in haze or a sunset glow, lower it if bright buildings come in. One undoable step. Returns the selection, and a note when no sky was found (the selection is then unchanged).",
|
|
13688
|
+
inputSchema: {
|
|
13689
|
+
type: "object",
|
|
13690
|
+
properties: {
|
|
13691
|
+
threshold: { type: "number", minimum: 0, maximum: 100, description: "How sky-like a pixel must be, 0..100 (default 50): higher takes in less sky-like pixels." },
|
|
13692
|
+
softness: { type: "number", minimum: 0, maximum: 100, description: "Soften the edge over this many natural pixels (default 4; 0 for a hard edge)." },
|
|
13693
|
+
mode: modeSchema,
|
|
13694
|
+
sampleAllLayers: { type: "boolean", description: "Read every layer (default true); false reads only the active pixel layer." }
|
|
13695
|
+
},
|
|
13696
|
+
additionalProperties: false
|
|
13697
|
+
},
|
|
13698
|
+
annotations: writeAnnotations("Select the sky", true),
|
|
13699
|
+
handler: async (args) => {
|
|
13700
|
+
const { note } = await controller.selectSky({
|
|
13701
|
+
threshold: args.threshold === void 0 ? void 0 : num2(args.threshold, 50),
|
|
13702
|
+
softness: args.softness === void 0 ? void 0 : num2(args.softness, 4),
|
|
13703
|
+
mode: parseSelectionMode(args.mode),
|
|
13704
|
+
sampleAllLayers: args.sampleAllLayers
|
|
13705
|
+
});
|
|
13706
|
+
return { ...summary(controller), ...note ? { note } : {} };
|
|
13707
|
+
}
|
|
13708
|
+
};
|
|
13550
13709
|
const magicWandTool = {
|
|
13551
13710
|
name: "magic_wand",
|
|
13552
13711
|
title: "Magic wand",
|
|
@@ -14103,6 +14262,7 @@ function createImageSelectionTools(controller) {
|
|
|
14103
14262
|
selectObjectTool,
|
|
14104
14263
|
createImageSubjectSelectionTool(controller),
|
|
14105
14264
|
selectFocusAreaTool,
|
|
14265
|
+
selectSkyTool,
|
|
14106
14266
|
lineTool,
|
|
14107
14267
|
magicWandTool,
|
|
14108
14268
|
colorRangeTool,
|
|
@@ -20816,6 +20976,7 @@ export {
|
|
|
20816
20976
|
findAdjustmentControl,
|
|
20817
20977
|
findLayer,
|
|
20818
20978
|
findLayerControl,
|
|
20979
|
+
findSky,
|
|
20819
20980
|
findSubject,
|
|
20820
20981
|
findWebGpu,
|
|
20821
20982
|
finishTile,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Select › Sky without a segmentation model. Sky is smooth, sits at the top of the frame, and is
|
|
3
|
+
* blue, or bright and grey when overcast. Each pixel gets a score from those three cues; the sky
|
|
4
|
+
* is grown down from the top rows through high-scoring pixels; patches of the same colour seen
|
|
5
|
+
* through gaps (between branches, under a bridge) are added; and the edge is fitted to the image
|
|
6
|
+
* with the colour guided filter.
|
|
7
|
+
*/
|
|
8
|
+
import { type PixelBuffer, type SelectionMask } from '../mask.cjs';
|
|
9
|
+
export interface SkyOptions {
|
|
10
|
+
/** 0..100 (default 50): higher takes in less sky-like pixels (haze, a sunset glow). */
|
|
11
|
+
threshold?: number;
|
|
12
|
+
/** Soften the edge over this many natural pixels (default 4; 0 for a hard edge). */
|
|
13
|
+
softness?: number;
|
|
14
|
+
}
|
|
15
|
+
export interface FoundSky {
|
|
16
|
+
selection: SelectionMask;
|
|
17
|
+
/** Why nothing was selected, when nothing was. */
|
|
18
|
+
reason?: string;
|
|
19
|
+
}
|
|
20
|
+
/** The sky in `pixels`, as a selection of the whole image (empty, with a reason, when there is none). */
|
|
21
|
+
export declare function findSky(pixels: PixelBuffer, options?: SkyOptions): FoundSky;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Select › Sky without a segmentation model. Sky is smooth, sits at the top of the frame, and is
|
|
3
|
+
* blue, or bright and grey when overcast. Each pixel gets a score from those three cues; the sky
|
|
4
|
+
* is grown down from the top rows through high-scoring pixels; patches of the same colour seen
|
|
5
|
+
* through gaps (between branches, under a bridge) are added; and the edge is fitted to the image
|
|
6
|
+
* with the colour guided filter.
|
|
7
|
+
*/
|
|
8
|
+
import { type PixelBuffer, type SelectionMask } from '../mask.js';
|
|
9
|
+
export interface SkyOptions {
|
|
10
|
+
/** 0..100 (default 50): higher takes in less sky-like pixels (haze, a sunset glow). */
|
|
11
|
+
threshold?: number;
|
|
12
|
+
/** Soften the edge over this many natural pixels (default 4; 0 for a hard edge). */
|
|
13
|
+
softness?: number;
|
|
14
|
+
}
|
|
15
|
+
export interface FoundSky {
|
|
16
|
+
selection: SelectionMask;
|
|
17
|
+
/** Why nothing was selected, when nothing was. */
|
|
18
|
+
reason?: string;
|
|
19
|
+
}
|
|
20
|
+
/** The sky in `pixels`, as a selection of the whole image (empty, with a reason, when there is none). */
|
|
21
|
+
export declare function findSky(pixels: PixelBuffer, options?: SkyOptions): FoundSky;
|
|
@@ -3,6 +3,6 @@
|
|
|
3
3
|
* images) and colour (harmonies, swatches, the mixing pad). Guides, swatches and the pad are
|
|
4
4
|
* editor state saved with the document (the pad is not saved), not undo steps.
|
|
5
5
|
*/
|
|
6
|
-
import type {
|
|
6
|
+
import type { PaintAgentTool } from './hosted.cjs';
|
|
7
7
|
import type { PaintEditorController } from './controller.cjs';
|
|
8
|
-
export declare function createPaintGuideTools(c: PaintEditorController):
|
|
8
|
+
export declare function createPaintGuideTools(c: PaintEditorController): PaintAgentTool[];
|
|
@@ -3,6 +3,6 @@
|
|
|
3
3
|
* images) and colour (harmonies, swatches, the mixing pad). Guides, swatches and the pad are
|
|
4
4
|
* editor state saved with the document (the pad is not saved), not undo steps.
|
|
5
5
|
*/
|
|
6
|
-
import type {
|
|
6
|
+
import type { PaintAgentTool } from './hosted.js';
|
|
7
7
|
import type { PaintEditorController } from './controller.js';
|
|
8
|
-
export declare function createPaintGuideTools(c: PaintEditorController):
|
|
8
|
+
export declare function createPaintGuideTools(c: PaintEditorController): PaintAgentTool[];
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { AgentTool } from '../tools/registry.cjs';
|
|
2
|
+
import type { PaintEditorController } from './controller.cjs';
|
|
3
|
+
/**
|
|
4
|
+
* Position fields of a paint tool, as paths: keys joined by `.`, a key ending in `[]` maps over
|
|
5
|
+
* that array, `''` is the value itself. Each path names an object whose numeric `x` / `y` are a
|
|
6
|
+
* position (a point, or the top-left of a rect); missing or `null` values are skipped.
|
|
7
|
+
*/
|
|
8
|
+
export interface PaintToolCoordinates {
|
|
9
|
+
/** Argument fields, given in host coordinates and moved into document pixels. */
|
|
10
|
+
in?: readonly string[];
|
|
11
|
+
/** Result fields, in document pixels and moved out into host coordinates. */
|
|
12
|
+
out?: readonly string[];
|
|
13
|
+
}
|
|
14
|
+
/** A paint tool with its position fields. */
|
|
15
|
+
export type PaintAgentTool = AgentTool & {
|
|
16
|
+
coordinates?: PaintToolCoordinates;
|
|
17
|
+
};
|
|
18
|
+
export interface PaintOrigin {
|
|
19
|
+
x: number;
|
|
20
|
+
y: number;
|
|
21
|
+
}
|
|
22
|
+
/** Host coordinates → document pixels, at the given position fields. */
|
|
23
|
+
export declare const toDocument: <T>(value: T, paths: readonly string[], origin: PaintOrigin) => T;
|
|
24
|
+
/** Document pixels → host coordinates, at the given position fields. */
|
|
25
|
+
export declare const fromDocument: <T>(value: T, paths: readonly string[], origin: PaintOrigin) => T;
|
|
26
|
+
/** Tool prose in host terms: positions are canvas coordinates, not document pixels. */
|
|
27
|
+
export declare function hostedText(text: string): string;
|
|
28
|
+
/** `hostedText` over every `description` in a JSON schema. */
|
|
29
|
+
export declare function hostedSchema<T>(schema: T): T;
|
|
30
|
+
/**
|
|
31
|
+
* A controller that resolves `get` on every property access, so tools built over it never touch
|
|
32
|
+
* the document until a handler runs (a host can create the document lazily).
|
|
33
|
+
*/
|
|
34
|
+
export declare function lazyController(get: () => PaintEditorController): PaintEditorController;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { AgentTool } from '../tools/registry.js';
|
|
2
|
+
import type { PaintEditorController } from './controller.js';
|
|
3
|
+
/**
|
|
4
|
+
* Position fields of a paint tool, as paths: keys joined by `.`, a key ending in `[]` maps over
|
|
5
|
+
* that array, `''` is the value itself. Each path names an object whose numeric `x` / `y` are a
|
|
6
|
+
* position (a point, or the top-left of a rect); missing or `null` values are skipped.
|
|
7
|
+
*/
|
|
8
|
+
export interface PaintToolCoordinates {
|
|
9
|
+
/** Argument fields, given in host coordinates and moved into document pixels. */
|
|
10
|
+
in?: readonly string[];
|
|
11
|
+
/** Result fields, in document pixels and moved out into host coordinates. */
|
|
12
|
+
out?: readonly string[];
|
|
13
|
+
}
|
|
14
|
+
/** A paint tool with its position fields. */
|
|
15
|
+
export type PaintAgentTool = AgentTool & {
|
|
16
|
+
coordinates?: PaintToolCoordinates;
|
|
17
|
+
};
|
|
18
|
+
export interface PaintOrigin {
|
|
19
|
+
x: number;
|
|
20
|
+
y: number;
|
|
21
|
+
}
|
|
22
|
+
/** Host coordinates → document pixels, at the given position fields. */
|
|
23
|
+
export declare const toDocument: <T>(value: T, paths: readonly string[], origin: PaintOrigin) => T;
|
|
24
|
+
/** Document pixels → host coordinates, at the given position fields. */
|
|
25
|
+
export declare const fromDocument: <T>(value: T, paths: readonly string[], origin: PaintOrigin) => T;
|
|
26
|
+
/** Tool prose in host terms: positions are canvas coordinates, not document pixels. */
|
|
27
|
+
export declare function hostedText(text: string): string;
|
|
28
|
+
/** `hostedText` over every `description` in a JSON schema. */
|
|
29
|
+
export declare function hostedSchema<T>(schema: T): T;
|
|
30
|
+
/**
|
|
31
|
+
* A controller that resolves `get` on every property access, so tools built over it never touch
|
|
32
|
+
* the document until a handler runs (a host can create the document lazily).
|
|
33
|
+
*/
|
|
34
|
+
export declare function lazyController(get: () => PaintEditorController): PaintEditorController;
|