@pixerate/editor 0.206.0 → 0.208.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 +60 -59
- package/dist/animation-editor/index.js +6 -5
- package/dist/{brushstrokeRender-7LXCU7PD.js → brushstrokeRender-C3UU6GVC.js} +5 -4
- package/dist/{brushstrokeRender-VO4ZTWVA.cjs → brushstrokeRender-EAJNE6WP.cjs} +12 -11
- package/dist/canvas/index.cjs +8 -8
- package/dist/canvas/index.js +1 -1
- package/dist/{chunk-2N2QPO3H.js → chunk-2PBZBRP5.js} +9 -7
- package/dist/{chunk-7NW46N7I.cjs → chunk-3EHPLDFQ.cjs} +54 -52
- package/dist/{chunk-H367YTXI.js → chunk-42TWUPNJ.js} +13 -387
- package/dist/chunk-474IQG74.cjs +384 -0
- package/dist/{chunk-MLC7HUHO.js → chunk-BGOCAUXH.js} +1 -1
- package/dist/chunk-DWYQPL2A.js +2010 -0
- package/dist/{chunk-YXKLVD56.js → chunk-F7YA3MMC.js} +2 -2
- package/dist/{chunk-ZZGPYNS2.js → chunk-G4E6RUPC.js} +1 -1
- package/dist/chunk-HVVR5PZW.js +384 -0
- package/dist/{chunk-FR2Q2LVF.cjs → chunk-I7G3RSUJ.cjs} +35 -35
- package/dist/chunk-K6PXS6DO.cjs +102 -0
- package/dist/{chunk-ZIDUPSP4.js → chunk-KFIOEOCQ.js} +24 -761
- package/dist/{chunk-V7GWZIZK.cjs → chunk-KWNMBMKG.cjs} +23 -21
- package/dist/chunk-L3G23N5Y.js +763 -0
- package/dist/{chunk-GHCXLQ47.js → chunk-NDMXB4PR.js} +2 -2
- package/dist/{chunk-CWM3CUUX.cjs → chunk-OLNIPTB3.cjs} +39 -413
- package/dist/{chunk-EBDCRTZI.cjs → chunk-PQ3H2AAN.cjs} +92 -90
- package/dist/{chunk-BRKVNXDZ.cjs → chunk-QAUIZPYI.cjs} +7 -7
- package/dist/{chunk-UYXL6ZWQ.cjs → chunk-QE7D7TZR.cjs} +494 -1231
- package/dist/chunk-QYLWG22N.cjs +763 -0
- package/dist/{chunk-YQ5C5BVK.cjs → chunk-ROVK6AXX.cjs} +60 -60
- package/dist/{chunk-HGTDMKNE.cjs → chunk-UOGKSVRQ.cjs} +126 -221
- package/dist/{chunk-EL5PAHNP.js → chunk-USQCKRZG.js} +6 -4
- package/dist/{chunk-36OKQDCF.js → chunk-WJ2RBZMM.js} +5 -3
- package/dist/chunk-X4LUGTQX.cjs +2010 -0
- package/dist/{chunk-NQMP3CWF.js → chunk-YBJXCQTR.js} +15 -110
- package/dist/chunk-ZIF4UJJ6.js +102 -0
- package/dist/{chunk-WTW7QRTS.cjs → chunk-ZMHPQ6HP.cjs} +1 -1
- package/dist/image-editor/controller.d.cts +28 -3
- package/dist/image-editor/controller.d.ts +28 -3
- package/dist/image-editor/index.cjs +2150 -3380
- package/dist/image-editor/index.js +675 -1905
- package/dist/image-editor/selection/index.d.cts +2 -0
- package/dist/image-editor/selection/index.d.ts +2 -0
- package/dist/image-editor/selection/refine-edits.d.cts +13 -4
- package/dist/image-editor/selection/refine-edits.d.ts +13 -4
- package/dist/image-editor/selection/segment/focus.d.cts +2 -0
- package/dist/image-editor/selection/segment/focus.d.ts +2 -0
- package/dist/image-editor/selection/segment/grabcut.d.cts +4 -2
- package/dist/image-editor/selection/segment/grabcut.d.ts +4 -2
- package/dist/image-editor/selection/segment/sky.d.cts +2 -0
- package/dist/image-editor/selection/segment/sky.d.ts +2 -0
- package/dist/image-editor/selection/segment/subject.d.cts +2 -1
- package/dist/image-editor/selection/segment/subject.d.ts +2 -1
- package/dist/image-editor/selection/segment/work.d.cts +5 -0
- package/dist/image-editor/selection/segment/work.d.ts +5 -0
- package/dist/image-editor/selection/select-and-mask.d.cts +136 -0
- package/dist/image-editor/selection/select-and-mask.d.ts +136 -0
- package/dist/image-editor/selection/worker.d.cts +126 -0
- package/dist/image-editor/selection/worker.d.ts +126 -0
- package/dist/image-editor/selection-worker.cjs +14 -0
- package/dist/image-editor/selection-worker.d.cts +12 -0
- package/dist/image-editor/selection-worker.d.ts +12 -0
- package/dist/image-editor/selection-worker.js +14 -0
- package/dist/image-editor/types.d.cts +8 -0
- package/dist/image-editor/types.d.ts +8 -0
- package/dist/paint/index.cjs +6 -5
- package/dist/paint/index.js +5 -4
- package/dist/paint-editor/index.cjs +394 -390
- package/dist/paint-editor/index.js +18 -14
- package/dist/raster/index.cjs +22 -18
- package/dist/raster/index.js +29 -25
- package/dist/vector/index.cjs +986 -985
- package/dist/vector/index.js +6 -5
- package/package.json +11 -1
- package/skills/pixerate-image-editor/SKILL.md +1 -1
- package/skills/pixerate-image-selection-masking/SKILL.md +4 -2
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
2
|
parseCssColor
|
|
3
3
|
} from "./chunk-3CI7FRMK.js";
|
|
4
|
+
import {
|
|
5
|
+
createSelectionMask,
|
|
6
|
+
decodeSelectionRle,
|
|
7
|
+
encodeSelectionRle,
|
|
8
|
+
hashMaskContent,
|
|
9
|
+
resizeSelection
|
|
10
|
+
} from "./chunk-L3G23N5Y.js";
|
|
4
11
|
import {
|
|
5
12
|
DEFAULT_GLOBAL_LIGHT,
|
|
6
13
|
DeepCompositor,
|
|
@@ -21,7 +28,6 @@ import {
|
|
|
21
28
|
effectsScale,
|
|
22
29
|
findLayer,
|
|
23
30
|
flattenLayers,
|
|
24
|
-
gaussianBlur,
|
|
25
31
|
insertLayer,
|
|
26
32
|
isInterpolation,
|
|
27
33
|
isLayerEffectKind,
|
|
@@ -37,7 +43,7 @@ import {
|
|
|
37
43
|
validateEffectGradient,
|
|
38
44
|
validateLayerEffects,
|
|
39
45
|
warpSurface
|
|
40
|
-
} from "./chunk-
|
|
46
|
+
} from "./chunk-YBJXCQTR.js";
|
|
41
47
|
import {
|
|
42
48
|
affineFromHomography,
|
|
43
49
|
applyHomography,
|
|
@@ -48,6 +54,9 @@ import {
|
|
|
48
54
|
multiplyHomographies,
|
|
49
55
|
rectToQuad
|
|
50
56
|
} from "./chunk-OHKOFKBY.js";
|
|
57
|
+
import {
|
|
58
|
+
gaussianBlur
|
|
59
|
+
} from "./chunk-ZIF4UJJ6.js";
|
|
51
60
|
import {
|
|
52
61
|
Transform,
|
|
53
62
|
builtinProfile,
|
|
@@ -90,7 +99,7 @@ import {
|
|
|
90
99
|
transformTextRect,
|
|
91
100
|
translateMatrix,
|
|
92
101
|
warpPresetMesh
|
|
93
|
-
} from "./chunk-
|
|
102
|
+
} from "./chunk-NDMXB4PR.js";
|
|
94
103
|
import {
|
|
95
104
|
bezierPathBounds,
|
|
96
105
|
outlineStroke,
|
|
@@ -99,36 +108,35 @@ import {
|
|
|
99
108
|
transformBezierPath
|
|
100
109
|
} from "./chunk-WEIK3UYB.js";
|
|
101
110
|
import {
|
|
102
|
-
TILE,
|
|
103
|
-
TiledSurface,
|
|
104
111
|
base64ToBytes,
|
|
105
112
|
blendRgb,
|
|
106
113
|
bytesToBase64,
|
|
107
|
-
contentBounds,
|
|
108
114
|
decodePng,
|
|
109
|
-
emptySurface,
|
|
110
115
|
encodePng,
|
|
111
|
-
finishTile,
|
|
112
116
|
isBlendMode,
|
|
113
117
|
mixPigmentRgb,
|
|
114
118
|
normalizeBlendMode,
|
|
115
119
|
parseDataUrl,
|
|
116
120
|
pngDataUrl,
|
|
117
121
|
readDeepRegion,
|
|
118
|
-
readPixel,
|
|
119
|
-
readRegion,
|
|
120
122
|
setLum,
|
|
121
123
|
surfaceDepth,
|
|
122
124
|
surfaceFromDeepPixels,
|
|
125
|
+
writeDeepRegion
|
|
126
|
+
} from "./chunk-42TWUPNJ.js";
|
|
127
|
+
import {
|
|
128
|
+
TILE,
|
|
129
|
+
TiledSurface,
|
|
130
|
+
contentBounds,
|
|
131
|
+
emptySurface,
|
|
132
|
+
finishTile,
|
|
133
|
+
readRegion,
|
|
123
134
|
surfaceFromPixels,
|
|
124
|
-
tileCoord,
|
|
125
135
|
tileKey,
|
|
126
136
|
tilesInRect,
|
|
127
137
|
translateSurface,
|
|
128
|
-
uniformTile,
|
|
129
|
-
writeDeepRegion,
|
|
130
138
|
writeRegion
|
|
131
|
-
} from "./chunk-
|
|
139
|
+
} from "./chunk-HVVR5PZW.js";
|
|
132
140
|
import {
|
|
133
141
|
ZlibError,
|
|
134
142
|
deflate,
|
|
@@ -711,716 +719,6 @@ function hitTestAnnotation(imgPt, ann, padding = 10) {
|
|
|
711
719
|
return imgPt.x >= b.x - padding && imgPt.x <= b.x + b.width + padding && imgPt.y >= b.y - padding && imgPt.y <= b.y + b.height + padding;
|
|
712
720
|
}
|
|
713
721
|
|
|
714
|
-
// src/image-editor/selection/mask.ts
|
|
715
|
-
var SELECTION_MODES = ["replace", "add", "subtract", "intersect"];
|
|
716
|
-
function isSelectionMode(value) {
|
|
717
|
-
return typeof value === "string" && SELECTION_MODES.includes(value);
|
|
718
|
-
}
|
|
719
|
-
function parseSelectionMode(value) {
|
|
720
|
-
return isSelectionMode(value) ? value : "replace";
|
|
721
|
-
}
|
|
722
|
-
var MAX_SIDE = 1 << 15;
|
|
723
|
-
function checkSize(width, height) {
|
|
724
|
-
if (!Number.isInteger(width) || !Number.isInteger(height) || width < 1 || height < 1 || width > MAX_SIDE || height > MAX_SIDE) {
|
|
725
|
-
throw new Error(`Invalid selection size ${width}\xD7${height}`);
|
|
726
|
-
}
|
|
727
|
-
}
|
|
728
|
-
function computeSelectionBounds(width, height, data) {
|
|
729
|
-
let x0 = width;
|
|
730
|
-
let x1 = -1;
|
|
731
|
-
let y0 = -1;
|
|
732
|
-
let y1 = -1;
|
|
733
|
-
for (let y = 0; y < height; y++) {
|
|
734
|
-
const row = y * width;
|
|
735
|
-
let first = -1;
|
|
736
|
-
for (let x = 0; x < width; x++) {
|
|
737
|
-
if (data[row + x] !== 0) {
|
|
738
|
-
first = x;
|
|
739
|
-
break;
|
|
740
|
-
}
|
|
741
|
-
}
|
|
742
|
-
if (first < 0) continue;
|
|
743
|
-
let last = first;
|
|
744
|
-
for (let x = width - 1; x > first; x--) {
|
|
745
|
-
if (data[row + x] !== 0) {
|
|
746
|
-
last = x;
|
|
747
|
-
break;
|
|
748
|
-
}
|
|
749
|
-
}
|
|
750
|
-
if (y0 < 0) y0 = y;
|
|
751
|
-
y1 = y;
|
|
752
|
-
if (first < x0) x0 = first;
|
|
753
|
-
if (last > x1) x1 = last;
|
|
754
|
-
}
|
|
755
|
-
if (y0 < 0) return null;
|
|
756
|
-
return { x: x0, y: y0, width: x1 - x0 + 1, height: y1 - y0 + 1 };
|
|
757
|
-
}
|
|
758
|
-
var TILE_PIXELS = TILE * TILE;
|
|
759
|
-
var MATERIALIZED_LIMIT = 4;
|
|
760
|
-
var materialized = /* @__PURE__ */ new Map();
|
|
761
|
-
var lastRead = null;
|
|
762
|
-
var lastData = null;
|
|
763
|
-
var TiledSelectionMask = class {
|
|
764
|
-
constructor(width, height, surface, bounds, data) {
|
|
765
|
-
this.width = width;
|
|
766
|
-
this.height = height;
|
|
767
|
-
this.surface = surface;
|
|
768
|
-
this.cachedBounds = bounds;
|
|
769
|
-
if (data) this.remember(data);
|
|
770
|
-
}
|
|
771
|
-
width;
|
|
772
|
-
height;
|
|
773
|
-
surface;
|
|
774
|
-
cachedBounds;
|
|
775
|
-
weak = null;
|
|
776
|
-
get bounds() {
|
|
777
|
-
if (this.cachedBounds === void 0) this.cachedBounds = contentBounds(this.surface);
|
|
778
|
-
return this.cachedBounds;
|
|
779
|
-
}
|
|
780
|
-
get data() {
|
|
781
|
-
if (lastRead === this) return lastData;
|
|
782
|
-
let d = materialized.get(this);
|
|
783
|
-
if (d) {
|
|
784
|
-
materialized.delete(this);
|
|
785
|
-
materialized.set(this, d);
|
|
786
|
-
lastRead = this;
|
|
787
|
-
lastData = d;
|
|
788
|
-
return d;
|
|
789
|
-
}
|
|
790
|
-
d = this.weak?.deref();
|
|
791
|
-
if (!d) {
|
|
792
|
-
const all = readRegion(this.surface, { x: 0, y: 0, width: this.width, height: this.height });
|
|
793
|
-
d = new Uint8Array(all.buffer, all.byteOffset, all.length);
|
|
794
|
-
}
|
|
795
|
-
this.remember(d);
|
|
796
|
-
return d;
|
|
797
|
-
}
|
|
798
|
-
remember(d) {
|
|
799
|
-
lastRead = this;
|
|
800
|
-
lastData = d;
|
|
801
|
-
this.weak = typeof WeakRef === "function" ? new WeakRef(d) : null;
|
|
802
|
-
materialized.set(this, d);
|
|
803
|
-
while (materialized.size > MATERIALIZED_LIMIT) materialized.delete(materialized.keys().next().value);
|
|
804
|
-
}
|
|
805
|
-
};
|
|
806
|
-
function tileGrid(width, height) {
|
|
807
|
-
return { cols: Math.ceil(width / TILE), rows: Math.ceil(height / TILE) };
|
|
808
|
-
}
|
|
809
|
-
function insideTile(tx, ty, width, height) {
|
|
810
|
-
return (tx + 1) * TILE <= width && (ty + 1) * TILE <= height;
|
|
811
|
-
}
|
|
812
|
-
function surfaceFromCoverage(width, height, data) {
|
|
813
|
-
const tiles = /* @__PURE__ */ new Map();
|
|
814
|
-
const { cols, rows } = tileGrid(width, height);
|
|
815
|
-
for (let ty = 0; ty < rows; ty++) {
|
|
816
|
-
for (let tx = 0; tx < cols; tx++) {
|
|
817
|
-
const ox = tx * TILE;
|
|
818
|
-
const oy = ty * TILE;
|
|
819
|
-
const w = Math.min(TILE, width - ox);
|
|
820
|
-
const h = Math.min(TILE, height - oy);
|
|
821
|
-
let lo = 255;
|
|
822
|
-
let hi = 0;
|
|
823
|
-
for (let y = 0; y < h && !(hi > 0 && lo < hi); y++) {
|
|
824
|
-
const row = (oy + y) * width + ox;
|
|
825
|
-
for (let x = 0; x < w; x++) {
|
|
826
|
-
const v = data[row + x];
|
|
827
|
-
if (v < lo) lo = v;
|
|
828
|
-
if (v > hi) hi = v;
|
|
829
|
-
}
|
|
830
|
-
}
|
|
831
|
-
if (hi === 0) continue;
|
|
832
|
-
if (lo === hi && w === TILE && h === TILE) {
|
|
833
|
-
tiles.set(tileKey(tx, ty), uniformTile(1, hi));
|
|
834
|
-
continue;
|
|
835
|
-
}
|
|
836
|
-
const t = new Uint8ClampedArray(TILE_PIXELS);
|
|
837
|
-
for (let y = 0; y < h; y++) {
|
|
838
|
-
const row = (oy + y) * width + ox;
|
|
839
|
-
for (let x = 0; x < w; x++) t[y * TILE + x] = data[row + x];
|
|
840
|
-
}
|
|
841
|
-
tiles.set(tileKey(tx, ty), { data: t, uniform: null });
|
|
842
|
-
}
|
|
843
|
-
}
|
|
844
|
-
return new TiledSurface(1, tiles, 0);
|
|
845
|
-
}
|
|
846
|
-
function clipTile(tile, w, h) {
|
|
847
|
-
const d = tile.data;
|
|
848
|
-
let outside = false;
|
|
849
|
-
for (let y = 0; y < TILE && !outside; y++) {
|
|
850
|
-
for (let x = y < h ? w : 0; x < TILE; x++) {
|
|
851
|
-
if (d[y * TILE + x] !== 0) {
|
|
852
|
-
outside = true;
|
|
853
|
-
break;
|
|
854
|
-
}
|
|
855
|
-
}
|
|
856
|
-
}
|
|
857
|
-
if (!outside) return tile;
|
|
858
|
-
const t = new Uint8ClampedArray(TILE_PIXELS);
|
|
859
|
-
for (let y = 0; y < h; y++) t.set(d.subarray(y * TILE, y * TILE + w), y * TILE);
|
|
860
|
-
return finishTile(t, 1, 0);
|
|
861
|
-
}
|
|
862
|
-
function selectionFromTiles(width, height, surface) {
|
|
863
|
-
checkSize(width, height);
|
|
864
|
-
if (surface.channels !== 1) throw new Error("A selection needs a 1-channel surface");
|
|
865
|
-
if (surface.defaultValue !== 0) {
|
|
866
|
-
const all = readRegion(surface, { x: 0, y: 0, width, height });
|
|
867
|
-
return createSelectionMask(width, height, new Uint8Array(all.buffer, all.byteOffset, all.length));
|
|
868
|
-
}
|
|
869
|
-
const { cols, rows } = tileGrid(width, height);
|
|
870
|
-
let tiles = null;
|
|
871
|
-
for (const [key, tile] of surface.tiles) {
|
|
872
|
-
const { tx, ty } = tileCoord(key);
|
|
873
|
-
let next = tile;
|
|
874
|
-
if (tx < 0 || ty < 0 || tx >= cols || ty >= rows) next = null;
|
|
875
|
-
else if (!insideTile(tx, ty, width, height)) next = clipTile(tile, Math.min(TILE, width - tx * TILE), Math.min(TILE, height - ty * TILE));
|
|
876
|
-
if (next === tile) continue;
|
|
877
|
-
tiles ??= new Map(surface.tiles);
|
|
878
|
-
if (next) tiles.set(key, next);
|
|
879
|
-
else tiles.delete(key);
|
|
880
|
-
}
|
|
881
|
-
return new TiledSelectionMask(width, height, tiles ? new TiledSurface(1, tiles, 0) : surface);
|
|
882
|
-
}
|
|
883
|
-
function createSelectionMask(width, height, data) {
|
|
884
|
-
checkSize(width, height);
|
|
885
|
-
if (!data) return new TiledSelectionMask(width, height, new TiledSurface(1, /* @__PURE__ */ new Map(), 0), null);
|
|
886
|
-
if (data.length !== width * height) throw new Error("Selection data does not match its size");
|
|
887
|
-
return new TiledSelectionMask(width, height, surfaceFromCoverage(width, height, data), void 0, data);
|
|
888
|
-
}
|
|
889
|
-
function selectionFromRegion(width, height, rect, data) {
|
|
890
|
-
checkSize(width, height);
|
|
891
|
-
if (rect.x < 0 || rect.y < 0 || rect.x + rect.width > width || rect.y + rect.height > height) throw new Error("The region lies outside the selection");
|
|
892
|
-
return new TiledSelectionMask(width, height, writeRegion(new TiledSurface(1, /* @__PURE__ */ new Map(), 0), rect, data));
|
|
893
|
-
}
|
|
894
|
-
function fullSelection(width, height) {
|
|
895
|
-
checkSize(width, height);
|
|
896
|
-
const tiles = /* @__PURE__ */ new Map();
|
|
897
|
-
const { cols, rows } = tileGrid(width, height);
|
|
898
|
-
for (let ty = 0; ty < rows; ty++) {
|
|
899
|
-
for (let tx = 0; tx < cols; tx++) {
|
|
900
|
-
if (insideTile(tx, ty, width, height)) {
|
|
901
|
-
tiles.set(tileKey(tx, ty), uniformTile(1, 255));
|
|
902
|
-
continue;
|
|
903
|
-
}
|
|
904
|
-
const t = new Uint8ClampedArray(TILE_PIXELS);
|
|
905
|
-
const w = Math.min(TILE, width - tx * TILE);
|
|
906
|
-
for (let y = 0; y < Math.min(TILE, height - ty * TILE); y++) t.fill(255, y * TILE, y * TILE + w);
|
|
907
|
-
tiles.set(tileKey(tx, ty), { data: t, uniform: null });
|
|
908
|
-
}
|
|
909
|
-
}
|
|
910
|
-
return new TiledSelectionMask(width, height, new TiledSurface(1, tiles, 0), { x: 0, y: 0, width, height });
|
|
911
|
-
}
|
|
912
|
-
function isSelectionEmpty(mask) {
|
|
913
|
-
return !mask || mask.bounds === null;
|
|
914
|
-
}
|
|
915
|
-
function selectionValueAt(mask, x, y) {
|
|
916
|
-
const xi = Math.floor(x);
|
|
917
|
-
const yi = Math.floor(y);
|
|
918
|
-
if (xi < 0 || yi < 0 || xi >= mask.width || yi >= mask.height) return 0;
|
|
919
|
-
return readPixel(mask.surface, xi, yi)[0] / 255;
|
|
920
|
-
}
|
|
921
|
-
function coverageToByte(v) {
|
|
922
|
-
if (!(v > 0)) return 0;
|
|
923
|
-
if (v >= 1) return 255;
|
|
924
|
-
return Math.floor(v * 255 + 0.5);
|
|
925
|
-
}
|
|
926
|
-
function selectionFromCoverage(width, height, coverage) {
|
|
927
|
-
const n = width * height;
|
|
928
|
-
const out = new Uint8Array(n);
|
|
929
|
-
for (let i = 0; i < n; i++) out[i] = coverageToByte(coverage[i]);
|
|
930
|
-
return createSelectionMask(width, height, out);
|
|
931
|
-
}
|
|
932
|
-
function selectionToCoverage(mask) {
|
|
933
|
-
const out = new Float32Array(mask.data.length);
|
|
934
|
-
for (let i = 0; i < out.length; i++) out[i] = mask.data[i] / 255;
|
|
935
|
-
return out;
|
|
936
|
-
}
|
|
937
|
-
function selectionArea(mask) {
|
|
938
|
-
if (!mask || !mask.bounds) return 0;
|
|
939
|
-
let sum = 0;
|
|
940
|
-
for (const tile of mask.surface.tiles.values()) {
|
|
941
|
-
if (tile.uniform !== null) {
|
|
942
|
-
sum += tile.uniform * TILE_PIXELS;
|
|
943
|
-
continue;
|
|
944
|
-
}
|
|
945
|
-
const d = tile.data;
|
|
946
|
-
for (let i = 0; i < d.length; i++) sum += d[i];
|
|
947
|
-
}
|
|
948
|
-
return sum / 255;
|
|
949
|
-
}
|
|
950
|
-
function combineSelections(prev, next, mode) {
|
|
951
|
-
let result;
|
|
952
|
-
if (mode === "replace" || !prev && mode === "add") {
|
|
953
|
-
result = next;
|
|
954
|
-
} else if (!prev) {
|
|
955
|
-
return null;
|
|
956
|
-
} else {
|
|
957
|
-
if (prev.width !== next.width || prev.height !== next.height) throw new Error("Selections differ in size");
|
|
958
|
-
result = new TiledSelectionMask(prev.width, prev.height, combineSurfaces(prev.surface, next.surface, mode));
|
|
959
|
-
}
|
|
960
|
-
return result.bounds ? result : null;
|
|
961
|
-
}
|
|
962
|
-
var FULL = 255;
|
|
963
|
-
function combineTile(a, b, mode) {
|
|
964
|
-
if (mode === "add") {
|
|
965
|
-
if (!b || a?.uniform === FULL) return a;
|
|
966
|
-
if (!a || b.uniform === FULL) return b;
|
|
967
|
-
} else if (mode === "subtract") {
|
|
968
|
-
if (!a || !b) return a;
|
|
969
|
-
if (b.uniform === FULL) return void 0;
|
|
970
|
-
} else {
|
|
971
|
-
if (!a || !b) return void 0;
|
|
972
|
-
if (a.uniform === FULL) return b;
|
|
973
|
-
if (b.uniform === FULL) return a;
|
|
974
|
-
}
|
|
975
|
-
const x = a.data;
|
|
976
|
-
const y = b.data;
|
|
977
|
-
const out = new Uint8ClampedArray(TILE_PIXELS);
|
|
978
|
-
if (mode === "add") for (let i = 0; i < out.length; i++) out[i] = x[i] > y[i] ? x[i] : y[i];
|
|
979
|
-
else if (mode === "subtract") for (let i = 0; i < out.length; i++) out[i] = x[i] > y[i] ? x[i] - y[i] : 0;
|
|
980
|
-
else for (let i = 0; i < out.length; i++) out[i] = x[i] < y[i] ? x[i] : y[i];
|
|
981
|
-
return finishTile(out, 1, 0) ?? void 0;
|
|
982
|
-
}
|
|
983
|
-
function combineSurfaces(a, b, mode) {
|
|
984
|
-
const tiles = /* @__PURE__ */ new Map();
|
|
985
|
-
for (const key of /* @__PURE__ */ new Set([...a.tiles.keys(), ...b.tiles.keys()])) {
|
|
986
|
-
const tile = combineTile(a.tiles.get(key), b.tiles.get(key), mode);
|
|
987
|
-
if (tile) tiles.set(key, tile);
|
|
988
|
-
}
|
|
989
|
-
return new TiledSurface(1, tiles, 0);
|
|
990
|
-
}
|
|
991
|
-
function invertSelection(mask) {
|
|
992
|
-
const { width, height } = mask;
|
|
993
|
-
const tiles = /* @__PURE__ */ new Map();
|
|
994
|
-
const { cols, rows } = tileGrid(width, height);
|
|
995
|
-
for (let ty = 0; ty < rows; ty++) {
|
|
996
|
-
for (let tx = 0; tx < cols; tx++) {
|
|
997
|
-
const src = mask.surface.getTile(tx, ty);
|
|
998
|
-
const key = tileKey(tx, ty);
|
|
999
|
-
if (insideTile(tx, ty, width, height)) {
|
|
1000
|
-
if (!src) tiles.set(key, uniformTile(1, FULL));
|
|
1001
|
-
else if (src.uniform !== null) {
|
|
1002
|
-
if (src.uniform !== FULL) tiles.set(key, uniformTile(1, FULL - src.uniform));
|
|
1003
|
-
} else {
|
|
1004
|
-
const out2 = new Uint8ClampedArray(TILE_PIXELS);
|
|
1005
|
-
for (let i = 0; i < out2.length; i++) out2[i] = FULL - src.data[i];
|
|
1006
|
-
const t2 = finishTile(out2, 1, 0);
|
|
1007
|
-
if (t2) tiles.set(key, t2);
|
|
1008
|
-
}
|
|
1009
|
-
continue;
|
|
1010
|
-
}
|
|
1011
|
-
const w = Math.min(TILE, width - tx * TILE);
|
|
1012
|
-
const h = Math.min(TILE, height - ty * TILE);
|
|
1013
|
-
const out = new Uint8ClampedArray(TILE_PIXELS);
|
|
1014
|
-
for (let y = 0; y < h; y++) for (let x = 0; x < w; x++) out[y * TILE + x] = FULL - (src ? src.data[y * TILE + x] : 0);
|
|
1015
|
-
const t = finishTile(out, 1, 0);
|
|
1016
|
-
if (t) tiles.set(key, t);
|
|
1017
|
-
}
|
|
1018
|
-
}
|
|
1019
|
-
return new TiledSelectionMask(width, height, new TiledSurface(1, tiles, 0));
|
|
1020
|
-
}
|
|
1021
|
-
function resizeSelection(mask, width, height) {
|
|
1022
|
-
if (mask.width === width && mask.height === height) return mask;
|
|
1023
|
-
checkSize(width, height);
|
|
1024
|
-
const out = new Uint8Array(width * height);
|
|
1025
|
-
const sx = mask.width / width;
|
|
1026
|
-
const sy = mask.height / height;
|
|
1027
|
-
const src = mask.data;
|
|
1028
|
-
const mw = mask.width;
|
|
1029
|
-
for (let y = 0; y < height; y++) {
|
|
1030
|
-
const fy = Math.min(mask.height - 1, Math.max(0, (y + 0.5) * sy - 0.5));
|
|
1031
|
-
const y0 = Math.floor(fy);
|
|
1032
|
-
const y1 = Math.min(mask.height - 1, y0 + 1);
|
|
1033
|
-
const ty = fy - y0;
|
|
1034
|
-
for (let x = 0; x < width; x++) {
|
|
1035
|
-
const fx = Math.min(mw - 1, Math.max(0, (x + 0.5) * sx - 0.5));
|
|
1036
|
-
const x0 = Math.floor(fx);
|
|
1037
|
-
const x1 = Math.min(mw - 1, x0 + 1);
|
|
1038
|
-
const tx = fx - x0;
|
|
1039
|
-
const top = src[y0 * mw + x0] * (1 - tx) + src[y0 * mw + x1] * tx;
|
|
1040
|
-
const bot = src[y1 * mw + x0] * (1 - tx) + src[y1 * mw + x1] * tx;
|
|
1041
|
-
out[y * width + x] = Math.round(top * (1 - ty) + bot * ty);
|
|
1042
|
-
}
|
|
1043
|
-
}
|
|
1044
|
-
return createSelectionMask(width, height, out);
|
|
1045
|
-
}
|
|
1046
|
-
function selectionFromPixels(pixels, options = {}) {
|
|
1047
|
-
const { width, height, data } = pixels;
|
|
1048
|
-
checkSize(width, height);
|
|
1049
|
-
if (data.length < width * height * 4) throw new Error("Pixel data is shorter than width \xD7 height \xD7 4");
|
|
1050
|
-
const channel = options.channel ?? "luminance";
|
|
1051
|
-
const out = new Uint8Array(width * height);
|
|
1052
|
-
const threshold = options.threshold;
|
|
1053
|
-
for (let i = 0, p = 0; i < out.length; i++, p += 4) {
|
|
1054
|
-
let v;
|
|
1055
|
-
switch (channel) {
|
|
1056
|
-
case "alpha":
|
|
1057
|
-
v = data[p + 3];
|
|
1058
|
-
break;
|
|
1059
|
-
case "red":
|
|
1060
|
-
v = data[p];
|
|
1061
|
-
break;
|
|
1062
|
-
case "green":
|
|
1063
|
-
v = data[p + 1];
|
|
1064
|
-
break;
|
|
1065
|
-
case "blue":
|
|
1066
|
-
v = data[p + 2];
|
|
1067
|
-
break;
|
|
1068
|
-
default:
|
|
1069
|
-
v = Math.round((0.2126 * data[p] + 0.7152 * data[p + 1] + 0.0722 * data[p + 2]) * data[p + 3] / 255);
|
|
1070
|
-
}
|
|
1071
|
-
if (options.invert) v = 255 - v;
|
|
1072
|
-
if (threshold !== void 0) v = v >= threshold ? 255 : 0;
|
|
1073
|
-
out[i] = v;
|
|
1074
|
-
}
|
|
1075
|
-
return createSelectionMask(width, height, out);
|
|
1076
|
-
}
|
|
1077
|
-
function selectionToPixels(mask, as = "gray") {
|
|
1078
|
-
const out = new Uint8ClampedArray(mask.data.length * 4);
|
|
1079
|
-
for (let i = 0, p = 0; i < mask.data.length; i++, p += 4) {
|
|
1080
|
-
const v = mask.data[i];
|
|
1081
|
-
if (as === "gray") {
|
|
1082
|
-
out[p] = out[p + 1] = out[p + 2] = v;
|
|
1083
|
-
out[p + 3] = 255;
|
|
1084
|
-
} else {
|
|
1085
|
-
out[p] = out[p + 1] = out[p + 2] = 255;
|
|
1086
|
-
out[p + 3] = v;
|
|
1087
|
-
}
|
|
1088
|
-
}
|
|
1089
|
-
return { width: mask.width, height: mask.height, data: out };
|
|
1090
|
-
}
|
|
1091
|
-
var B64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
1092
|
-
var B64_INDEX = (() => {
|
|
1093
|
-
const t = new Int16Array(128).fill(-1);
|
|
1094
|
-
for (let i = 0; i < B64.length; i++) t[B64.charCodeAt(i)] = i;
|
|
1095
|
-
return t;
|
|
1096
|
-
})();
|
|
1097
|
-
function toBase64(bytes, length) {
|
|
1098
|
-
const parts = [];
|
|
1099
|
-
let chunk = "";
|
|
1100
|
-
for (let i = 0; i < length; i += 3) {
|
|
1101
|
-
const a = bytes[i];
|
|
1102
|
-
const b = i + 1 < length ? bytes[i + 1] : 0;
|
|
1103
|
-
const c = i + 2 < length ? bytes[i + 2] : 0;
|
|
1104
|
-
chunk += B64[a >> 2] + B64[(a & 3) << 4 | b >> 4];
|
|
1105
|
-
chunk += i + 1 < length ? B64[(b & 15) << 2 | c >> 6] : "=";
|
|
1106
|
-
chunk += i + 2 < length ? B64[c & 63] : "=";
|
|
1107
|
-
if (chunk.length >= 8192) {
|
|
1108
|
-
parts.push(chunk);
|
|
1109
|
-
chunk = "";
|
|
1110
|
-
}
|
|
1111
|
-
}
|
|
1112
|
-
parts.push(chunk);
|
|
1113
|
-
return parts.join("");
|
|
1114
|
-
}
|
|
1115
|
-
function fromBase64(text3) {
|
|
1116
|
-
if (text3.length % 4 !== 0) throw new Error("Invalid base64 length");
|
|
1117
|
-
let pad = 0;
|
|
1118
|
-
if (text3.endsWith("==")) pad = 2;
|
|
1119
|
-
else if (text3.endsWith("=")) pad = 1;
|
|
1120
|
-
const out = new Uint8Array(text3.length / 4 * 3 - pad);
|
|
1121
|
-
let o = 0;
|
|
1122
|
-
const digit = (i, k) => {
|
|
1123
|
-
const ch = text3.charCodeAt(i + k);
|
|
1124
|
-
if (ch === 61 && i + 4 === text3.length && k >= 4 - pad) return 0;
|
|
1125
|
-
const idx = ch < 128 ? B64_INDEX[ch] : -1;
|
|
1126
|
-
if (idx < 0) throw new Error("Invalid base64 character");
|
|
1127
|
-
return idx;
|
|
1128
|
-
};
|
|
1129
|
-
for (let i = 0; i < text3.length; i += 4) {
|
|
1130
|
-
const n = digit(i, 0) << 18 | digit(i, 1) << 12 | digit(i, 2) << 6 | digit(i, 3);
|
|
1131
|
-
if (o < out.length) out[o++] = n >> 16 & 255;
|
|
1132
|
-
if (o < out.length) out[o++] = n >> 8 & 255;
|
|
1133
|
-
if (o < out.length) out[o++] = n & 255;
|
|
1134
|
-
}
|
|
1135
|
-
return out;
|
|
1136
|
-
}
|
|
1137
|
-
function encodeSelectionRle(data) {
|
|
1138
|
-
let buf = new Uint8Array(64);
|
|
1139
|
-
let len = 0;
|
|
1140
|
-
const push = (b) => {
|
|
1141
|
-
if (len === buf.length) {
|
|
1142
|
-
const next = new Uint8Array(buf.length * 2);
|
|
1143
|
-
next.set(buf);
|
|
1144
|
-
buf = next;
|
|
1145
|
-
}
|
|
1146
|
-
buf[len++] = b;
|
|
1147
|
-
};
|
|
1148
|
-
let i = 0;
|
|
1149
|
-
while (i < data.length) {
|
|
1150
|
-
const v = data[i];
|
|
1151
|
-
let j = i + 1;
|
|
1152
|
-
while (j < data.length && data[j] === v) j++;
|
|
1153
|
-
push(v);
|
|
1154
|
-
let run = j - i;
|
|
1155
|
-
while (run >= 128) {
|
|
1156
|
-
push(run & 127 | 128);
|
|
1157
|
-
run = Math.floor(run / 128);
|
|
1158
|
-
}
|
|
1159
|
-
push(run);
|
|
1160
|
-
i = j;
|
|
1161
|
-
}
|
|
1162
|
-
return "r1:" + toBase64(buf, len);
|
|
1163
|
-
}
|
|
1164
|
-
function decodeSelectionRle(rle, length) {
|
|
1165
|
-
if (typeof rle !== "string" || !rle.startsWith("r1:")) throw new Error("Unknown selection encoding");
|
|
1166
|
-
const bytes = fromBase64(rle.slice(3));
|
|
1167
|
-
const out = new Uint8Array(length);
|
|
1168
|
-
let o = 0;
|
|
1169
|
-
let i = 0;
|
|
1170
|
-
while (i < bytes.length) {
|
|
1171
|
-
const v = bytes[i++];
|
|
1172
|
-
let run = 0;
|
|
1173
|
-
let scale = 1;
|
|
1174
|
-
for (; ; ) {
|
|
1175
|
-
if (i >= bytes.length) throw new Error("Truncated selection run");
|
|
1176
|
-
const b = bytes[i++];
|
|
1177
|
-
run += (b & 127) * scale;
|
|
1178
|
-
if (b < 128) break;
|
|
1179
|
-
scale *= 128;
|
|
1180
|
-
if (scale > 2 ** 35) throw new Error("Selection run too long");
|
|
1181
|
-
}
|
|
1182
|
-
if (run === 0 || o + run > length) throw new Error("Selection runs do not match its size");
|
|
1183
|
-
if (v !== 0) out.fill(v, o, o + run);
|
|
1184
|
-
o += run;
|
|
1185
|
-
}
|
|
1186
|
-
if (o !== length) throw new Error("Selection runs do not match its size");
|
|
1187
|
-
return out;
|
|
1188
|
-
}
|
|
1189
|
-
function serializeSelection(mask) {
|
|
1190
|
-
return { width: mask.width, height: mask.height, rle: encodeSelectionRle(mask.data) };
|
|
1191
|
-
}
|
|
1192
|
-
function deserializeSelection(value) {
|
|
1193
|
-
if (!value || typeof value !== "object") throw new Error("selection must be an object");
|
|
1194
|
-
const v = value;
|
|
1195
|
-
const width = v.width;
|
|
1196
|
-
const height = v.height;
|
|
1197
|
-
checkSize(width, height);
|
|
1198
|
-
return createSelectionMask(width, height, decodeSelectionRle(v.rle, width * height));
|
|
1199
|
-
}
|
|
1200
|
-
function hashMaskContent(content) {
|
|
1201
|
-
let h = 2166136261;
|
|
1202
|
-
if (typeof content === "string") {
|
|
1203
|
-
for (let i = 0; i < content.length; i++) {
|
|
1204
|
-
h ^= content.charCodeAt(i);
|
|
1205
|
-
h = Math.imul(h, 16777619);
|
|
1206
|
-
}
|
|
1207
|
-
} else {
|
|
1208
|
-
for (let i = 0; i < content.length; i++) {
|
|
1209
|
-
h ^= content[i];
|
|
1210
|
-
h = Math.imul(h, 16777619);
|
|
1211
|
-
}
|
|
1212
|
-
}
|
|
1213
|
-
return `${(h >>> 0).toString(16)}:${content.length}`;
|
|
1214
|
-
}
|
|
1215
|
-
|
|
1216
|
-
// src/image-editor/selection/modify.ts
|
|
1217
|
-
var EMPTY_DISTANCE = 1e10;
|
|
1218
|
-
function distanceTransform(inside, width, height) {
|
|
1219
|
-
const n = width * height;
|
|
1220
|
-
const out = new Float32Array(n);
|
|
1221
|
-
let any = false;
|
|
1222
|
-
let all = true;
|
|
1223
|
-
for (let i = 0; i < n; i++) {
|
|
1224
|
-
if (inside[i]) any = true;
|
|
1225
|
-
else all = false;
|
|
1226
|
-
}
|
|
1227
|
-
if (!any) return out.fill(EMPTY_DISTANCE);
|
|
1228
|
-
if (all) return out;
|
|
1229
|
-
const NONE = 4294967295;
|
|
1230
|
-
const vertical = new Uint32Array(n).fill(NONE);
|
|
1231
|
-
for (let x = 0; x < width; x++) {
|
|
1232
|
-
let nearest = -1;
|
|
1233
|
-
for (let y = 0; y < height; y++) {
|
|
1234
|
-
const i = y * width + x;
|
|
1235
|
-
if (inside[i]) nearest = y;
|
|
1236
|
-
if (nearest >= 0) vertical[i] = y - nearest;
|
|
1237
|
-
}
|
|
1238
|
-
nearest = -1;
|
|
1239
|
-
for (let y = height - 1; y >= 0; y--) {
|
|
1240
|
-
const i = y * width + x;
|
|
1241
|
-
if (vertical[i] === 0) nearest = y;
|
|
1242
|
-
if (nearest >= 0) vertical[i] = Math.min(vertical[i], nearest - y);
|
|
1243
|
-
}
|
|
1244
|
-
}
|
|
1245
|
-
const sites = new Int32Array(width);
|
|
1246
|
-
const starts = new Float64Array(width);
|
|
1247
|
-
for (let y = 0; y < height; y++) {
|
|
1248
|
-
const row = y * width;
|
|
1249
|
-
let count = 0;
|
|
1250
|
-
for (let q2 = 0; q2 < width; q2++) {
|
|
1251
|
-
const dq = vertical[row + q2];
|
|
1252
|
-
if (dq === NONE) continue;
|
|
1253
|
-
const fq = dq * dq;
|
|
1254
|
-
let start = -Infinity;
|
|
1255
|
-
while (count > 0) {
|
|
1256
|
-
const p = sites[count - 1];
|
|
1257
|
-
const dp = vertical[row + p];
|
|
1258
|
-
start = (fq - dp * dp) / (2 * (q2 - p)) + (q2 + p) * 0.5;
|
|
1259
|
-
if (start > starts[count - 1]) break;
|
|
1260
|
-
count--;
|
|
1261
|
-
}
|
|
1262
|
-
if (count === 0) start = -Infinity;
|
|
1263
|
-
sites[count] = q2;
|
|
1264
|
-
starts[count] = start;
|
|
1265
|
-
count++;
|
|
1266
|
-
}
|
|
1267
|
-
if (count === 0) {
|
|
1268
|
-
out.fill(EMPTY_DISTANCE, row, row + width);
|
|
1269
|
-
continue;
|
|
1270
|
-
}
|
|
1271
|
-
let k = 0;
|
|
1272
|
-
for (let x = 0; x < width; x++) {
|
|
1273
|
-
while (k + 1 < count && starts[k + 1] < x) k++;
|
|
1274
|
-
const site = sites[k];
|
|
1275
|
-
const dx = x - site;
|
|
1276
|
-
const dv = vertical[row + site];
|
|
1277
|
-
out[row + x] = Math.sqrt(dx * dx + dv * dv);
|
|
1278
|
-
}
|
|
1279
|
-
}
|
|
1280
|
-
return out;
|
|
1281
|
-
}
|
|
1282
|
-
function thresholdInside(m, threshold = 0.5) {
|
|
1283
|
-
const out = new Uint8Array(m.length);
|
|
1284
|
-
for (let i = 0; i < m.length; i++) out[i] = m[i] >= threshold ? 1 : 0;
|
|
1285
|
-
return out;
|
|
1286
|
-
}
|
|
1287
|
-
function expandCoverage(m, w, h, r) {
|
|
1288
|
-
const d = distanceTransform(thresholdInside(m), w, h);
|
|
1289
|
-
const out = new Float32Array(m.length);
|
|
1290
|
-
for (let i = 0; i < out.length; i++) out[i] = Math.max(m[i], Math.min(1, Math.max(0, r + 1 - d[i])));
|
|
1291
|
-
return out;
|
|
1292
|
-
}
|
|
1293
|
-
function contractCoverage(m, w, h, r) {
|
|
1294
|
-
const inv = new Float32Array(m.length);
|
|
1295
|
-
for (let i = 0; i < inv.length; i++) inv[i] = 1 - m[i];
|
|
1296
|
-
const e = expandCoverage(inv, w, h, r);
|
|
1297
|
-
for (let i = 0; i < e.length; i++) e[i] = 1 - e[i];
|
|
1298
|
-
return e;
|
|
1299
|
-
}
|
|
1300
|
-
function borderCoverage(m, w, h, r) {
|
|
1301
|
-
const inside = thresholdInside(m);
|
|
1302
|
-
const outside = new Uint8Array(inside.length);
|
|
1303
|
-
for (let i = 0; i < inside.length; i++) outside[i] = inside[i] ? 0 : 1;
|
|
1304
|
-
const out = new Float32Array(m.length);
|
|
1305
|
-
if (!(r > 0)) return out;
|
|
1306
|
-
const toInside = distanceTransform(inside, w, h);
|
|
1307
|
-
const toOutside = distanceTransform(outside, w, h);
|
|
1308
|
-
for (let i = 0; i < out.length; i++) {
|
|
1309
|
-
const d = inside[i] ? toOutside[i] : toInside[i];
|
|
1310
|
-
out[i] = Math.min(1, Math.max(0, 1 - (d - 1) / r));
|
|
1311
|
-
}
|
|
1312
|
-
return out;
|
|
1313
|
-
}
|
|
1314
|
-
function boxLineReplicate(src, srcOff, stride, len, r, dst) {
|
|
1315
|
-
const at = (i) => src[srcOff + Math.min(len - 1, Math.max(0, i)) * stride];
|
|
1316
|
-
let sum = 0;
|
|
1317
|
-
for (let i = -r; i <= r; i++) sum += at(i);
|
|
1318
|
-
const k = 2 * r + 1;
|
|
1319
|
-
for (let i = 0; i < len; i++) {
|
|
1320
|
-
dst[i] = sum / k;
|
|
1321
|
-
sum += at(i + r + 1) - at(i - r);
|
|
1322
|
-
}
|
|
1323
|
-
}
|
|
1324
|
-
function smoothCoverage(m, w, h, r) {
|
|
1325
|
-
const rr = Number.isFinite(r) ? Math.max(0, Math.round(r)) : 0;
|
|
1326
|
-
const src = Float32Array.from(m);
|
|
1327
|
-
const tmp = new Float32Array(src.length);
|
|
1328
|
-
const line = new Float32Array(Math.max(w, h));
|
|
1329
|
-
if (rr > 0) {
|
|
1330
|
-
for (let y = 0; y < h; y++) {
|
|
1331
|
-
boxLineReplicate(src, y * w, 1, w, rr, line);
|
|
1332
|
-
tmp.set(line.subarray(0, w), y * w);
|
|
1333
|
-
}
|
|
1334
|
-
for (let x = 0; x < w; x++) {
|
|
1335
|
-
boxLineReplicate(tmp, x, w, h, rr, line);
|
|
1336
|
-
for (let y = 0; y < h; y++) src[y * w + x] = line[y];
|
|
1337
|
-
}
|
|
1338
|
-
}
|
|
1339
|
-
for (let i = 0; i < src.length; i++) src[i] = src[i] >= 0.5 ? 1 : 0;
|
|
1340
|
-
return src;
|
|
1341
|
-
}
|
|
1342
|
-
function featherCoverage(m, w, h, radius) {
|
|
1343
|
-
const out = gaussianBlur(m, w, h, Number.isFinite(radius) ? Math.max(0, radius) : 0, "zero");
|
|
1344
|
-
for (let i = 0; i < out.length; i++) if (Math.abs(out[i]) < 1e-7) out[i] = 0;
|
|
1345
|
-
return out;
|
|
1346
|
-
}
|
|
1347
|
-
function runningExtreme(src, off, stride, len, r, max, g, hb, dst) {
|
|
1348
|
-
const k = 2 * r + 1;
|
|
1349
|
-
const pick = max ? (a, b) => a > b ? a : b : (a, b) => a < b ? a : b;
|
|
1350
|
-
const fill = max ? -Infinity : Infinity;
|
|
1351
|
-
const plen = len + 2 * r;
|
|
1352
|
-
const at = (i) => i < r || i >= r + len ? fill : src[off + (i - r) * stride];
|
|
1353
|
-
for (let start = 0; start < plen; start += k) {
|
|
1354
|
-
const end = Math.min(plen, start + k);
|
|
1355
|
-
g[start] = at(start);
|
|
1356
|
-
for (let i = start + 1; i < end; i++) g[i] = pick(g[i - 1], at(i));
|
|
1357
|
-
hb[end - 1] = at(end - 1);
|
|
1358
|
-
for (let i = end - 2; i >= start; i--) hb[i] = pick(hb[i + 1], at(i));
|
|
1359
|
-
}
|
|
1360
|
-
for (let i = 0; i < len; i++) {
|
|
1361
|
-
const a = hb[i];
|
|
1362
|
-
const b = g[i + 2 * r];
|
|
1363
|
-
dst[i] = pick(a, b);
|
|
1364
|
-
}
|
|
1365
|
-
}
|
|
1366
|
-
function greyMorphology(src, w, h, r, grow) {
|
|
1367
|
-
const out = Float32Array.from(src);
|
|
1368
|
-
if (r <= 0) return out;
|
|
1369
|
-
const n = Math.max(w, h);
|
|
1370
|
-
const g = new Float32Array(n + 2 * r);
|
|
1371
|
-
const hb = new Float32Array(n + 2 * r);
|
|
1372
|
-
const line = new Float32Array(n);
|
|
1373
|
-
for (let y = 0; y < h; y++) {
|
|
1374
|
-
runningExtreme(out, y * w, 1, w, r, grow, g, hb, line);
|
|
1375
|
-
out.set(line.subarray(0, w), y * w);
|
|
1376
|
-
}
|
|
1377
|
-
for (let x = 0; x < w; x++) {
|
|
1378
|
-
runningExtreme(out, x, w, h, r, grow, g, hb, line);
|
|
1379
|
-
for (let y = 0; y < h; y++) out[y * w + x] = line[y];
|
|
1380
|
-
}
|
|
1381
|
-
return out;
|
|
1382
|
-
}
|
|
1383
|
-
function shiftCoverage(src, w, h, amount) {
|
|
1384
|
-
const mag = Math.abs(amount);
|
|
1385
|
-
if (!Number.isFinite(mag) || mag < 1e-4) return Float32Array.from(src);
|
|
1386
|
-
const grow = amount > 0;
|
|
1387
|
-
const whole = Math.floor(mag);
|
|
1388
|
-
const frac = mag - whole;
|
|
1389
|
-
if (frac < 1e-4) return greyMorphology(src, w, h, whole, grow);
|
|
1390
|
-
const lo = whole === 0 ? Float32Array.from(src) : greyMorphology(src, w, h, whole, grow);
|
|
1391
|
-
const hi = greyMorphology(src, w, h, whole + 1, grow);
|
|
1392
|
-
for (let i = 0; i < lo.length; i++) lo[i] += (hi[i] - lo[i]) * frac;
|
|
1393
|
-
return lo;
|
|
1394
|
-
}
|
|
1395
|
-
var SELECTION_MODIFY_OPS = ["expand", "contract", "feather", "border", "smooth", "grow", "shrink"];
|
|
1396
|
-
function wrap(mask, coverage) {
|
|
1397
|
-
const out = new Uint8Array(coverage.length);
|
|
1398
|
-
for (let i = 0; i < out.length; i++) out[i] = coverageToByte(coverage[i]);
|
|
1399
|
-
return createSelectionMask(mask.width, mask.height, out);
|
|
1400
|
-
}
|
|
1401
|
-
function modifySelection(mask, op, radius) {
|
|
1402
|
-
const r = Number.isFinite(radius) ? Math.max(0, radius) : 0;
|
|
1403
|
-
if (r === 0 && op !== "border") return mask;
|
|
1404
|
-
const m = selectionToCoverage(mask);
|
|
1405
|
-
const { width: w, height: h } = mask;
|
|
1406
|
-
switch (op) {
|
|
1407
|
-
case "expand":
|
|
1408
|
-
case "grow":
|
|
1409
|
-
return wrap(mask, expandCoverage(m, w, h, r));
|
|
1410
|
-
case "contract":
|
|
1411
|
-
case "shrink":
|
|
1412
|
-
return wrap(mask, contractCoverage(m, w, h, r));
|
|
1413
|
-
case "feather":
|
|
1414
|
-
return wrap(mask, featherCoverage(m, w, h, r));
|
|
1415
|
-
case "border":
|
|
1416
|
-
return wrap(mask, borderCoverage(m, w, h, r));
|
|
1417
|
-
case "smooth":
|
|
1418
|
-
return wrap(mask, smoothCoverage(m, w, h, r));
|
|
1419
|
-
default:
|
|
1420
|
-
throw new Error(`Unknown selection modification "${op}"`);
|
|
1421
|
-
}
|
|
1422
|
-
}
|
|
1423
|
-
|
|
1424
722
|
// src/image-editor/selection/layer-mask.ts
|
|
1425
723
|
function layerMaskSelection(layer, width, height) {
|
|
1426
724
|
if (!layer.mask || layer.mask.source) return null;
|
|
@@ -1429,12 +727,12 @@ function layerMaskSelection(layer, width, height) {
|
|
|
1429
727
|
const data = readRegion(layer.mask.surface, { x: -ox, y: -oy, width, height });
|
|
1430
728
|
return createSelectionMask(width, height, new Uint8Array(data.buffer, data.byteOffset, data.length));
|
|
1431
729
|
}
|
|
1432
|
-
var
|
|
730
|
+
var MAX_SIDE = 1 << 15;
|
|
1433
731
|
function normalizeLayerMask(raw) {
|
|
1434
732
|
if (!raw || typeof raw !== "object") return null;
|
|
1435
733
|
const r = raw;
|
|
1436
734
|
const { width, height, rle } = r;
|
|
1437
|
-
if (typeof width !== "number" || typeof height !== "number" || !Number.isInteger(width) || !Number.isInteger(height) || width < 1 || height < 1 || width >
|
|
735
|
+
if (typeof width !== "number" || typeof height !== "number" || !Number.isInteger(width) || !Number.isInteger(height) || width < 1 || height < 1 || width > MAX_SIDE || height > MAX_SIDE || typeof rle !== "string" || !rle.startsWith("r1:")) {
|
|
1438
736
|
return null;
|
|
1439
737
|
}
|
|
1440
738
|
const out = { width, height, rle };
|
|
@@ -12062,41 +11360,6 @@ export {
|
|
|
12062
11360
|
calculateCropDrag,
|
|
12063
11361
|
getAnnotationBounds,
|
|
12064
11362
|
hitTestAnnotation,
|
|
12065
|
-
SELECTION_MODES,
|
|
12066
|
-
isSelectionMode,
|
|
12067
|
-
parseSelectionMode,
|
|
12068
|
-
computeSelectionBounds,
|
|
12069
|
-
selectionFromTiles,
|
|
12070
|
-
createSelectionMask,
|
|
12071
|
-
selectionFromRegion,
|
|
12072
|
-
fullSelection,
|
|
12073
|
-
isSelectionEmpty,
|
|
12074
|
-
selectionValueAt,
|
|
12075
|
-
coverageToByte,
|
|
12076
|
-
selectionFromCoverage,
|
|
12077
|
-
selectionToCoverage,
|
|
12078
|
-
selectionArea,
|
|
12079
|
-
combineSelections,
|
|
12080
|
-
invertSelection,
|
|
12081
|
-
resizeSelection,
|
|
12082
|
-
selectionFromPixels,
|
|
12083
|
-
selectionToPixels,
|
|
12084
|
-
encodeSelectionRle,
|
|
12085
|
-
decodeSelectionRle,
|
|
12086
|
-
serializeSelection,
|
|
12087
|
-
deserializeSelection,
|
|
12088
|
-
hashMaskContent,
|
|
12089
|
-
EMPTY_DISTANCE,
|
|
12090
|
-
distanceTransform,
|
|
12091
|
-
expandCoverage,
|
|
12092
|
-
contractCoverage,
|
|
12093
|
-
borderCoverage,
|
|
12094
|
-
smoothCoverage,
|
|
12095
|
-
featherCoverage,
|
|
12096
|
-
greyMorphology,
|
|
12097
|
-
shiftCoverage,
|
|
12098
|
-
SELECTION_MODIFY_OPS,
|
|
12099
|
-
modifySelection,
|
|
12100
11363
|
layerMaskSelection,
|
|
12101
11364
|
normalizeLayerMask,
|
|
12102
11365
|
layerMaskFromSelection,
|