@pixerate/editor 0.187.0 → 0.188.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/{chunk-FW7N64XK.js → chunk-IGMNJM2H.js} +221 -20
- package/dist/{chunk-X2KQQZBT.cjs → chunk-JYV4R62M.cjs} +381 -180
- package/dist/image-editor/controller.d.cts +3 -3
- package/dist/image-editor/controller.d.ts +3 -3
- package/dist/image-editor/index.cjs +729 -714
- package/dist/image-editor/index.js +59 -44
- package/dist/image-editor/selection/mask.d.cts +20 -1
- package/dist/image-editor/selection/mask.d.ts +20 -1
- package/dist/image-editor/selection/shapes.d.cts +8 -0
- package/dist/image-editor/selection/shapes.d.ts +8 -0
- package/dist/image-editor/selection/tool-options.d.cts +1 -1
- package/dist/image-editor/selection/tool-options.d.ts +1 -1
- package/dist/paint-editor/index.cjs +6 -6
- package/dist/paint-editor/index.js +1 -1
- package/package.json +1 -1
|
@@ -114,15 +114,19 @@ import {
|
|
|
114
114
|
parseDataUrl,
|
|
115
115
|
pngDataUrl,
|
|
116
116
|
readDeepRegion,
|
|
117
|
+
readPixel,
|
|
117
118
|
readRegion,
|
|
118
119
|
setLum,
|
|
119
120
|
surfaceDepth,
|
|
120
121
|
surfaceFromDeepPixels,
|
|
121
122
|
surfaceFromPixels,
|
|
123
|
+
tileCoord,
|
|
122
124
|
tileKey,
|
|
123
125
|
tilesInRect,
|
|
124
126
|
translateSurface,
|
|
125
|
-
|
|
127
|
+
uniformTile,
|
|
128
|
+
writeDeepRegion,
|
|
129
|
+
writeRegion
|
|
126
130
|
} from "./chunk-H367YTXI.js";
|
|
127
131
|
import {
|
|
128
132
|
ZlibError,
|
|
@@ -750,15 +754,159 @@ function computeSelectionBounds(width, height, data) {
|
|
|
750
754
|
if (y0 < 0) return null;
|
|
751
755
|
return { x: x0, y: y0, width: x1 - x0 + 1, height: y1 - y0 + 1 };
|
|
752
756
|
}
|
|
757
|
+
var TILE_PIXELS = TILE * TILE;
|
|
758
|
+
var MATERIALIZED_LIMIT = 4;
|
|
759
|
+
var materialized = /* @__PURE__ */ new Map();
|
|
760
|
+
var lastRead = null;
|
|
761
|
+
var lastData = null;
|
|
762
|
+
var TiledSelectionMask = class {
|
|
763
|
+
constructor(width, height, surface, bounds, data) {
|
|
764
|
+
this.width = width;
|
|
765
|
+
this.height = height;
|
|
766
|
+
this.surface = surface;
|
|
767
|
+
this.cachedBounds = bounds;
|
|
768
|
+
if (data) this.remember(data);
|
|
769
|
+
}
|
|
770
|
+
width;
|
|
771
|
+
height;
|
|
772
|
+
surface;
|
|
773
|
+
cachedBounds;
|
|
774
|
+
weak = null;
|
|
775
|
+
get bounds() {
|
|
776
|
+
if (this.cachedBounds === void 0) this.cachedBounds = contentBounds(this.surface);
|
|
777
|
+
return this.cachedBounds;
|
|
778
|
+
}
|
|
779
|
+
get data() {
|
|
780
|
+
if (lastRead === this) return lastData;
|
|
781
|
+
let d = materialized.get(this);
|
|
782
|
+
if (d) {
|
|
783
|
+
materialized.delete(this);
|
|
784
|
+
materialized.set(this, d);
|
|
785
|
+
lastRead = this;
|
|
786
|
+
lastData = d;
|
|
787
|
+
return d;
|
|
788
|
+
}
|
|
789
|
+
d = this.weak?.deref();
|
|
790
|
+
if (!d) {
|
|
791
|
+
const all = readRegion(this.surface, { x: 0, y: 0, width: this.width, height: this.height });
|
|
792
|
+
d = new Uint8Array(all.buffer, all.byteOffset, all.length);
|
|
793
|
+
}
|
|
794
|
+
this.remember(d);
|
|
795
|
+
return d;
|
|
796
|
+
}
|
|
797
|
+
remember(d) {
|
|
798
|
+
lastRead = this;
|
|
799
|
+
lastData = d;
|
|
800
|
+
this.weak = typeof WeakRef === "function" ? new WeakRef(d) : null;
|
|
801
|
+
materialized.set(this, d);
|
|
802
|
+
while (materialized.size > MATERIALIZED_LIMIT) materialized.delete(materialized.keys().next().value);
|
|
803
|
+
}
|
|
804
|
+
};
|
|
805
|
+
function tileGrid(width, height) {
|
|
806
|
+
return { cols: Math.ceil(width / TILE), rows: Math.ceil(height / TILE) };
|
|
807
|
+
}
|
|
808
|
+
function insideTile(tx, ty, width, height) {
|
|
809
|
+
return (tx + 1) * TILE <= width && (ty + 1) * TILE <= height;
|
|
810
|
+
}
|
|
811
|
+
function surfaceFromCoverage(width, height, data) {
|
|
812
|
+
const tiles = /* @__PURE__ */ new Map();
|
|
813
|
+
const { cols, rows } = tileGrid(width, height);
|
|
814
|
+
for (let ty = 0; ty < rows; ty++) {
|
|
815
|
+
for (let tx = 0; tx < cols; tx++) {
|
|
816
|
+
const ox = tx * TILE;
|
|
817
|
+
const oy = ty * TILE;
|
|
818
|
+
const w = Math.min(TILE, width - ox);
|
|
819
|
+
const h = Math.min(TILE, height - oy);
|
|
820
|
+
let lo = 255;
|
|
821
|
+
let hi = 0;
|
|
822
|
+
for (let y = 0; y < h && !(hi > 0 && lo < hi); y++) {
|
|
823
|
+
const row = (oy + y) * width + ox;
|
|
824
|
+
for (let x = 0; x < w; x++) {
|
|
825
|
+
const v = data[row + x];
|
|
826
|
+
if (v < lo) lo = v;
|
|
827
|
+
if (v > hi) hi = v;
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
if (hi === 0) continue;
|
|
831
|
+
if (lo === hi && w === TILE && h === TILE) {
|
|
832
|
+
tiles.set(tileKey(tx, ty), uniformTile(1, hi));
|
|
833
|
+
continue;
|
|
834
|
+
}
|
|
835
|
+
const t = new Uint8ClampedArray(TILE_PIXELS);
|
|
836
|
+
for (let y = 0; y < h; y++) {
|
|
837
|
+
const row = (oy + y) * width + ox;
|
|
838
|
+
for (let x = 0; x < w; x++) t[y * TILE + x] = data[row + x];
|
|
839
|
+
}
|
|
840
|
+
tiles.set(tileKey(tx, ty), { data: t, uniform: null });
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
return new TiledSurface(1, tiles, 0);
|
|
844
|
+
}
|
|
845
|
+
function clipTile(tile, w, h) {
|
|
846
|
+
const d = tile.data;
|
|
847
|
+
let outside = false;
|
|
848
|
+
for (let y = 0; y < TILE && !outside; y++) {
|
|
849
|
+
for (let x = y < h ? w : 0; x < TILE; x++) {
|
|
850
|
+
if (d[y * TILE + x] !== 0) {
|
|
851
|
+
outside = true;
|
|
852
|
+
break;
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
if (!outside) return tile;
|
|
857
|
+
const t = new Uint8ClampedArray(TILE_PIXELS);
|
|
858
|
+
for (let y = 0; y < h; y++) t.set(d.subarray(y * TILE, y * TILE + w), y * TILE);
|
|
859
|
+
return finishTile(t, 1, 0);
|
|
860
|
+
}
|
|
861
|
+
function selectionFromTiles(width, height, surface) {
|
|
862
|
+
checkSize(width, height);
|
|
863
|
+
if (surface.channels !== 1) throw new Error("A selection needs a 1-channel surface");
|
|
864
|
+
if (surface.defaultValue !== 0) {
|
|
865
|
+
const all = readRegion(surface, { x: 0, y: 0, width, height });
|
|
866
|
+
return createSelectionMask(width, height, new Uint8Array(all.buffer, all.byteOffset, all.length));
|
|
867
|
+
}
|
|
868
|
+
const { cols, rows } = tileGrid(width, height);
|
|
869
|
+
let tiles = null;
|
|
870
|
+
for (const [key, tile] of surface.tiles) {
|
|
871
|
+
const { tx, ty } = tileCoord(key);
|
|
872
|
+
let next = tile;
|
|
873
|
+
if (tx < 0 || ty < 0 || tx >= cols || ty >= rows) next = null;
|
|
874
|
+
else if (!insideTile(tx, ty, width, height)) next = clipTile(tile, Math.min(TILE, width - tx * TILE), Math.min(TILE, height - ty * TILE));
|
|
875
|
+
if (next === tile) continue;
|
|
876
|
+
tiles ??= new Map(surface.tiles);
|
|
877
|
+
if (next) tiles.set(key, next);
|
|
878
|
+
else tiles.delete(key);
|
|
879
|
+
}
|
|
880
|
+
return new TiledSelectionMask(width, height, tiles ? new TiledSurface(1, tiles, 0) : surface);
|
|
881
|
+
}
|
|
753
882
|
function createSelectionMask(width, height, data) {
|
|
754
883
|
checkSize(width, height);
|
|
755
|
-
|
|
756
|
-
if (
|
|
757
|
-
return
|
|
884
|
+
if (!data) return new TiledSelectionMask(width, height, new TiledSurface(1, /* @__PURE__ */ new Map(), 0), null);
|
|
885
|
+
if (data.length !== width * height) throw new Error("Selection data does not match its size");
|
|
886
|
+
return new TiledSelectionMask(width, height, surfaceFromCoverage(width, height, data), void 0, data);
|
|
887
|
+
}
|
|
888
|
+
function selectionFromRegion(width, height, rect, data) {
|
|
889
|
+
checkSize(width, height);
|
|
890
|
+
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");
|
|
891
|
+
return new TiledSelectionMask(width, height, writeRegion(new TiledSurface(1, /* @__PURE__ */ new Map(), 0), rect, data));
|
|
758
892
|
}
|
|
759
893
|
function fullSelection(width, height) {
|
|
760
894
|
checkSize(width, height);
|
|
761
|
-
|
|
895
|
+
const tiles = /* @__PURE__ */ new Map();
|
|
896
|
+
const { cols, rows } = tileGrid(width, height);
|
|
897
|
+
for (let ty = 0; ty < rows; ty++) {
|
|
898
|
+
for (let tx = 0; tx < cols; tx++) {
|
|
899
|
+
if (insideTile(tx, ty, width, height)) {
|
|
900
|
+
tiles.set(tileKey(tx, ty), uniformTile(1, 255));
|
|
901
|
+
continue;
|
|
902
|
+
}
|
|
903
|
+
const t = new Uint8ClampedArray(TILE_PIXELS);
|
|
904
|
+
const w = Math.min(TILE, width - tx * TILE);
|
|
905
|
+
for (let y = 0; y < Math.min(TILE, height - ty * TILE); y++) t.fill(255, y * TILE, y * TILE + w);
|
|
906
|
+
tiles.set(tileKey(tx, ty), { data: t, uniform: null });
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
return new TiledSelectionMask(width, height, new TiledSurface(1, tiles, 0), { x: 0, y: 0, width, height });
|
|
762
910
|
}
|
|
763
911
|
function isSelectionEmpty(mask) {
|
|
764
912
|
return !mask || mask.bounds === null;
|
|
@@ -767,7 +915,7 @@ function selectionValueAt(mask, x, y) {
|
|
|
767
915
|
const xi = Math.floor(x);
|
|
768
916
|
const yi = Math.floor(y);
|
|
769
917
|
if (xi < 0 || yi < 0 || xi >= mask.width || yi >= mask.height) return 0;
|
|
770
|
-
return mask.
|
|
918
|
+
return readPixel(mask.surface, xi, yi)[0] / 255;
|
|
771
919
|
}
|
|
772
920
|
function coverageToByte(v) {
|
|
773
921
|
if (!(v > 0)) return 0;
|
|
@@ -788,10 +936,13 @@ function selectionToCoverage(mask) {
|
|
|
788
936
|
function selectionArea(mask) {
|
|
789
937
|
if (!mask || !mask.bounds) return 0;
|
|
790
938
|
let sum = 0;
|
|
791
|
-
const
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
939
|
+
for (const tile of mask.surface.tiles.values()) {
|
|
940
|
+
if (tile.uniform !== null) {
|
|
941
|
+
sum += tile.uniform * TILE_PIXELS;
|
|
942
|
+
continue;
|
|
943
|
+
}
|
|
944
|
+
const d = tile.data;
|
|
945
|
+
for (let i = 0; i < d.length; i++) sum += d[i];
|
|
795
946
|
}
|
|
796
947
|
return sum / 255;
|
|
797
948
|
}
|
|
@@ -803,20 +954,68 @@ function combineSelections(prev, next, mode) {
|
|
|
803
954
|
return null;
|
|
804
955
|
} else {
|
|
805
956
|
if (prev.width !== next.width || prev.height !== next.height) throw new Error("Selections differ in size");
|
|
806
|
-
|
|
807
|
-
const b = next.data;
|
|
808
|
-
const out = new Uint8Array(a.length);
|
|
809
|
-
if (mode === "add") for (let i = 0; i < a.length; i++) out[i] = a[i] > b[i] ? a[i] : b[i];
|
|
810
|
-
else if (mode === "subtract") for (let i = 0; i < a.length; i++) out[i] = a[i] > b[i] ? a[i] - b[i] : 0;
|
|
811
|
-
else for (let i = 0; i < a.length; i++) out[i] = a[i] < b[i] ? a[i] : b[i];
|
|
812
|
-
result = createSelectionMask(prev.width, prev.height, out);
|
|
957
|
+
result = new TiledSelectionMask(prev.width, prev.height, combineSurfaces(prev.surface, next.surface, mode));
|
|
813
958
|
}
|
|
814
959
|
return result.bounds ? result : null;
|
|
815
960
|
}
|
|
961
|
+
var FULL = 255;
|
|
962
|
+
function combineTile(a, b, mode) {
|
|
963
|
+
if (mode === "add") {
|
|
964
|
+
if (!b || a?.uniform === FULL) return a;
|
|
965
|
+
if (!a || b.uniform === FULL) return b;
|
|
966
|
+
} else if (mode === "subtract") {
|
|
967
|
+
if (!a || !b) return a;
|
|
968
|
+
if (b.uniform === FULL) return void 0;
|
|
969
|
+
} else {
|
|
970
|
+
if (!a || !b) return void 0;
|
|
971
|
+
if (a.uniform === FULL) return b;
|
|
972
|
+
if (b.uniform === FULL) return a;
|
|
973
|
+
}
|
|
974
|
+
const x = a.data;
|
|
975
|
+
const y = b.data;
|
|
976
|
+
const out = new Uint8ClampedArray(TILE_PIXELS);
|
|
977
|
+
if (mode === "add") for (let i = 0; i < out.length; i++) out[i] = x[i] > y[i] ? x[i] : y[i];
|
|
978
|
+
else if (mode === "subtract") for (let i = 0; i < out.length; i++) out[i] = x[i] > y[i] ? x[i] - y[i] : 0;
|
|
979
|
+
else for (let i = 0; i < out.length; i++) out[i] = x[i] < y[i] ? x[i] : y[i];
|
|
980
|
+
return finishTile(out, 1, 0) ?? void 0;
|
|
981
|
+
}
|
|
982
|
+
function combineSurfaces(a, b, mode) {
|
|
983
|
+
const tiles = /* @__PURE__ */ new Map();
|
|
984
|
+
for (const key of /* @__PURE__ */ new Set([...a.tiles.keys(), ...b.tiles.keys()])) {
|
|
985
|
+
const tile = combineTile(a.tiles.get(key), b.tiles.get(key), mode);
|
|
986
|
+
if (tile) tiles.set(key, tile);
|
|
987
|
+
}
|
|
988
|
+
return new TiledSurface(1, tiles, 0);
|
|
989
|
+
}
|
|
816
990
|
function invertSelection(mask) {
|
|
817
|
-
const
|
|
818
|
-
|
|
819
|
-
|
|
991
|
+
const { width, height } = mask;
|
|
992
|
+
const tiles = /* @__PURE__ */ new Map();
|
|
993
|
+
const { cols, rows } = tileGrid(width, height);
|
|
994
|
+
for (let ty = 0; ty < rows; ty++) {
|
|
995
|
+
for (let tx = 0; tx < cols; tx++) {
|
|
996
|
+
const src = mask.surface.getTile(tx, ty);
|
|
997
|
+
const key = tileKey(tx, ty);
|
|
998
|
+
if (insideTile(tx, ty, width, height)) {
|
|
999
|
+
if (!src) tiles.set(key, uniformTile(1, FULL));
|
|
1000
|
+
else if (src.uniform !== null) {
|
|
1001
|
+
if (src.uniform !== FULL) tiles.set(key, uniformTile(1, FULL - src.uniform));
|
|
1002
|
+
} else {
|
|
1003
|
+
const out2 = new Uint8ClampedArray(TILE_PIXELS);
|
|
1004
|
+
for (let i = 0; i < out2.length; i++) out2[i] = FULL - src.data[i];
|
|
1005
|
+
const t2 = finishTile(out2, 1, 0);
|
|
1006
|
+
if (t2) tiles.set(key, t2);
|
|
1007
|
+
}
|
|
1008
|
+
continue;
|
|
1009
|
+
}
|
|
1010
|
+
const w = Math.min(TILE, width - tx * TILE);
|
|
1011
|
+
const h = Math.min(TILE, height - ty * TILE);
|
|
1012
|
+
const out = new Uint8ClampedArray(TILE_PIXELS);
|
|
1013
|
+
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);
|
|
1014
|
+
const t = finishTile(out, 1, 0);
|
|
1015
|
+
if (t) tiles.set(key, t);
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
return new TiledSelectionMask(width, height, new TiledSurface(1, tiles, 0));
|
|
820
1019
|
}
|
|
821
1020
|
function resizeSelection(mask, width, height) {
|
|
822
1021
|
if (mask.width === width && mask.height === height) return mask;
|
|
@@ -11640,7 +11839,9 @@ export {
|
|
|
11640
11839
|
isSelectionMode,
|
|
11641
11840
|
parseSelectionMode,
|
|
11642
11841
|
computeSelectionBounds,
|
|
11842
|
+
selectionFromTiles,
|
|
11643
11843
|
createSelectionMask,
|
|
11844
|
+
selectionFromRegion,
|
|
11644
11845
|
fullSelection,
|
|
11645
11846
|
isSelectionEmpty,
|
|
11646
11847
|
selectionValueAt,
|