pixel-data-js 0.17.1 → 0.19.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.
Files changed (78) hide show
  1. package/README.md +6 -1
  2. package/dist/index.dev.cjs +2747 -1397
  3. package/dist/index.dev.cjs.map +1 -1
  4. package/dist/index.dev.js +2725 -1403
  5. package/dist/index.dev.js.map +1 -1
  6. package/dist/index.prod.cjs +2747 -1397
  7. package/dist/index.prod.cjs.map +1 -1
  8. package/dist/index.prod.d.ts +401 -241
  9. package/dist/index.prod.js +2725 -1403
  10. package/dist/index.prod.js.map +1 -1
  11. package/package.json +21 -6
  12. package/src/Algorithm/forEachLinePoint.ts +36 -0
  13. package/src/BlendModes/BlendModeRegistry.ts +2 -0
  14. package/src/BlendModes/blend-modes-fast.ts +2 -2
  15. package/src/BlendModes/blend-modes-perfect.ts +5 -4
  16. package/src/BlendModes/toBlendModeIndexAndName.ts +41 -0
  17. package/src/History/PixelAccumulator.ts +2 -2
  18. package/src/History/PixelMutator/mutatorApplyAlphaMask.ts +30 -0
  19. package/src/History/PixelMutator/mutatorApplyBinaryMask.ts +30 -0
  20. package/src/History/PixelMutator/mutatorApplyCircleBrush.ts +59 -0
  21. package/src/History/PixelMutator/mutatorApplyCircleBrushStroke.ts +138 -0
  22. package/src/History/PixelMutator/mutatorApplyCirclePencil.ts +59 -0
  23. package/src/History/PixelMutator/mutatorApplyCirclePencilStroke.ts +131 -0
  24. package/src/History/PixelMutator/mutatorApplyRectBrush.ts +61 -0
  25. package/src/History/PixelMutator/mutatorApplyRectBrushStroke.ts +169 -0
  26. package/src/History/PixelMutator/mutatorApplyRectPencil.ts +62 -0
  27. package/src/History/PixelMutator/mutatorApplyRectPencilStroke.ts +149 -0
  28. package/src/History/PixelMutator/mutatorBlendColor.ts +9 -4
  29. package/src/History/PixelMutator/mutatorBlendPixelData.ts +10 -5
  30. package/src/History/PixelMutator/mutatorClear.ts +27 -0
  31. package/src/History/PixelMutator/{mutatorFillPixelData.ts → mutatorFill.ts} +9 -3
  32. package/src/History/PixelMutator/mutatorInvert.ts +10 -3
  33. package/src/History/PixelMutator.ts +27 -3
  34. package/src/History/PixelPatchTiles.ts +2 -2
  35. package/src/History/PixelWriter.ts +7 -3
  36. package/src/ImageData/ImageDataLike.ts +13 -0
  37. package/src/ImageData/ReusableImageData.ts +1 -4
  38. package/src/ImageData/extractImageDataBuffer.ts +22 -15
  39. package/src/ImageData/serialization.ts +4 -4
  40. package/src/ImageData/uInt32ArrayToImageData.ts +29 -0
  41. package/src/ImageData/writeImageData.ts +26 -18
  42. package/src/ImageData/writeImageDataBuffer.ts +30 -18
  43. package/src/IndexedImage/indexedImageToAverageColor.ts +1 -1
  44. package/src/Internal/resolveClipping.ts +140 -0
  45. package/src/Mask/applyBinaryMaskToAlphaMask.ts +89 -0
  46. package/src/Mask/copyMask.ts +1 -3
  47. package/src/Mask/mergeAlphaMasks.ts +81 -0
  48. package/src/Mask/mergeBinaryMasks.ts +89 -0
  49. package/src/PixelData/PixelBuffer32.ts +28 -0
  50. package/src/PixelData/PixelData.ts +38 -33
  51. package/src/PixelData/applyAlphaMaskToPixelData.ts +119 -0
  52. package/src/PixelData/applyBinaryMaskToPixelData.ts +111 -0
  53. package/src/PixelData/applyCircleBrushToPixelData.ts +58 -28
  54. package/src/PixelData/applyRectBrushToPixelData.ts +56 -73
  55. package/src/PixelData/blendColorPixelData.ts +18 -111
  56. package/src/PixelData/blendColorPixelDataAlphaMask.ts +111 -0
  57. package/src/PixelData/blendColorPixelDataBinaryMask.ts +89 -0
  58. package/src/PixelData/blendPixelData.ts +19 -107
  59. package/src/PixelData/blendPixelDataAlphaMask.ts +149 -0
  60. package/src/PixelData/blendPixelDataBinaryMask.ts +133 -0
  61. package/src/PixelData/clearPixelData.ts +2 -3
  62. package/src/PixelData/extractPixelData.ts +4 -4
  63. package/src/PixelData/extractPixelDataBuffer.ts +38 -26
  64. package/src/PixelData/fillPixelData.ts +18 -20
  65. package/src/PixelData/invertPixelData.ts +13 -21
  66. package/src/PixelData/pixelDataToAlphaMask.ts +2 -3
  67. package/src/PixelData/reflectPixelData.ts +3 -3
  68. package/src/PixelData/resamplePixelData.ts +2 -6
  69. package/src/PixelData/writePixelDataBuffer.ts +34 -20
  70. package/src/Rect/getCircleBrushOrPencilBounds.ts +43 -0
  71. package/src/Rect/getCircleBrushOrPencilStrokeBounds.ts +24 -0
  72. package/src/Rect/getRectBrushOrPencilBounds.ts +38 -0
  73. package/src/Rect/getRectBrushOrPencilStrokeBounds.ts +26 -0
  74. package/src/_types.ts +49 -33
  75. package/src/index.ts +47 -11
  76. package/src/History/PixelMutator/mutatorApplyMask.ts +0 -20
  77. package/src/Mask/mergeMasks.ts +0 -100
  78. package/src/PixelData/applyMaskToPixelData.ts +0 -129
package/dist/index.dev.js CHANGED
@@ -1,614 +1,235 @@
1
- // src/BlendModes/blend-modes.ts
2
- var BaseBlendMode = {
3
- overwrite: 0,
4
- sourceOver: 1,
5
- darken: 2,
6
- multiply: 3,
7
- colorBurn: 4,
8
- linearBurn: 5,
9
- darkerColor: 6,
10
- lighten: 7,
11
- screen: 8,
12
- colorDodge: 9,
13
- linearDodge: 10,
14
- lighterColor: 11,
15
- overlay: 12,
16
- softLight: 13,
17
- hardLight: 14,
18
- vividLight: 15,
19
- linearLight: 16,
20
- pinLight: 17,
21
- hardMix: 18,
22
- difference: 19,
23
- exclusion: 20,
24
- subtract: 21,
25
- divide: 22
1
+ // src/_types.ts
2
+ var MaskType = /* @__PURE__ */ ((MaskType2) => {
3
+ MaskType2[MaskType2["ALPHA"] = 0] = "ALPHA";
4
+ MaskType2[MaskType2["BINARY"] = 1] = "BINARY";
5
+ return MaskType2;
6
+ })(MaskType || {});
7
+
8
+ // src/color.ts
9
+ function packColor(r, g, b, a) {
10
+ return (a << 24 | b << 16 | g << 8 | r) >>> 0;
11
+ }
12
+ function packRGBA({
13
+ r,
14
+ g,
15
+ b,
16
+ a
17
+ }) {
18
+ return (a << 24 | b << 16 | g << 8 | r) >>> 0;
19
+ }
20
+ var unpackRed = (packed) => packed >>> 0 & 255;
21
+ var unpackGreen = (packed) => packed >>> 8 & 255;
22
+ var unpackBlue = (packed) => packed >>> 16 & 255;
23
+ var unpackAlpha = (packed) => packed >>> 24 & 255;
24
+ function unpackColor(packed) {
25
+ return {
26
+ r: packed >>> 0 & 255,
27
+ g: packed >>> 8 & 255,
28
+ b: packed >>> 16 & 255,
29
+ a: packed >>> 24 & 255
30
+ };
31
+ }
32
+ var SCRATCH_RGBA = {
33
+ r: 0,
34
+ g: 0,
35
+ b: 0,
36
+ a: 0
26
37
  };
27
- var overwriteBase = (src, _dst) => src;
28
- overwriteBase.isOverwrite = true;
38
+ function unpackColorTo(packed, scratch = SCRATCH_RGBA) {
39
+ scratch.r = packed >>> 0 & 255;
40
+ scratch.g = packed >>> 8 & 255;
41
+ scratch.b = packed >>> 16 & 255;
42
+ scratch.a = packed >>> 24 & 255;
43
+ return scratch;
44
+ }
45
+ function colorDistance(a, b) {
46
+ const dr = (a & 255) - (b & 255);
47
+ const dg = (a >>> 8 & 255) - (b >>> 8 & 255);
48
+ const db = (a >>> 16 & 255) - (b >>> 16 & 255);
49
+ const da = (a >>> 24 & 255) - (b >>> 24 & 255);
50
+ return dr * dr + dg * dg + db * db + da * da;
51
+ }
52
+ function lerpColor32(a, b, t) {
53
+ const r = (a & 255) + t * ((b & 255) - (a & 255));
54
+ const g = (a >>> 8 & 255) + t * ((b >>> 8 & 255) - (a >>> 8 & 255));
55
+ const b_ = (a >>> 16 & 255) + t * ((b >>> 16 & 255) - (a >>> 16 & 255));
56
+ const a_ = (a >>> 24 & 255) + t * ((b >>> 24 & 255) - (a >>> 24 & 255));
57
+ return (a_ << 24 | b_ << 16 | g << 8 | r) >>> 0;
58
+ }
59
+ function lerpColor32Fast(src, dst, w) {
60
+ const invA = 255 - w;
61
+ const rb = (src & 16711935) * w + (dst & 16711935) * invA >>> 8 & 16711935;
62
+ const ga = (src >>> 8 & 16711935) * w + (dst >>> 8 & 16711935) * invA >>> 8 & 16711935;
63
+ return (rb | ga << 8) >>> 0;
64
+ }
65
+ function color32ToHex(color) {
66
+ const r = (color & 255).toString(16).padStart(2, "0");
67
+ const g = (color >>> 8 & 255).toString(16).padStart(2, "0");
68
+ const b = (color >>> 16 & 255).toString(16).padStart(2, "0");
69
+ const a = (color >>> 24 & 255).toString(16).padStart(2, "0");
70
+ return `#${r}${g}${b}${a}`;
71
+ }
72
+ function color32ToCssRGBA(color) {
73
+ const r = color & 255;
74
+ const g = color >>> 8 & 255;
75
+ const b = color >>> 16 & 255;
76
+ const a = color >>> 24 & 255;
77
+ const alpha = Number((a / 255).toFixed(3));
78
+ return `rgba(${r},${g},${b},${alpha})`;
79
+ }
29
80
 
30
- // src/BlendModes/BlendModeRegistry.ts
31
- function makeBlendModeRegistry(blendModes, initialEntries) {
32
- const blendToName = /* @__PURE__ */ new Map();
33
- const blendToIndex = /* @__PURE__ */ new Map();
34
- const indexToName = [];
35
- const indexToBlend = [];
36
- const nameToBlend = {};
37
- const nameToIndex = {};
38
- const add = (name, index, blendFn) => {
39
- if (!Number.isFinite(index)) {
40
- throw new Error(`Index "${index}" is not a number. Attempting to add name: "${name}", index: "${index}"`);
81
+ // src/Internal/resolveClipping.ts
82
+ var makeClippedRect = () => ({
83
+ x: 0,
84
+ y: 0,
85
+ w: 0,
86
+ h: 0,
87
+ inBounds: false
88
+ });
89
+ var makeClippedBlit = () => ({
90
+ x: 0,
91
+ y: 0,
92
+ sx: 0,
93
+ sy: 0,
94
+ w: 0,
95
+ h: 0,
96
+ inBounds: false
97
+ });
98
+ function resolveRectClipping(x, y, w, h, boundaryW, boundaryH, out) {
99
+ if (x < 0) {
100
+ w += x;
101
+ x = 0;
102
+ }
103
+ if (y < 0) {
104
+ h += y;
105
+ y = 0;
106
+ }
107
+ const actualW = Math.min(w, boundaryW - x);
108
+ const actualH = Math.min(h, boundaryH - y);
109
+ if (actualW <= 0 || actualH <= 0) {
110
+ out.inBounds = false;
111
+ return out;
112
+ }
113
+ out.x = x;
114
+ out.y = y;
115
+ out.w = actualW;
116
+ out.h = actualH;
117
+ out.inBounds = true;
118
+ return out;
119
+ }
120
+ function resolveBlitClipping(x, y, sx, sy, w, h, dstW, dstH, srcW, srcH, out) {
121
+ if (sx < 0) {
122
+ x -= sx;
123
+ w += sx;
124
+ sx = 0;
125
+ }
126
+ if (sy < 0) {
127
+ y -= sy;
128
+ h += sy;
129
+ sy = 0;
130
+ }
131
+ w = Math.min(w, srcW - sx);
132
+ h = Math.min(h, srcH - sy);
133
+ if (x < 0) {
134
+ sx -= x;
135
+ w += x;
136
+ x = 0;
137
+ }
138
+ if (y < 0) {
139
+ sy -= y;
140
+ h += y;
141
+ y = 0;
142
+ }
143
+ const actualW = Math.min(w, dstW - x);
144
+ const actualH = Math.min(h, dstH - y);
145
+ if (actualW <= 0 || actualH <= 0) {
146
+ out.inBounds = false;
147
+ return out;
148
+ }
149
+ out.x = x;
150
+ out.y = y;
151
+ out.sx = sx;
152
+ out.sy = sy;
153
+ out.w = actualW;
154
+ out.h = actualH;
155
+ out.inBounds = true;
156
+ return out;
157
+ }
158
+
159
+ // src/ImageData/extractImageDataBuffer.ts
160
+ var SCRATCH_BLIT = makeClippedBlit();
161
+ function extractImageDataBuffer(imageData, _x, _y, _w, _h) {
162
+ const {
163
+ x,
164
+ y,
165
+ w,
166
+ h
167
+ } = typeof _x === "object" ? _x : {
168
+ x: _x,
169
+ y: _y,
170
+ w: _w,
171
+ h: _h
172
+ };
173
+ const {
174
+ width: srcW,
175
+ height: srcH,
176
+ data: src
177
+ } = imageData;
178
+ if (w <= 0 || h <= 0) return new Uint8ClampedArray(0);
179
+ const out = new Uint8ClampedArray(w * h * 4);
180
+ const clip = resolveBlitClipping(0, 0, x, y, w, h, w, h, srcW, srcH, SCRATCH_BLIT);
181
+ if (!clip.inBounds) return out;
182
+ const {
183
+ x: dstX,
184
+ y: dstY,
185
+ sx: srcX,
186
+ sy: srcY,
187
+ w: copyW,
188
+ h: copyH
189
+ } = clip;
190
+ const rowLen = copyW * 4;
191
+ for (let row = 0; row < copyH; row++) {
192
+ const srcStart = ((srcY + row) * srcW + srcX) * 4;
193
+ const dstStart = ((dstY + row) * w + dstX) * 4;
194
+ out.set(src.subarray(srcStart, srcStart + rowLen), dstStart);
195
+ }
196
+ return out;
197
+ }
198
+
199
+ // src/Mask/extractMask.ts
200
+ function extractMask(mask, maskWidth, xOrRect, y, w, h) {
201
+ let finalX;
202
+ let finalY;
203
+ let finalW;
204
+ let finalH;
205
+ if (typeof xOrRect === "object") {
206
+ finalX = xOrRect.x;
207
+ finalY = xOrRect.y;
208
+ finalW = xOrRect.w;
209
+ finalH = xOrRect.h;
210
+ } else {
211
+ finalX = xOrRect;
212
+ finalY = y;
213
+ finalW = w;
214
+ finalH = h;
215
+ }
216
+ const out = new Uint8Array(finalW * finalH);
217
+ const srcH = mask.length / maskWidth;
218
+ for (let row = 0; row < finalH; row++) {
219
+ const currentSrcY = finalY + row;
220
+ if (currentSrcY < 0 || currentSrcY >= srcH) {
221
+ continue;
41
222
  }
42
- if (indexToBlend[index]) {
43
- throw new Error(`Blend Mode index: ${index} is already used. Attempting to add name: "${name}", index: "${index}"`);
223
+ const start = Math.max(0, finalX);
224
+ const end = Math.min(maskWidth, finalX + finalW);
225
+ if (start < end) {
226
+ const srcOffset = currentSrcY * maskWidth + start;
227
+ const dstOffset = row * finalW + (start - finalX);
228
+ const count = end - start;
229
+ out.set(mask.subarray(srcOffset, srcOffset + count), dstOffset);
44
230
  }
45
- indexToName[index] = name;
46
- indexToBlend[index] = blendFn;
47
- blendToIndex.set(blendFn, index);
48
- blendToName.set(blendFn, name);
49
- nameToBlend[name] = blendFn;
50
- nameToIndex[name] = index;
51
- };
52
- for (const [name, index] of Object.entries(blendModes)) {
53
- const blend = initialEntries[index];
54
- add(name, index, blend);
55
231
  }
56
- return {
57
- nameToBlend,
58
- nameToIndex,
59
- blendToIndex,
60
- blendToName,
61
- indexToBlend,
62
- indexToName,
63
- indexType: null,
64
- nameType: null
65
- };
66
- }
67
-
68
- // src/BlendModes/blend-modes-fast.ts
69
- var overwriteFast = overwriteBase;
70
- var sourceOverFast = (src, dst) => {
71
- const sa = src >>> 24 & 255;
72
- if (sa === 255) return src;
73
- if (sa === 0) return dst;
74
- const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
75
- const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
76
- const da = dst >>> 24 & 255;
77
- const invA = 255 - sa;
78
- const r = sr * sa + dr * invA >> 8;
79
- const g = sg * sa + dg * invA >> 8;
80
- const b = sb * sa + db * invA >> 8;
81
- const a = 255 * sa + da * invA >> 8;
82
- return (a << 24 | b << 16 | g << 8 | r) >>> 0;
83
- };
84
- var darkenFast = (src, dst) => {
85
- const sa = src >>> 24 & 255;
86
- if (sa === 0) return dst;
87
- const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
88
- const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
89
- const br = sr < dr ? sr : dr;
90
- const bg = sg < dg ? sg : dg;
91
- const bb = sb < db ? sb : db;
92
- if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
93
- const invA = 255 - sa;
94
- const r = br * sa + dr * invA >> 8;
95
- const g = bg * sa + dg * invA >> 8;
96
- const b = bb * sa + db * invA >> 8;
97
- const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
98
- return (a << 24 | b << 16 | g << 8 | r) >>> 0;
99
- };
100
- var multiplyFast = (src, dst) => {
101
- const sa = src >>> 24 & 255;
102
- if (sa === 0) return dst;
103
- const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
104
- const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
105
- const br = sr * dr >> 8;
106
- const bg = sg * dg >> 8;
107
- const bb = sb * db >> 8;
108
- if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
109
- const invA = 255 - sa;
110
- const da = dst >>> 24 & 255;
111
- const r = br * sa + dr * invA >> 8;
112
- const g = bg * sa + dg * invA >> 8;
113
- const b = bb * sa + db * invA >> 8;
114
- const a = 255 * sa + da * invA >> 8;
115
- return (a << 24 | b << 16 | g << 8 | r) >>> 0;
116
- };
117
- var colorBurnFast = (src, dst) => {
118
- const sa = src >>> 24 & 255;
119
- if (sa === 0) return dst;
120
- const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
121
- const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
122
- const br = dr === 255 ? 255 : sr === 0 ? 0 : Math.max(0, 255 - (255 - dr << 8) / sr | 0);
123
- const bg = dg === 255 ? 255 : sg === 0 ? 0 : Math.max(0, 255 - (255 - dg << 8) / sg | 0);
124
- const bb = db === 255 ? 255 : sb === 0 ? 0 : Math.max(0, 255 - (255 - db << 8) / sb | 0);
125
- if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
126
- const invA = 255 - sa;
127
- const da = dst >>> 24 & 255;
128
- const r = br * sa + dr * invA >> 8;
129
- const g = bg * sa + dg * invA >> 8;
130
- const b = bb * sa + db * invA >> 8;
131
- const a = 255 * sa + da * invA >> 8;
132
- return (a << 24 | b << 16 | g << 8 | r) >>> 0;
133
- };
134
- var linearBurnFast = (src, dst) => {
135
- const sa = src >>> 24 & 255;
136
- if (sa === 0) return dst;
137
- const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
138
- const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
139
- const brU = dr + sr - 255;
140
- const bgU = dg + sg - 255;
141
- const bbU = db + sb - 255;
142
- const br = brU < 0 ? 0 : brU;
143
- const bg = bgU < 0 ? 0 : bgU;
144
- const bb = bbU < 0 ? 0 : bbU;
145
- if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
146
- const invA = 255 - sa;
147
- const r = br * sa + dr * invA >> 8;
148
- const g = bg * sa + dg * invA >> 8;
149
- const b = bb * sa + db * invA >> 8;
150
- const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
151
- return (a << 24 | b << 16 | g << 8 | r) >>> 0;
152
- };
153
- var darkerFast = (src, dst) => {
154
- const sa = src >>> 24 & 255;
155
- if (sa === 0) return dst;
156
- const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
157
- const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
158
- const lumSrc = sr * 77 + sg * 151 + sb * 28;
159
- const lumDst = dr * 77 + dg * 151 + db * 28;
160
- let br, bg, bb;
161
- if (lumSrc < lumDst) {
162
- br = sr;
163
- bg = sg;
164
- bb = sb;
165
- } else {
166
- br = dr;
167
- bg = dg;
168
- bb = db;
169
- }
170
- if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
171
- const invA = 255 - sa;
172
- const r = br * sa + dr * invA >> 8;
173
- const g = bg * sa + dg * invA >> 8;
174
- const b = bb * sa + db * invA >> 8;
175
- const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
176
- return (a << 24 | b << 16 | g << 8 | r) >>> 0;
177
- };
178
- var lightenFast = (src, dst) => {
179
- const sa = src >>> 24 & 255;
180
- if (sa === 0) return dst;
181
- const br = Math.max(src & 255, dst & 255);
182
- const bg = Math.max(src >> 8 & 255, dst >> 8 & 255);
183
- const bb = Math.max(src >> 16 & 255, dst >> 16 & 255);
184
- if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
185
- const dr = dst & 255;
186
- const dg = dst >> 8 & 255;
187
- const db = dst >> 16 & 255;
188
- const invA = 255 - sa;
189
- const r = br * sa + dr * invA >> 8;
190
- const g = bg * sa + dg * invA >> 8;
191
- const b = bb * sa + db * invA >> 8;
192
- const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
193
- return (a << 24 | b << 16 | g << 8 | r) >>> 0;
194
- };
195
- var screenFast = (src, dst) => {
196
- const sa = src >>> 24 & 255;
197
- if (sa === 0) return dst;
198
- const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
199
- const br = 255 - ((255 - (src & 255)) * (255 - dr) >> 8);
200
- const bg = 255 - ((255 - (src >>> 8 & 255)) * (255 - dg) >> 8);
201
- const bb = 255 - ((255 - (src >>> 16 & 255)) * (255 - db) >> 8);
202
- if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
203
- const invA = 255 - sa;
204
- const r = br * sa + dr * invA >> 8;
205
- const g = bg * sa + dg * invA >> 8;
206
- const b = bb * sa + db * invA >> 8;
207
- const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
208
- return (a << 24 | b << 16 | g << 8 | r) >>> 0;
209
- };
210
- var colorDodgeFast = (src, dst) => {
211
- const sa = src >>> 24 & 255;
212
- if (sa === 0) return dst;
213
- const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
214
- const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
215
- const br = sr === 255 ? 255 : Math.min(255, (dr << 8) / (255 - sr) | 0);
216
- const bg = sg === 255 ? 255 : Math.min(255, (dg << 8) / (255 - sg) | 0);
217
- const bb = sb === 255 ? 255 : Math.min(255, (db << 8) / (255 - sb) | 0);
218
- if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
219
- const invA = 255 - sa;
220
- const r = br * sa + dr * invA >> 8;
221
- const g = bg * sa + dg * invA >> 8;
222
- const b = bb * sa + db * invA >> 8;
223
- const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
224
- return (a << 24 | b << 16 | g << 8 | r) >>> 0;
225
- };
226
- var linearDodgeFast = (src, dst) => {
227
- const sa = src >>> 24 & 255;
228
- if (sa === 0) return dst;
229
- const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
230
- const brU = (src & 255) + dr;
231
- const bgU = (src >>> 8 & 255) + dg;
232
- const bbU = (src >>> 16 & 255) + db;
233
- const br = brU > 255 ? 255 : brU;
234
- const bg = bgU > 255 ? 255 : bgU;
235
- const bb = bbU > 255 ? 255 : bbU;
236
- if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
237
- const invA = 255 - sa;
238
- const r = br * sa + dr * invA >> 8;
239
- const g = bg * sa + dg * invA >> 8;
240
- const b = bb * sa + db * invA >> 8;
241
- const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
242
- return (a << 24 | b << 16 | g << 8 | r) >>> 0;
243
- };
244
- var lighterFast = (src, dst) => {
245
- const sa = src >>> 24 & 255;
246
- if (sa === 0) return dst;
247
- const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
248
- const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
249
- const lumSrc = sr * 77 + sg * 151 + sb * 28;
250
- const lumDst = dr * 77 + dg * 151 + db * 28;
251
- let br, bg, bb;
252
- if (lumSrc > lumDst) {
253
- br = sr;
254
- bg = sg;
255
- bb = sb;
256
- } else {
257
- br = dr;
258
- bg = dg;
259
- bb = db;
260
- }
261
- if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
262
- const invA = 255 - sa;
263
- const r = br * sa + dr * invA >> 8;
264
- const g = bg * sa + dg * invA >> 8;
265
- const b = bb * sa + db * invA >> 8;
266
- const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
267
- return (a << 24 | b << 16 | g << 8 | r) >>> 0;
268
- };
269
- var overlayFast = (src, dst) => {
270
- const sa = src >>> 24 & 255;
271
- if (sa === 0) return dst;
272
- const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
273
- const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
274
- const br = dr < 128 ? 2 * sr * dr >> 8 : 255 - (2 * (255 - sr) * (255 - dr) >> 8);
275
- const bg = dg < 128 ? 2 * sg * dg >> 8 : 255 - (2 * (255 - sg) * (255 - dg) >> 8);
276
- const bb = db < 128 ? 2 * sb * db >> 8 : 255 - (2 * (255 - sb) * (255 - db) >> 8);
277
- if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
278
- const invA = 255 - sa;
279
- const r = br * sa + dr * invA >> 8;
280
- const g = bg * sa + dg * invA >> 8;
281
- const b = bb * sa + db * invA >> 8;
282
- const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
283
- return (a << 24 | b << 16 | g << 8 | r) >>> 0;
284
- };
285
- var softLightFast = (src, dst) => {
286
- const sa = src >>> 24 & 255;
287
- if (sa === 0) return dst;
288
- const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
289
- const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
290
- const br = (255 - dr) * (sr * dr >> 8) + dr * (255 - ((255 - sr) * (255 - dr) >> 8)) >> 8;
291
- const bg = (255 - dg) * (sg * dg >> 8) + dg * (255 - ((255 - sg) * (255 - dg) >> 8)) >> 8;
292
- const bb = (255 - db) * (sb * db >> 8) + db * (255 - ((255 - sb) * (255 - db) >> 8)) >> 8;
293
- if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
294
- const invA = 255 - sa;
295
- const r = br * sa + dr * invA >> 8;
296
- const g = bg * sa + dg * invA >> 8;
297
- const b = bb * sa + db * invA >> 8;
298
- const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
299
- return (a << 24 | b << 16 | g << 8 | r) >>> 0;
300
- };
301
- var hardLightFast = (src, dst) => {
302
- const sa = src >>> 24 & 255;
303
- if (sa === 0) return dst;
304
- const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
305
- const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
306
- const br = sr < 128 ? 2 * sr * dr >> 8 : 255 - (2 * (255 - sr) * (255 - dr) >> 8);
307
- const bg = sg < 128 ? 2 * sg * dg >> 8 : 255 - (2 * (255 - sg) * (255 - dg) >> 8);
308
- const bb = sb < 128 ? 2 * sb * db >> 8 : 255 - (2 * (255 - sb) * (255 - db) >> 8);
309
- if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
310
- const invA = 255 - sa;
311
- const r = br * sa + dr * invA >> 8;
312
- const g = bg * sa + dg * invA >> 8;
313
- const b = bb * sa + db * invA >> 8;
314
- const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
315
- return (a << 24 | b << 16 | g << 8 | r) >>> 0;
316
- };
317
- var vividLightFast = (src, dst) => {
318
- const sa = src >>> 24 & 255;
319
- if (sa === 0) return dst;
320
- const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
321
- const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
322
- const br = sr < 128 ? sr === 0 ? 0 : Math.max(0, 255 - (255 - dr << 8) / (2 * sr) | 0) : sr === 255 ? 255 : Math.min(255, (dr << 8) / (2 * (255 - sr)) | 0);
323
- const bg = sg < 128 ? sg === 0 ? 0 : Math.max(0, 255 - (255 - dg << 8) / (2 * sg) | 0) : sg === 255 ? 255 : Math.min(255, (dg << 8) / (2 * (255 - sg)) | 0);
324
- const bb = sb < 128 ? sb === 0 ? 0 : Math.max(0, 255 - (255 - db << 8) / (2 * sb) | 0) : sb === 255 ? 255 : Math.min(255, (db << 8) / (2 * (255 - sb)) | 0);
325
- if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
326
- const invA = 255 - sa;
327
- const r = br * sa + dr * invA >> 8;
328
- const g = bg * sa + dg * invA >> 8;
329
- const b = bb * sa + db * invA >> 8;
330
- const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
331
- return (a << 24 | b << 16 | g << 8 | r) >>> 0;
332
- };
333
- var linearLightFast = (src, dst) => {
334
- const sa = src >>> 24 & 255;
335
- if (sa === 0) return dst;
336
- const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
337
- const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
338
- const brU = dr + 2 * sr - 255;
339
- const bgU = dg + 2 * sg - 255;
340
- const bbU = db + 2 * sb - 255;
341
- const br = brU < 0 ? 0 : brU > 255 ? 255 : brU;
342
- const bg = bgU < 0 ? 0 : bgU > 255 ? 255 : bgU;
343
- const bb = bbU < 0 ? 0 : bbU > 255 ? 255 : bbU;
344
- if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
345
- const invA = 255 - sa;
346
- const r = br * sa + dr * invA >> 8;
347
- const g = bg * sa + dg * invA >> 8;
348
- const b = bb * sa + db * invA >> 8;
349
- const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
350
- return (a << 24 | b << 16 | g << 8 | r) >>> 0;
351
- };
352
- var pinLightFast = (src, dst) => {
353
- const sa = src >>> 24 & 255;
354
- if (sa === 0) return dst;
355
- const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
356
- const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
357
- const br = sr < 128 ? dr < 2 * sr ? dr : 2 * sr : dr > 2 * sr - 256 ? dr : 2 * sr - 256;
358
- const bg = sg < 128 ? dg < 2 * sg ? dg : 2 * sg : dg > 2 * sg - 256 ? dg : 2 * sg - 256;
359
- const bb = sb < 128 ? db < 2 * sb ? db : 2 * sb : db > 2 * sb - 256 ? db : 2 * sb - 256;
360
- if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
361
- const invA = 255 - sa;
362
- const r = br * sa + dr * invA >> 8;
363
- const g = bg * sa + dg * invA >> 8;
364
- const b = bb * sa + db * invA >> 8;
365
- const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
366
- return (a << 24 | b << 16 | g << 8 | r) >>> 0;
367
- };
368
- var hardMixFast = (src, dst) => {
369
- const sa = src >>> 24 & 255;
370
- if (sa === 0) return dst;
371
- const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
372
- const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
373
- const br = (sr < 128 ? sr === 0 ? 0 : Math.max(0, 255 - (255 - dr << 8) / (2 * sr) | 0) : sr === 255 ? 255 : Math.min(255, (dr << 8) / (2 * (255 - sr)) | 0)) < 128 ? 0 : 255;
374
- const bg = (sg < 128 ? sg === 0 ? 0 : Math.max(0, 255 - (255 - dg << 8) / (2 * sg) | 0) : sg === 255 ? 255 : Math.min(255, (dg << 8) / (2 * (255 - sg)) | 0)) < 128 ? 0 : 255;
375
- const bb = (sb < 128 ? sb === 0 ? 0 : Math.max(0, 255 - (255 - db << 8) / (2 * sb) | 0) : sb === 255 ? 255 : Math.min(255, (db << 8) / (2 * (255 - sb)) | 0)) < 128 ? 0 : 255;
376
- if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
377
- const invA = 255 - sa;
378
- const r = br * sa + dr * invA >> 8;
379
- const g = bg * sa + dg * invA >> 8;
380
- const b = bb * sa + db * invA >> 8;
381
- const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
382
- return (a << 24 | b << 16 | g << 8 | r) >>> 0;
383
- };
384
- var differenceFast = (src, dst) => {
385
- const sa = src >>> 24 & 255;
386
- if (sa === 0) return dst;
387
- const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
388
- const brD = (src & 255) - dr;
389
- const bgD = (src >>> 8 & 255) - dg;
390
- const bbD = (src >>> 16 & 255) - db;
391
- const br = brD < 0 ? -brD : brD;
392
- const bg = bgD < 0 ? -bgD : bgD;
393
- const bb = bbD < 0 ? -bbD : bbD;
394
- if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
395
- const invA = 255 - sa;
396
- const r = br * sa + dr * invA >> 8;
397
- const g = bg * sa + dg * invA >> 8;
398
- const b = bb * sa + db * invA >> 8;
399
- const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
400
- return (a << 24 | b << 16 | g << 8 | r) >>> 0;
401
- };
402
- var exclusionFast = (src, dst) => {
403
- const sa = src >>> 24 & 255;
404
- if (sa === 0) return dst;
405
- const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
406
- const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
407
- const br = dr + sr - (dr * sr >> 7);
408
- const bg = dg + sg - (dg * sg >> 7);
409
- const bb = db + sb - (db * sb >> 7);
410
- if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
411
- const invA = 255 - sa;
412
- const r = br * sa + dr * invA >> 8;
413
- const g = bg * sa + dg * invA >> 8;
414
- const b = bb * sa + db * invA >> 8;
415
- const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
416
- return (a << 24 | b << 16 | g << 8 | r) >>> 0;
417
- };
418
- var subtractFast = (src, dst) => {
419
- const sa = src >>> 24 & 255;
420
- if (sa === 0) return dst;
421
- const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
422
- const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
423
- const brU = dr - sr;
424
- const bgU = dg - sg;
425
- const bbU = db - sb;
426
- const br = brU < 0 ? 0 : brU;
427
- const bg = bgU < 0 ? 0 : bgU;
428
- const bb = bbU < 0 ? 0 : bbU;
429
- if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
430
- const invA = 255 - sa;
431
- const r = br * sa + dr * invA >> 8;
432
- const g = bg * sa + dg * invA >> 8;
433
- const b = bb * sa + db * invA >> 8;
434
- const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
435
- return (a << 24 | b << 16 | g << 8 | r) >>> 0;
436
- };
437
- var divideFast = (src, dst) => {
438
- const sa = src >>> 24 & 255;
439
- if (sa === 0) return dst;
440
- const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
441
- const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
442
- const br = sr === 0 ? 255 : Math.min(255, (dr << 8) / sr | 0);
443
- const bg = sg === 0 ? 255 : Math.min(255, (dg << 8) / sg | 0);
444
- const bb = sb === 0 ? 255 : Math.min(255, (db << 8) / sb | 0);
445
- if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
446
- const invA = 255 - sa;
447
- const r = br * sa + dr * invA >> 8;
448
- const g = bg * sa + dg * invA >> 8;
449
- const b = bb * sa + db * invA >> 8;
450
- const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
451
- return (a << 24 | b << 16 | g << 8 | r) >>> 0;
452
- };
453
- var BASE_FAST_BLEND_MODE_FUNCTIONS = {
454
- [BaseBlendMode.overwrite]: overwriteFast,
455
- [BaseBlendMode.sourceOver]: sourceOverFast,
456
- [BaseBlendMode.darken]: darkenFast,
457
- [BaseBlendMode.multiply]: multiplyFast,
458
- [BaseBlendMode.colorBurn]: colorBurnFast,
459
- [BaseBlendMode.linearBurn]: linearBurnFast,
460
- [BaseBlendMode.darkerColor]: darkerFast,
461
- [BaseBlendMode.lighten]: lightenFast,
462
- [BaseBlendMode.screen]: screenFast,
463
- [BaseBlendMode.colorDodge]: colorDodgeFast,
464
- [BaseBlendMode.linearDodge]: linearDodgeFast,
465
- [BaseBlendMode.lighterColor]: lighterFast,
466
- [BaseBlendMode.overlay]: overlayFast,
467
- [BaseBlendMode.softLight]: softLightFast,
468
- [BaseBlendMode.hardLight]: hardLightFast,
469
- [BaseBlendMode.vividLight]: vividLightFast,
470
- [BaseBlendMode.linearLight]: linearLightFast,
471
- [BaseBlendMode.pinLight]: pinLightFast,
472
- [BaseBlendMode.hardMix]: hardMixFast,
473
- [BaseBlendMode.difference]: differenceFast,
474
- [BaseBlendMode.exclusion]: exclusionFast,
475
- [BaseBlendMode.subtract]: subtractFast,
476
- [BaseBlendMode.divide]: divideFast
477
- };
478
- function makeFastBlendModeRegistry() {
479
- return makeBlendModeRegistry(BaseBlendMode, BASE_FAST_BLEND_MODE_FUNCTIONS);
480
- }
481
-
482
- // src/_types.ts
483
- var MaskType = /* @__PURE__ */ ((MaskType2) => {
484
- MaskType2[MaskType2["ALPHA"] = 0] = "ALPHA";
485
- MaskType2[MaskType2["BINARY"] = 1] = "BINARY";
486
- return MaskType2;
487
- })(MaskType || {});
488
-
489
- // src/color.ts
490
- function packColor(r, g, b, a) {
491
- return (a << 24 | b << 16 | g << 8 | r) >>> 0;
492
- }
493
- function packRGBA({ r, g, b, a }) {
494
- return (a << 24 | b << 16 | g << 8 | r) >>> 0;
495
- }
496
- var unpackRed = (packed) => packed >>> 0 & 255;
497
- var unpackGreen = (packed) => packed >>> 8 & 255;
498
- var unpackBlue = (packed) => packed >>> 16 & 255;
499
- var unpackAlpha = (packed) => packed >>> 24 & 255;
500
- function unpackColor(packed) {
501
- return {
502
- r: packed >>> 0 & 255,
503
- g: packed >>> 8 & 255,
504
- b: packed >>> 16 & 255,
505
- a: packed >>> 24 & 255
506
- };
507
- }
508
- var SCRATCH_RGBA = { r: 0, g: 0, b: 0, a: 0 };
509
- function unpackColorTo(packed, scratch = SCRATCH_RGBA) {
510
- scratch.r = packed >>> 0 & 255;
511
- scratch.g = packed >>> 8 & 255;
512
- scratch.b = packed >>> 16 & 255;
513
- scratch.a = packed >>> 24 & 255;
514
- return scratch;
515
- }
516
- function colorDistance(a, b) {
517
- const dr = (a & 255) - (b & 255);
518
- const dg = (a >>> 8 & 255) - (b >>> 8 & 255);
519
- const db = (a >>> 16 & 255) - (b >>> 16 & 255);
520
- const da = (a >>> 24 & 255) - (b >>> 24 & 255);
521
- return dr * dr + dg * dg + db * db + da * da;
522
- }
523
- function lerpColor32(a, b, t) {
524
- const r = (a & 255) + t * ((b & 255) - (a & 255));
525
- const g = (a >>> 8 & 255) + t * ((b >>> 8 & 255) - (a >>> 8 & 255));
526
- const b_ = (a >>> 16 & 255) + t * ((b >>> 16 & 255) - (a >>> 16 & 255));
527
- const a_ = (a >>> 24 & 255) + t * ((b >>> 24 & 255) - (a >>> 24 & 255));
528
- return (a_ << 24 | b_ << 16 | g << 8 | r) >>> 0;
529
- }
530
- function lerpColor32Fast(src, dst, w) {
531
- const invA = 255 - w;
532
- const rb = (src & 16711935) * w + (dst & 16711935) * invA >>> 8 & 16711935;
533
- const ga = (src >>> 8 & 16711935) * w + (dst >>> 8 & 16711935) * invA >>> 8 & 16711935;
534
- return (rb | ga << 8) >>> 0;
535
- }
536
- function color32ToHex(color) {
537
- const r = (color & 255).toString(16).padStart(2, "0");
538
- const g = (color >>> 8 & 255).toString(16).padStart(2, "0");
539
- const b = (color >>> 16 & 255).toString(16).padStart(2, "0");
540
- const a = (color >>> 24 & 255).toString(16).padStart(2, "0");
541
- return `#${r}${g}${b}${a}`;
542
- }
543
- function color32ToCssRGBA(color) {
544
- const r = color & 255;
545
- const g = color >>> 8 & 255;
546
- const b = color >>> 16 & 255;
547
- const a = color >>> 24 & 255;
548
- const alpha = Number((a / 255).toFixed(3));
549
- return `rgba(${r},${g},${b},${alpha})`;
550
- }
551
-
552
- // src/ImageData/extractImageDataBuffer.ts
553
- function extractImageDataBuffer(imageData, _x, _y, _w, _h) {
554
- const { x, y, w, h } = typeof _x === "object" ? _x : { x: _x, y: _y, w: _w, h: _h };
555
- const { width: srcW, height: srcH, data: src } = imageData;
556
- if (w <= 0 || h <= 0) return new Uint8ClampedArray(0);
557
- const out = new Uint8ClampedArray(w * h * 4);
558
- const x0 = Math.max(0, x);
559
- const y0 = Math.max(0, y);
560
- const x1 = Math.min(srcW, x + w);
561
- const y1 = Math.min(srcH, y + h);
562
- if (x1 <= x0 || y1 <= y0) return out;
563
- for (let row = 0; row < y1 - y0; row++) {
564
- const srcRow = y0 + row;
565
- const srcStart = (srcRow * srcW + x0) * 4;
566
- const rowLen = (x1 - x0) * 4;
567
- const dstRow = y0 - y + row;
568
- const dstCol = x0 - x;
569
- const dstStart = (dstRow * w + dstCol) * 4;
570
- out.set(src.subarray(srcStart, srcStart + rowLen), dstStart);
571
- }
572
- return out;
573
- }
574
-
575
- // src/Mask/extractMask.ts
576
- function extractMask(mask, maskWidth, xOrRect, y, w, h) {
577
- let finalX;
578
- let finalY;
579
- let finalW;
580
- let finalH;
581
- if (typeof xOrRect === "object") {
582
- finalX = xOrRect.x;
583
- finalY = xOrRect.y;
584
- finalW = xOrRect.w;
585
- finalH = xOrRect.h;
586
- } else {
587
- finalX = xOrRect;
588
- finalY = y;
589
- finalW = w;
590
- finalH = h;
591
- }
592
- const out = new Uint8Array(finalW * finalH);
593
- const srcH = mask.length / maskWidth;
594
- for (let row = 0; row < finalH; row++) {
595
- const currentSrcY = finalY + row;
596
- if (currentSrcY < 0 || currentSrcY >= srcH) {
597
- continue;
598
- }
599
- const start = Math.max(0, finalX);
600
- const end = Math.min(maskWidth, finalX + finalW);
601
- if (start < end) {
602
- const srcOffset = currentSrcY * maskWidth + start;
603
- const dstOffset = row * finalW + (start - finalX);
604
- const count = end - start;
605
- out.set(
606
- mask.subarray(srcOffset, srcOffset + count),
607
- dstOffset
608
- );
609
- }
610
- }
611
- return out;
232
+ return out;
612
233
  }
613
234
 
614
235
  // src/Rect/trimRectBounds.ts
@@ -618,14 +239,8 @@ function trimRectBounds(target, bounds) {
618
239
  const originalW = target.w;
619
240
  const intersectedX = Math.max(target.x, bounds.x);
620
241
  const intersectedY = Math.max(target.y, bounds.y);
621
- const intersectedMaxX = Math.min(
622
- target.x + target.w,
623
- bounds.x + bounds.w
624
- );
625
- const intersectedMaxY = Math.min(
626
- target.y + target.h,
627
- bounds.y + bounds.h
628
- );
242
+ const intersectedMaxX = Math.min(target.x + target.w, bounds.x + bounds.w);
243
+ const intersectedMaxY = Math.min(target.y + target.h, bounds.y + bounds.h);
629
244
  if (intersectedMaxX <= intersectedX || intersectedMaxY <= intersectedY) {
630
245
  target.w = 0;
631
246
  target.h = 0;
@@ -643,14 +258,7 @@ function trimRectBounds(target, bounds) {
643
258
  target.w = intersectedW;
644
259
  target.h = intersectedH;
645
260
  if ("mask" in target && target.mask) {
646
- const currentMask = extractMask(
647
- target.mask,
648
- originalW,
649
- offsetX,
650
- offsetY,
651
- intersectedW,
652
- intersectedH
653
- );
261
+ const currentMask = extractMask(target.mask, originalW, offsetX, offsetY, intersectedW, intersectedH);
654
262
  let minX = intersectedW;
655
263
  let maxX = -1;
656
264
  let minY = intersectedH;
@@ -674,14 +282,7 @@ function trimRectBounds(target, bounds) {
674
282
  const finalW = maxX - minX + 1;
675
283
  const finalH = maxY - minY + 1;
676
284
  if (finalW !== intersectedW || finalH !== intersectedH) {
677
- target.mask = extractMask(
678
- currentMask,
679
- intersectedW,
680
- minX,
681
- minY,
682
- finalW,
683
- finalH
684
- );
285
+ target.mask = extractMask(currentMask, intersectedW, minX, minY, finalW, finalH);
685
286
  target.x += minX;
686
287
  target.y += minY;
687
288
  target.w = finalW;
@@ -704,11 +305,7 @@ function floodFillSelection(img, startX, startY, {
704
305
  data32 = img.data32;
705
306
  imageData = img.imageData;
706
307
  } else {
707
- data32 = new Uint32Array(
708
- img.data.buffer,
709
- img.data.byteOffset,
710
- img.data.byteLength >> 2
711
- );
308
+ data32 = new Uint32Array(img.data.buffer, img.data.byteOffset, img.data.byteLength >> 2);
712
309
  imageData = img;
713
310
  }
714
311
  const {
@@ -783,59 +380,537 @@ function floodFillSelection(img, startX, startY, {
783
380
  }
784
381
  }
785
382
  } else {
786
- for (let y = yMin; y <= yMax; y++) {
787
- for (let x = xMin; x <= xMax; x++) {
788
- const color = data32[y * width + x];
789
- if (colorDistance(color, baseColor) <= tolerance) {
790
- matchX[matchCount] = x;
791
- matchY[matchCount] = y;
792
- matchCount++;
793
- if (x < minX) minX = x;
794
- if (x > maxX) maxX = x;
795
- if (y < minY) minY = y;
796
- if (y > maxY) maxY = y;
797
- }
798
- }
799
- }
800
- }
801
- if (matchCount === 0) {
802
- return null;
383
+ for (let y = yMin; y <= yMax; y++) {
384
+ for (let x = xMin; x <= xMax; x++) {
385
+ const color = data32[y * width + x];
386
+ if (colorDistance(color, baseColor) <= tolerance) {
387
+ matchX[matchCount] = x;
388
+ matchY[matchCount] = y;
389
+ matchCount++;
390
+ if (x < minX) minX = x;
391
+ if (x > maxX) maxX = x;
392
+ if (y < minY) minY = y;
393
+ if (y > maxY) maxY = y;
394
+ }
395
+ }
396
+ }
397
+ }
398
+ if (matchCount === 0) {
399
+ return null;
400
+ }
401
+ const selectionRect = {
402
+ x: minX,
403
+ y: minY,
404
+ w: maxX - minX + 1,
405
+ h: maxY - minY + 1,
406
+ mask: new Uint8Array((maxX - minX + 1) * (maxY - minY + 1)),
407
+ maskType: 1 /* BINARY */
408
+ };
409
+ const sw = selectionRect.w;
410
+ const sh = selectionRect.h;
411
+ const finalMask = selectionRect.mask;
412
+ for (let i = 0; i < matchCount; i++) {
413
+ const mx = matchX[i] - selectionRect.x;
414
+ const my = matchY[i] - selectionRect.y;
415
+ if (mx >= 0 && mx < sw && my >= 0 && my < sh) {
416
+ finalMask[my * sw + mx] = 1;
417
+ }
418
+ }
419
+ trimRectBounds(selectionRect, {
420
+ x: 0,
421
+ y: 0,
422
+ w: width,
423
+ h: height
424
+ });
425
+ const extracted = extractImageDataBuffer(imageData, selectionRect.x, selectionRect.y, selectionRect.w, selectionRect.h);
426
+ return {
427
+ startX,
428
+ startY,
429
+ selectionRect,
430
+ pixels: extracted
431
+ };
432
+ }
433
+
434
+ // src/BlendModes/blend-modes.ts
435
+ var BaseBlendMode = {
436
+ overwrite: 0,
437
+ sourceOver: 1,
438
+ darken: 2,
439
+ multiply: 3,
440
+ colorBurn: 4,
441
+ linearBurn: 5,
442
+ darkerColor: 6,
443
+ lighten: 7,
444
+ screen: 8,
445
+ colorDodge: 9,
446
+ linearDodge: 10,
447
+ lighterColor: 11,
448
+ overlay: 12,
449
+ softLight: 13,
450
+ hardLight: 14,
451
+ vividLight: 15,
452
+ linearLight: 16,
453
+ pinLight: 17,
454
+ hardMix: 18,
455
+ difference: 19,
456
+ exclusion: 20,
457
+ subtract: 21,
458
+ divide: 22
459
+ };
460
+ var overwriteBase = (src, _dst) => src;
461
+ overwriteBase.isOverwrite = true;
462
+
463
+ // src/BlendModes/BlendModeRegistry.ts
464
+ function makeBlendModeRegistry(blendModes, initialEntries, registryName = "anonymous") {
465
+ const blendToName = /* @__PURE__ */ new Map();
466
+ const blendToIndex = /* @__PURE__ */ new Map();
467
+ const indexToName = [];
468
+ const indexToBlend = [];
469
+ const nameToBlend = {};
470
+ const nameToIndex = {};
471
+ const add = (name, index, blendFn) => {
472
+ if (!Number.isFinite(index)) {
473
+ throw new Error(`Index "${index}" is not a number. Attempting to add name: "${name}", index: "${index}"`);
474
+ }
475
+ if (indexToBlend[index]) {
476
+ throw new Error(`Blend Mode index: ${index} is already used. Attempting to add name: "${name}", index: "${index}"`);
477
+ }
478
+ indexToName[index] = name;
479
+ indexToBlend[index] = blendFn;
480
+ blendToIndex.set(blendFn, index);
481
+ blendToName.set(blendFn, name);
482
+ nameToBlend[name] = blendFn;
483
+ nameToIndex[name] = index;
484
+ };
485
+ for (const [name, index] of Object.entries(blendModes)) {
486
+ const blend = initialEntries[index];
487
+ add(name, index, blend);
488
+ }
489
+ return {
490
+ registryName,
491
+ nameToBlend,
492
+ nameToIndex,
493
+ blendToIndex,
494
+ blendToName,
495
+ indexToBlend,
496
+ indexToName,
497
+ indexType: null,
498
+ nameType: null
499
+ };
500
+ }
501
+
502
+ // src/BlendModes/blend-modes-fast.ts
503
+ var overwriteFast = overwriteBase;
504
+ var sourceOverFast = (src, dst) => {
505
+ const sa = src >>> 24 & 255;
506
+ if (sa === 255) return src;
507
+ if (sa === 0) return dst;
508
+ const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
509
+ const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
510
+ const da = dst >>> 24 & 255;
511
+ const invA = 255 - sa;
512
+ const r = sr * sa + dr * invA >> 8;
513
+ const g = sg * sa + dg * invA >> 8;
514
+ const b = sb * sa + db * invA >> 8;
515
+ const a = 255 * sa + da * invA >> 8;
516
+ return (a << 24 | b << 16 | g << 8 | r) >>> 0;
517
+ };
518
+ var darkenFast = (src, dst) => {
519
+ const sa = src >>> 24 & 255;
520
+ if (sa === 0) return dst;
521
+ const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
522
+ const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
523
+ const br = sr < dr ? sr : dr;
524
+ const bg = sg < dg ? sg : dg;
525
+ const bb = sb < db ? sb : db;
526
+ if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
527
+ const invA = 255 - sa;
528
+ const r = br * sa + dr * invA >> 8;
529
+ const g = bg * sa + dg * invA >> 8;
530
+ const b = bb * sa + db * invA >> 8;
531
+ const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
532
+ return (a << 24 | b << 16 | g << 8 | r) >>> 0;
533
+ };
534
+ var multiplyFast = (src, dst) => {
535
+ const sa = src >>> 24 & 255;
536
+ if (sa === 0) return dst;
537
+ const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
538
+ const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
539
+ const br = sr * dr >> 8;
540
+ const bg = sg * dg >> 8;
541
+ const bb = sb * db >> 8;
542
+ if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
543
+ const invA = 255 - sa;
544
+ const da = dst >>> 24 & 255;
545
+ const r = br * sa + dr * invA >> 8;
546
+ const g = bg * sa + dg * invA >> 8;
547
+ const b = bb * sa + db * invA >> 8;
548
+ const a = 255 * sa + da * invA >> 8;
549
+ return (a << 24 | b << 16 | g << 8 | r) >>> 0;
550
+ };
551
+ var colorBurnFast = (src, dst) => {
552
+ const sa = src >>> 24 & 255;
553
+ if (sa === 0) return dst;
554
+ const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
555
+ const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
556
+ const br = dr === 255 ? 255 : sr === 0 ? 0 : Math.max(0, 255 - (255 - dr << 8) / sr | 0);
557
+ const bg = dg === 255 ? 255 : sg === 0 ? 0 : Math.max(0, 255 - (255 - dg << 8) / sg | 0);
558
+ const bb = db === 255 ? 255 : sb === 0 ? 0 : Math.max(0, 255 - (255 - db << 8) / sb | 0);
559
+ if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
560
+ const invA = 255 - sa;
561
+ const da = dst >>> 24 & 255;
562
+ const r = br * sa + dr * invA >> 8;
563
+ const g = bg * sa + dg * invA >> 8;
564
+ const b = bb * sa + db * invA >> 8;
565
+ const a = 255 * sa + da * invA >> 8;
566
+ return (a << 24 | b << 16 | g << 8 | r) >>> 0;
567
+ };
568
+ var linearBurnFast = (src, dst) => {
569
+ const sa = src >>> 24 & 255;
570
+ if (sa === 0) return dst;
571
+ const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
572
+ const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
573
+ const brU = dr + sr - 255;
574
+ const bgU = dg + sg - 255;
575
+ const bbU = db + sb - 255;
576
+ const br = brU < 0 ? 0 : brU;
577
+ const bg = bgU < 0 ? 0 : bgU;
578
+ const bb = bbU < 0 ? 0 : bbU;
579
+ if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
580
+ const invA = 255 - sa;
581
+ const r = br * sa + dr * invA >> 8;
582
+ const g = bg * sa + dg * invA >> 8;
583
+ const b = bb * sa + db * invA >> 8;
584
+ const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
585
+ return (a << 24 | b << 16 | g << 8 | r) >>> 0;
586
+ };
587
+ var darkerFast = (src, dst) => {
588
+ const sa = src >>> 24 & 255;
589
+ if (sa === 0) return dst;
590
+ const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
591
+ const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
592
+ const lumSrc = sr * 77 + sg * 151 + sb * 28;
593
+ const lumDst = dr * 77 + dg * 151 + db * 28;
594
+ let br, bg, bb;
595
+ if (lumSrc < lumDst) {
596
+ br = sr;
597
+ bg = sg;
598
+ bb = sb;
599
+ } else {
600
+ br = dr;
601
+ bg = dg;
602
+ bb = db;
803
603
  }
804
- const selectionRect = {
805
- x: minX,
806
- y: minY,
807
- w: maxX - minX + 1,
808
- h: maxY - minY + 1,
809
- mask: new Uint8Array((maxX - minX + 1) * (maxY - minY + 1)),
810
- maskType: 1 /* BINARY */
811
- };
812
- const sw = selectionRect.w;
813
- const sh = selectionRect.h;
814
- const finalMask = selectionRect.mask;
815
- for (let i = 0; i < matchCount; i++) {
816
- const mx = matchX[i] - selectionRect.x;
817
- const my = matchY[i] - selectionRect.y;
818
- if (mx >= 0 && mx < sw && my >= 0 && my < sh) {
819
- finalMask[my * sw + mx] = 1;
820
- }
604
+ if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
605
+ const invA = 255 - sa;
606
+ const r = br * sa + dr * invA >> 8;
607
+ const g = bg * sa + dg * invA >> 8;
608
+ const b = bb * sa + db * invA >> 8;
609
+ const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
610
+ return (a << 24 | b << 16 | g << 8 | r) >>> 0;
611
+ };
612
+ var lightenFast = (src, dst) => {
613
+ const sa = src >>> 24 & 255;
614
+ if (sa === 0) return dst;
615
+ const br = Math.max(src & 255, dst & 255);
616
+ const bg = Math.max(src >> 8 & 255, dst >> 8 & 255);
617
+ const bb = Math.max(src >> 16 & 255, dst >> 16 & 255);
618
+ if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
619
+ const dr = dst & 255;
620
+ const dg = dst >> 8 & 255;
621
+ const db = dst >> 16 & 255;
622
+ const invA = 255 - sa;
623
+ const r = br * sa + dr * invA >> 8;
624
+ const g = bg * sa + dg * invA >> 8;
625
+ const b = bb * sa + db * invA >> 8;
626
+ const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
627
+ return (a << 24 | b << 16 | g << 8 | r) >>> 0;
628
+ };
629
+ var screenFast = (src, dst) => {
630
+ const sa = src >>> 24 & 255;
631
+ if (sa === 0) return dst;
632
+ const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
633
+ const br = 255 - ((255 - (src & 255)) * (255 - dr) >> 8);
634
+ const bg = 255 - ((255 - (src >>> 8 & 255)) * (255 - dg) >> 8);
635
+ const bb = 255 - ((255 - (src >>> 16 & 255)) * (255 - db) >> 8);
636
+ if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
637
+ const invA = 255 - sa;
638
+ const r = br * sa + dr * invA >> 8;
639
+ const g = bg * sa + dg * invA >> 8;
640
+ const b = bb * sa + db * invA >> 8;
641
+ const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
642
+ return (a << 24 | b << 16 | g << 8 | r) >>> 0;
643
+ };
644
+ var colorDodgeFast = (src, dst) => {
645
+ const sa = src >>> 24 & 255;
646
+ if (sa === 0) return dst;
647
+ const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
648
+ const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
649
+ const br = sr === 255 ? 255 : Math.min(255, (dr << 8) / (255 - sr) | 0);
650
+ const bg = sg === 255 ? 255 : Math.min(255, (dg << 8) / (255 - sg) | 0);
651
+ const bb = sb === 255 ? 255 : Math.min(255, (db << 8) / (255 - sb) | 0);
652
+ if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
653
+ const invA = 255 - sa;
654
+ const r = br * sa + dr * invA >> 8;
655
+ const g = bg * sa + dg * invA >> 8;
656
+ const b = bb * sa + db * invA >> 8;
657
+ const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
658
+ return (a << 24 | b << 16 | g << 8 | r) >>> 0;
659
+ };
660
+ var linearDodgeFast = (src, dst) => {
661
+ const sa = src >>> 24 & 255;
662
+ if (sa === 0) return dst;
663
+ const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
664
+ const brU = (src & 255) + dr;
665
+ const bgU = (src >>> 8 & 255) + dg;
666
+ const bbU = (src >>> 16 & 255) + db;
667
+ const br = brU > 255 ? 255 : brU;
668
+ const bg = bgU > 255 ? 255 : bgU;
669
+ const bb = bbU > 255 ? 255 : bbU;
670
+ if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
671
+ const invA = 255 - sa;
672
+ const r = br * sa + dr * invA >> 8;
673
+ const g = bg * sa + dg * invA >> 8;
674
+ const b = bb * sa + db * invA >> 8;
675
+ const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
676
+ return (a << 24 | b << 16 | g << 8 | r) >>> 0;
677
+ };
678
+ var lighterFast = (src, dst) => {
679
+ const sa = src >>> 24 & 255;
680
+ if (sa === 0) return dst;
681
+ const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
682
+ const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
683
+ const lumSrc = sr * 77 + sg * 151 + sb * 28;
684
+ const lumDst = dr * 77 + dg * 151 + db * 28;
685
+ let br, bg, bb;
686
+ if (lumSrc > lumDst) {
687
+ br = sr;
688
+ bg = sg;
689
+ bb = sb;
690
+ } else {
691
+ br = dr;
692
+ bg = dg;
693
+ bb = db;
821
694
  }
822
- trimRectBounds(
823
- selectionRect,
824
- { x: 0, y: 0, w: width, h: height }
825
- );
826
- const extracted = extractImageDataBuffer(
827
- imageData,
828
- selectionRect.x,
829
- selectionRect.y,
830
- selectionRect.w,
831
- selectionRect.h
832
- );
833
- return {
834
- startX,
835
- startY,
836
- selectionRect,
837
- pixels: extracted
838
- };
695
+ if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
696
+ const invA = 255 - sa;
697
+ const r = br * sa + dr * invA >> 8;
698
+ const g = bg * sa + dg * invA >> 8;
699
+ const b = bb * sa + db * invA >> 8;
700
+ const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
701
+ return (a << 24 | b << 16 | g << 8 | r) >>> 0;
702
+ };
703
+ var overlayFast = (src, dst) => {
704
+ const sa = src >>> 24 & 255;
705
+ if (sa === 0) return dst;
706
+ const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
707
+ const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
708
+ const br = dr < 128 ? 2 * sr * dr >> 8 : 255 - (2 * (255 - sr) * (255 - dr) >> 8);
709
+ const bg = dg < 128 ? 2 * sg * dg >> 8 : 255 - (2 * (255 - sg) * (255 - dg) >> 8);
710
+ const bb = db < 128 ? 2 * sb * db >> 8 : 255 - (2 * (255 - sb) * (255 - db) >> 8);
711
+ if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
712
+ const invA = 255 - sa;
713
+ const r = br * sa + dr * invA >> 8;
714
+ const g = bg * sa + dg * invA >> 8;
715
+ const b = bb * sa + db * invA >> 8;
716
+ const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
717
+ return (a << 24 | b << 16 | g << 8 | r) >>> 0;
718
+ };
719
+ var softLightFast = (src, dst) => {
720
+ const sa = src >>> 24 & 255;
721
+ if (sa === 0) return dst;
722
+ const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
723
+ const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
724
+ const br = (255 - dr) * (sr * dr >> 8) + dr * (255 - ((255 - sr) * (255 - dr) >> 8)) >> 8;
725
+ const bg = (255 - dg) * (sg * dg >> 8) + dg * (255 - ((255 - sg) * (255 - dg) >> 8)) >> 8;
726
+ const bb = (255 - db) * (sb * db >> 8) + db * (255 - ((255 - sb) * (255 - db) >> 8)) >> 8;
727
+ if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
728
+ const invA = 255 - sa;
729
+ const r = br * sa + dr * invA >> 8;
730
+ const g = bg * sa + dg * invA >> 8;
731
+ const b = bb * sa + db * invA >> 8;
732
+ const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
733
+ return (a << 24 | b << 16 | g << 8 | r) >>> 0;
734
+ };
735
+ var hardLightFast = (src, dst) => {
736
+ const sa = src >>> 24 & 255;
737
+ if (sa === 0) return dst;
738
+ const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
739
+ const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
740
+ const br = sr < 128 ? 2 * sr * dr >> 8 : 255 - (2 * (255 - sr) * (255 - dr) >> 8);
741
+ const bg = sg < 128 ? 2 * sg * dg >> 8 : 255 - (2 * (255 - sg) * (255 - dg) >> 8);
742
+ const bb = sb < 128 ? 2 * sb * db >> 8 : 255 - (2 * (255 - sb) * (255 - db) >> 8);
743
+ if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
744
+ const invA = 255 - sa;
745
+ const r = br * sa + dr * invA >> 8;
746
+ const g = bg * sa + dg * invA >> 8;
747
+ const b = bb * sa + db * invA >> 8;
748
+ const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
749
+ return (a << 24 | b << 16 | g << 8 | r) >>> 0;
750
+ };
751
+ var vividLightFast = (src, dst) => {
752
+ const sa = src >>> 24 & 255;
753
+ if (sa === 0) return dst;
754
+ const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
755
+ const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
756
+ const br = sr < 128 ? sr === 0 ? 0 : Math.max(0, 255 - (255 - dr << 8) / (2 * sr) | 0) : sr === 255 ? 255 : Math.min(255, (dr << 8) / (2 * (255 - sr)) | 0);
757
+ const bg = sg < 128 ? sg === 0 ? 0 : Math.max(0, 255 - (255 - dg << 8) / (2 * sg) | 0) : sg === 255 ? 255 : Math.min(255, (dg << 8) / (2 * (255 - sg)) | 0);
758
+ const bb = sb < 128 ? sb === 0 ? 0 : Math.max(0, 255 - (255 - db << 8) / (2 * sb) | 0) : sb === 255 ? 255 : Math.min(255, (db << 8) / (2 * (255 - sb)) | 0);
759
+ if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
760
+ const invA = 255 - sa;
761
+ const r = br * sa + dr * invA >> 8;
762
+ const g = bg * sa + dg * invA >> 8;
763
+ const b = bb * sa + db * invA >> 8;
764
+ const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
765
+ return (a << 24 | b << 16 | g << 8 | r) >>> 0;
766
+ };
767
+ var linearLightFast = (src, dst) => {
768
+ const sa = src >>> 24 & 255;
769
+ if (sa === 0) return dst;
770
+ const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
771
+ const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
772
+ const brU = dr + 2 * sr - 255;
773
+ const bgU = dg + 2 * sg - 255;
774
+ const bbU = db + 2 * sb - 255;
775
+ const br = brU < 0 ? 0 : brU > 255 ? 255 : brU;
776
+ const bg = bgU < 0 ? 0 : bgU > 255 ? 255 : bgU;
777
+ const bb = bbU < 0 ? 0 : bbU > 255 ? 255 : bbU;
778
+ if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
779
+ const invA = 255 - sa;
780
+ const r = br * sa + dr * invA >> 8;
781
+ const g = bg * sa + dg * invA >> 8;
782
+ const b = bb * sa + db * invA >> 8;
783
+ const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
784
+ return (a << 24 | b << 16 | g << 8 | r) >>> 0;
785
+ };
786
+ var pinLightFast = (src, dst) => {
787
+ const sa = src >>> 24 & 255;
788
+ if (sa === 0) return dst;
789
+ const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
790
+ const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
791
+ const br = sr < 128 ? dr < 2 * sr ? dr : 2 * sr : dr > 2 * sr - 256 ? dr : 2 * sr - 256;
792
+ const bg = sg < 128 ? dg < 2 * sg ? dg : 2 * sg : dg > 2 * sg - 256 ? dg : 2 * sg - 256;
793
+ const bb = sb < 128 ? db < 2 * sb ? db : 2 * sb : db > 2 * sb - 256 ? db : 2 * sb - 256;
794
+ if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
795
+ const invA = 255 - sa;
796
+ const r = br * sa + dr * invA >> 8;
797
+ const g = bg * sa + dg * invA >> 8;
798
+ const b = bb * sa + db * invA >> 8;
799
+ const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
800
+ return (a << 24 | b << 16 | g << 8 | r) >>> 0;
801
+ };
802
+ var hardMixFast = (src, dst) => {
803
+ const sa = src >>> 24 & 255;
804
+ if (sa === 0) return dst;
805
+ const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
806
+ const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
807
+ const br = (sr < 128 ? sr === 0 ? 0 : Math.max(0, 255 - (255 - dr << 8) / (2 * sr) | 0) : sr === 255 ? 255 : Math.min(255, (dr << 8) / (2 * (255 - sr)) | 0)) < 128 ? 0 : 255;
808
+ const bg = (sg < 128 ? sg === 0 ? 0 : Math.max(0, 255 - (255 - dg << 8) / (2 * sg) | 0) : sg === 255 ? 255 : Math.min(255, (dg << 8) / (2 * (255 - sg)) | 0)) < 128 ? 0 : 255;
809
+ const bb = (sb < 128 ? sb === 0 ? 0 : Math.max(0, 255 - (255 - db << 8) / (2 * sb) | 0) : sb === 255 ? 255 : Math.min(255, (db << 8) / (2 * (255 - sb)) | 0)) < 128 ? 0 : 255;
810
+ if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
811
+ const invA = 255 - sa;
812
+ const r = br * sa + dr * invA >> 8;
813
+ const g = bg * sa + dg * invA >> 8;
814
+ const b = bb * sa + db * invA >> 8;
815
+ const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
816
+ return (a << 24 | b << 16 | g << 8 | r) >>> 0;
817
+ };
818
+ var differenceFast = (src, dst) => {
819
+ const sa = src >>> 24 & 255;
820
+ if (sa === 0) return dst;
821
+ const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
822
+ const brD = (src & 255) - dr;
823
+ const bgD = (src >>> 8 & 255) - dg;
824
+ const bbD = (src >>> 16 & 255) - db;
825
+ const br = brD < 0 ? -brD : brD;
826
+ const bg = bgD < 0 ? -bgD : bgD;
827
+ const bb = bbD < 0 ? -bbD : bbD;
828
+ if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
829
+ const invA = 255 - sa;
830
+ const r = br * sa + dr * invA >> 8;
831
+ const g = bg * sa + dg * invA >> 8;
832
+ const b = bb * sa + db * invA >> 8;
833
+ const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
834
+ return (a << 24 | b << 16 | g << 8 | r) >>> 0;
835
+ };
836
+ var exclusionFast = (src, dst) => {
837
+ const sa = src >>> 24 & 255;
838
+ if (sa === 0) return dst;
839
+ const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
840
+ const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
841
+ const br = dr + sr - (dr * sr >> 7);
842
+ const bg = dg + sg - (dg * sg >> 7);
843
+ const bb = db + sb - (db * sb >> 7);
844
+ if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
845
+ const invA = 255 - sa;
846
+ const r = br * sa + dr * invA >> 8;
847
+ const g = bg * sa + dg * invA >> 8;
848
+ const b = bb * sa + db * invA >> 8;
849
+ const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
850
+ return (a << 24 | b << 16 | g << 8 | r) >>> 0;
851
+ };
852
+ var subtractFast = (src, dst) => {
853
+ const sa = src >>> 24 & 255;
854
+ if (sa === 0) return dst;
855
+ const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
856
+ const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
857
+ const brU = dr - sr;
858
+ const bgU = dg - sg;
859
+ const bbU = db - sb;
860
+ const br = brU < 0 ? 0 : brU;
861
+ const bg = bgU < 0 ? 0 : bgU;
862
+ const bb = bbU < 0 ? 0 : bbU;
863
+ if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
864
+ const invA = 255 - sa;
865
+ const r = br * sa + dr * invA >> 8;
866
+ const g = bg * sa + dg * invA >> 8;
867
+ const b = bb * sa + db * invA >> 8;
868
+ const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
869
+ return (a << 24 | b << 16 | g << 8 | r) >>> 0;
870
+ };
871
+ var divideFast = (src, dst) => {
872
+ const sa = src >>> 24 & 255;
873
+ if (sa === 0) return dst;
874
+ const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
875
+ const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
876
+ const br = sr === 0 ? 255 : Math.min(255, (dr << 8) / sr | 0);
877
+ const bg = sg === 0 ? 255 : Math.min(255, (dg << 8) / sg | 0);
878
+ const bb = sb === 0 ? 255 : Math.min(255, (db << 8) / sb | 0);
879
+ if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
880
+ const invA = 255 - sa;
881
+ const r = br * sa + dr * invA >> 8;
882
+ const g = bg * sa + dg * invA >> 8;
883
+ const b = bb * sa + db * invA >> 8;
884
+ const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
885
+ return (a << 24 | b << 16 | g << 8 | r) >>> 0;
886
+ };
887
+ var BASE_FAST_BLEND_MODE_FUNCTIONS = {
888
+ [BaseBlendMode.overwrite]: overwriteFast,
889
+ [BaseBlendMode.sourceOver]: sourceOverFast,
890
+ [BaseBlendMode.darken]: darkenFast,
891
+ [BaseBlendMode.multiply]: multiplyFast,
892
+ [BaseBlendMode.colorBurn]: colorBurnFast,
893
+ [BaseBlendMode.linearBurn]: linearBurnFast,
894
+ [BaseBlendMode.darkerColor]: darkerFast,
895
+ [BaseBlendMode.lighten]: lightenFast,
896
+ [BaseBlendMode.screen]: screenFast,
897
+ [BaseBlendMode.colorDodge]: colorDodgeFast,
898
+ [BaseBlendMode.linearDodge]: linearDodgeFast,
899
+ [BaseBlendMode.lighterColor]: lighterFast,
900
+ [BaseBlendMode.overlay]: overlayFast,
901
+ [BaseBlendMode.softLight]: softLightFast,
902
+ [BaseBlendMode.hardLight]: hardLightFast,
903
+ [BaseBlendMode.vividLight]: vividLightFast,
904
+ [BaseBlendMode.linearLight]: linearLightFast,
905
+ [BaseBlendMode.pinLight]: pinLightFast,
906
+ [BaseBlendMode.hardMix]: hardMixFast,
907
+ [BaseBlendMode.difference]: differenceFast,
908
+ [BaseBlendMode.exclusion]: exclusionFast,
909
+ [BaseBlendMode.subtract]: subtractFast,
910
+ [BaseBlendMode.divide]: divideFast
911
+ };
912
+ function makeFastBlendModeRegistry(name = "fast") {
913
+ return makeBlendModeRegistry(BaseBlendMode, BASE_FAST_BLEND_MODE_FUNCTIONS, name);
839
914
  }
840
915
 
841
916
  // src/BlendModes/blend-modes-perfect.ts
@@ -844,10 +919,11 @@ var sourceOverPerfect = (src, dst) => {
844
919
  const sa = src >>> 24 & 255;
845
920
  if (sa === 255) return src;
846
921
  if (sa === 0) return dst;
847
- const invA = 255 - sa;
922
+ const da = dst >>> 24 & 255;
923
+ if (da === 0) return src;
848
924
  const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
849
925
  const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
850
- const da = dst >>> 24 & 255;
926
+ const invA = 255 - sa;
851
927
  const tR = sr * sa + dr * invA;
852
928
  const r = tR + 1 + (tR >> 8) >> 8;
853
929
  const tG = sg * sa + dg * invA;
@@ -1389,9 +1465,53 @@ var BASE_PERFECT_BLEND_MODE_FUNCTIONS = {
1389
1465
  [BaseBlendMode.subtract]: subtractPerfect,
1390
1466
  [BaseBlendMode.divide]: dividePerfect
1391
1467
  };
1392
- function makePerfectBlendModeRegistry() {
1393
- return makeBlendModeRegistry(BaseBlendMode, BASE_PERFECT_BLEND_MODE_FUNCTIONS);
1468
+ function makePerfectBlendModeRegistry(name = "perfect") {
1469
+ return makeBlendModeRegistry(BaseBlendMode, BASE_PERFECT_BLEND_MODE_FUNCTIONS, name);
1470
+ }
1471
+
1472
+ // src/BlendModes/toBlendModeIndexAndName.ts
1473
+ function toBlendModeIndexAndName(input) {
1474
+ if (typeof input === "number") {
1475
+ const name = getKeyByValue(BaseBlendMode, input);
1476
+ if (name === void 0) throw new Error(`Invalid index: ${input}`);
1477
+ return {
1478
+ blendIndex: input,
1479
+ blendName: name
1480
+ };
1481
+ }
1482
+ const trimmed = input.trim();
1483
+ const num = Number(trimmed);
1484
+ const isNumeric = trimmed !== "" && !Number.isNaN(num);
1485
+ if (isNumeric && Number.isInteger(num)) {
1486
+ console.log({
1487
+ trimmed,
1488
+ num,
1489
+ isNumeric,
1490
+ isInt: Number.isInteger(num)
1491
+ });
1492
+ const name = getKeyByValue(BaseBlendMode, num);
1493
+ console.log({
1494
+ name
1495
+ });
1496
+ if (name === void 0) throw new Error(`Invalid index: ${num}`);
1497
+ return {
1498
+ blendIndex: num,
1499
+ blendName: name
1500
+ };
1501
+ }
1502
+ if (trimmed in BaseBlendMode) {
1503
+ return {
1504
+ blendIndex: BaseBlendMode[trimmed],
1505
+ blendName: trimmed
1506
+ };
1507
+ }
1508
+ throw new Error(`Invalid blend mode: ${JSON.stringify(input)}`);
1394
1509
  }
1510
+ var getKeyByValue = (obj, value) => {
1511
+ for (const key in obj) {
1512
+ if (obj[key] === value) return key;
1513
+ }
1514
+ };
1395
1515
 
1396
1516
  // src/Canvas/_constants.ts
1397
1517
  var OFFSCREEN_CANVAS_CTX_FAILED = "Failed to create OffscreenCanvas context";
@@ -1430,7 +1550,10 @@ function makeReusableCanvas() {
1430
1550
  } else {
1431
1551
  ctx.clearRect(0, 0, width, height);
1432
1552
  }
1433
- return { canvas, ctx };
1553
+ return {
1554
+ canvas,
1555
+ ctx
1556
+ };
1434
1557
  }
1435
1558
  get2.reset = () => {
1436
1559
  canvas = null;
@@ -1444,21 +1567,13 @@ async function imgBlobToImageData(blob) {
1444
1567
  let bitmap = null;
1445
1568
  try {
1446
1569
  bitmap = await createImageBitmap(blob);
1447
- const canvas = new OffscreenCanvas(
1448
- bitmap.width,
1449
- bitmap.height
1450
- );
1570
+ const canvas = new OffscreenCanvas(bitmap.width, bitmap.height);
1451
1571
  const ctx = canvas.getContext("2d");
1452
1572
  if (!ctx) {
1453
1573
  throw new Error("Failed to get 2D context");
1454
1574
  }
1455
1575
  ctx.drawImage(bitmap, 0, 0);
1456
- return ctx.getImageData(
1457
- 0,
1458
- 0,
1459
- bitmap.width,
1460
- bitmap.height
1461
- );
1576
+ return ctx.getImageData(0, 0, bitmap.width, bitmap.height);
1462
1577
  } finally {
1463
1578
  bitmap?.close();
1464
1579
  }
@@ -1506,6 +1621,64 @@ async function writeImageDataToClipboard(imageData) {
1506
1621
  return writeImgBlobToClipboard(blob);
1507
1622
  }
1508
1623
 
1624
+ // src/History/HistoryManager.ts
1625
+ var HistoryManager = class {
1626
+ constructor(maxSteps = 50) {
1627
+ this.maxSteps = maxSteps;
1628
+ this.undoStack = [];
1629
+ this.redoStack = [];
1630
+ this.listeners = /* @__PURE__ */ new Set();
1631
+ }
1632
+ undoStack;
1633
+ redoStack;
1634
+ listeners;
1635
+ get canUndo() {
1636
+ return this.undoStack.length > 0;
1637
+ }
1638
+ get canRedo() {
1639
+ return this.redoStack.length > 0;
1640
+ }
1641
+ subscribe(fn) {
1642
+ this.listeners.add(fn);
1643
+ return () => this.listeners.delete(fn);
1644
+ }
1645
+ notify() {
1646
+ this.listeners.forEach((fn) => fn());
1647
+ }
1648
+ commit(action) {
1649
+ this.undoStack.push(action);
1650
+ this.clearRedoStack();
1651
+ if (this.undoStack.length > this.maxSteps) {
1652
+ this.undoStack.shift()?.dispose?.();
1653
+ }
1654
+ this.notify();
1655
+ }
1656
+ undo() {
1657
+ let action = this.undoStack.pop();
1658
+ if (!action) return;
1659
+ this.redoStack.push(action);
1660
+ action.undo();
1661
+ this.notify();
1662
+ }
1663
+ redo() {
1664
+ let action = this.redoStack.pop();
1665
+ if (!action) return;
1666
+ this.undoStack.push(action);
1667
+ action.redo();
1668
+ this.notify();
1669
+ }
1670
+ clearRedoStack() {
1671
+ let length = this.redoStack.length;
1672
+ for (let i = 0; i < length; i++) {
1673
+ let action = this.redoStack[i];
1674
+ if (action) {
1675
+ action.dispose?.();
1676
+ }
1677
+ }
1678
+ this.redoStack.length = 0;
1679
+ }
1680
+ };
1681
+
1509
1682
  // src/History/PixelPatchTiles.ts
1510
1683
  var PixelTile = class {
1511
1684
  constructor(id, tx, ty, tileArea) {
@@ -1559,12 +1732,7 @@ var PixelAccumulator = class {
1559
1732
  tile.ty = ty;
1560
1733
  return tile;
1561
1734
  }
1562
- return new PixelTile(
1563
- id,
1564
- tx,
1565
- ty,
1566
- this.config.tileArea
1567
- );
1735
+ return new PixelTile(id, tx, ty, this.config.tileArea);
1568
1736
  }
1569
1737
  recyclePatch(patch) {
1570
1738
  const before = patch.beforeTiles;
@@ -1595,11 +1763,7 @@ var PixelAccumulator = class {
1595
1763
  let id = ty * columns + tx;
1596
1764
  let tile = this.lookup[id];
1597
1765
  if (!tile) {
1598
- tile = this.getTile(
1599
- id,
1600
- tx,
1601
- ty
1602
- );
1766
+ tile = this.getTile(id, tx, ty);
1603
1767
  this.extractState(tile);
1604
1768
  this.lookup[id] = tile;
1605
1769
  this.beforeTiles.push(tile);
@@ -1625,11 +1789,7 @@ var PixelAccumulator = class {
1625
1789
  let id = ty * columns + tx;
1626
1790
  let tile = this.lookup[id];
1627
1791
  if (!tile) {
1628
- tile = this.getTile(
1629
- id,
1630
- tx,
1631
- ty
1632
- );
1792
+ tile = this.getTile(id, tx, ty);
1633
1793
  this.extractState(tile);
1634
1794
  this.lookup[id] = tile;
1635
1795
  this.beforeTiles.push(tile);
@@ -1656,90 +1816,28 @@ var PixelAccumulator = class {
1656
1816
  }
1657
1817
  let srcIndex = globalY * targetWidth + startX;
1658
1818
  let rowData = src.subarray(srcIndex, srcIndex + copyWidth);
1659
- dst.set(rowData, dstIndex);
1660
- if (copyWidth < TILE_SIZE) {
1661
- dst.fill(0, dstIndex + copyWidth, dstIndex + TILE_SIZE);
1662
- }
1663
- }
1664
- }
1665
- extractAfterTiles() {
1666
- let afterTiles = [];
1667
- let length = this.beforeTiles.length;
1668
- for (let i = 0; i < length; i++) {
1669
- let beforeTile = this.beforeTiles[i];
1670
- if (beforeTile) {
1671
- let afterTile = this.getTile(
1672
- beforeTile.id,
1673
- beforeTile.tx,
1674
- beforeTile.ty
1675
- );
1676
- this.extractState(afterTile);
1677
- afterTiles.push(afterTile);
1678
- }
1679
- }
1680
- return afterTiles;
1681
- }
1682
- reset() {
1683
- this.lookup = [];
1684
- this.beforeTiles = [];
1685
- }
1686
- };
1687
-
1688
- // src/History/HistoryManager.ts
1689
- var HistoryManager = class {
1690
- constructor(maxSteps = 50) {
1691
- this.maxSteps = maxSteps;
1692
- this.undoStack = [];
1693
- this.redoStack = [];
1694
- this.listeners = /* @__PURE__ */ new Set();
1695
- }
1696
- undoStack;
1697
- redoStack;
1698
- listeners;
1699
- get canUndo() {
1700
- return this.undoStack.length > 0;
1701
- }
1702
- get canRedo() {
1703
- return this.redoStack.length > 0;
1704
- }
1705
- subscribe(fn) {
1706
- this.listeners.add(fn);
1707
- return () => this.listeners.delete(fn);
1708
- }
1709
- notify() {
1710
- this.listeners.forEach((fn) => fn());
1711
- }
1712
- commit(action) {
1713
- this.undoStack.push(action);
1714
- this.clearRedoStack();
1715
- if (this.undoStack.length > this.maxSteps) {
1716
- this.undoStack.shift()?.dispose?.();
1717
- }
1718
- this.notify();
1719
- }
1720
- undo() {
1721
- let action = this.undoStack.pop();
1722
- if (!action) return;
1723
- this.redoStack.push(action);
1724
- action.undo();
1725
- this.notify();
1726
- }
1727
- redo() {
1728
- let action = this.redoStack.pop();
1729
- if (!action) return;
1730
- this.undoStack.push(action);
1731
- action.redo();
1732
- this.notify();
1819
+ dst.set(rowData, dstIndex);
1820
+ if (copyWidth < TILE_SIZE) {
1821
+ dst.fill(0, dstIndex + copyWidth, dstIndex + TILE_SIZE);
1822
+ }
1823
+ }
1733
1824
  }
1734
- clearRedoStack() {
1735
- let length = this.redoStack.length;
1825
+ extractAfterTiles() {
1826
+ let afterTiles = [];
1827
+ let length = this.beforeTiles.length;
1736
1828
  for (let i = 0; i < length; i++) {
1737
- let action = this.redoStack[i];
1738
- if (action) {
1739
- action.dispose?.();
1829
+ let beforeTile = this.beforeTiles[i];
1830
+ if (beforeTile) {
1831
+ let afterTile = this.getTile(beforeTile.id, beforeTile.tx, beforeTile.ty);
1832
+ this.extractState(afterTile);
1833
+ afterTiles.push(afterTile);
1740
1834
  }
1741
1835
  }
1742
- this.redoStack.length = 0;
1836
+ return afterTiles;
1837
+ }
1838
+ reset() {
1839
+ this.lookup = [];
1840
+ this.beforeTiles = [];
1743
1841
  }
1744
1842
  };
1745
1843
 
@@ -1760,20 +1858,20 @@ var PixelEngineConfig = class {
1760
1858
  }
1761
1859
  };
1762
1860
 
1763
- // src/PixelData/applyMaskToPixelData.ts
1764
- function applyMaskToPixelData(dst, mask, opts = {}) {
1861
+ // src/PixelData/applyAlphaMaskToPixelData.ts
1862
+ function applyAlphaMaskToPixelData(dst, mask, opts = {}) {
1765
1863
  const {
1766
1864
  x: targetX = 0,
1767
1865
  y: targetY = 0,
1768
1866
  w: width = dst.width,
1769
1867
  h: height = dst.height,
1770
1868
  alpha: globalAlpha = 255,
1771
- maskType = 0 /* ALPHA */,
1772
1869
  mw,
1773
1870
  mx = 0,
1774
1871
  my = 0,
1775
1872
  invertMask = false
1776
1873
  } = opts;
1874
+ if (globalAlpha === 0) return;
1777
1875
  let x = targetX;
1778
1876
  let y = targetY;
1779
1877
  let w = width;
@@ -1786,59 +1884,557 @@ function applyMaskToPixelData(dst, mask, opts = {}) {
1786
1884
  h += y;
1787
1885
  y = 0;
1788
1886
  }
1789
- const actualW = Math.min(w, dst.width - x);
1790
- const actualH = Math.min(h, dst.height - y);
1791
- if (actualW <= 0 || actualH <= 0 || globalAlpha === 0) {
1887
+ w = Math.min(w, dst.width - x);
1888
+ h = Math.min(h, dst.height - y);
1889
+ if (w <= 0) return;
1890
+ if (h <= 0) return;
1891
+ const mPitch = mw ?? width;
1892
+ if (mPitch <= 0) return;
1893
+ const maskHeight = mask.length / mPitch | 0;
1894
+ const startX = mx + (x - targetX);
1895
+ const startY = my + (y - targetY);
1896
+ const sX0 = Math.max(0, startX);
1897
+ const sY0 = Math.max(0, startY);
1898
+ const sX1 = Math.min(mPitch, startX + w);
1899
+ const sY1 = Math.min(maskHeight, startY + h);
1900
+ const finalW = sX1 - sX0;
1901
+ const finalH = sY1 - sY0;
1902
+ if (finalW <= 0) return;
1903
+ if (finalH <= 0) return;
1904
+ const xShift = sX0 - startX;
1905
+ const yShift = sY0 - startY;
1906
+ const dst32 = dst.data32;
1907
+ const dw = dst.width;
1908
+ const dStride = dw - finalW;
1909
+ const mStride = mPitch - finalW;
1910
+ let dIdx = (y + yShift) * dw + (x + xShift);
1911
+ let mIdx = sY0 * mPitch + sX0;
1912
+ for (let iy = 0; iy < h; iy++) {
1913
+ for (let ix = 0; ix < w; ix++) {
1914
+ const mVal = mask[mIdx];
1915
+ const effectiveM = invertMask ? 255 - mVal : mVal;
1916
+ let weight = 0;
1917
+ if (effectiveM === 0) {
1918
+ weight = 0;
1919
+ } else if (effectiveM === 255) {
1920
+ weight = globalAlpha;
1921
+ } else if (globalAlpha === 255) {
1922
+ weight = effectiveM;
1923
+ } else {
1924
+ weight = effectiveM * globalAlpha + 128 >> 8;
1925
+ }
1926
+ if (weight === 0) {
1927
+ dst32[dIdx] = (dst32[dIdx] & 16777215) >>> 0;
1928
+ } else if (weight !== 255) {
1929
+ const d = dst32[dIdx];
1930
+ const da = d >>> 24;
1931
+ if (da !== 0) {
1932
+ const finalAlpha = da === 255 ? weight : da * weight + 128 >> 8;
1933
+ dst32[dIdx] = (d & 16777215 | finalAlpha << 24) >>> 0;
1934
+ }
1935
+ }
1936
+ dIdx++;
1937
+ mIdx++;
1938
+ }
1939
+ dIdx += dStride;
1940
+ mIdx += mStride;
1941
+ }
1942
+ }
1943
+
1944
+ // src/History/PixelMutator/mutatorApplyAlphaMask.ts
1945
+ var defaults = {
1946
+ applyAlphaMaskToPixelData
1947
+ };
1948
+ var mutatorApplyAlphaMask = ((writer, deps = defaults) => {
1949
+ const {
1950
+ applyAlphaMaskToPixelData: applyAlphaMaskToPixelData2 = defaults.applyAlphaMaskToPixelData
1951
+ } = deps;
1952
+ return {
1953
+ applyAlphaMask: (mask, opts = {}) => {
1954
+ let target = writer.target;
1955
+ const {
1956
+ x = 0,
1957
+ y = 0,
1958
+ w = writer.target.width,
1959
+ h = writer.target.height
1960
+ } = opts;
1961
+ writer.accumulator.storeRegionBeforeState(x, y, w, h);
1962
+ applyAlphaMaskToPixelData2(target, mask, opts);
1963
+ }
1964
+ };
1965
+ });
1966
+
1967
+ // src/PixelData/applyBinaryMaskToPixelData.ts
1968
+ function applyBinaryMaskToPixelData(dst, mask, opts = {}) {
1969
+ const {
1970
+ x: targetX = 0,
1971
+ y: targetY = 0,
1972
+ w: width = dst.width,
1973
+ h: height = dst.height,
1974
+ alpha = 255,
1975
+ mw,
1976
+ mx = 0,
1977
+ my = 0,
1978
+ invertMask = false
1979
+ } = opts;
1980
+ if (alpha === 0) return;
1981
+ let x = targetX;
1982
+ let y = targetY;
1983
+ let w = width;
1984
+ let h = height;
1985
+ if (x < 0) {
1986
+ w += x;
1987
+ x = 0;
1988
+ }
1989
+ if (y < 0) {
1990
+ h += y;
1991
+ y = 0;
1992
+ }
1993
+ w = Math.min(w, dst.width - x);
1994
+ h = Math.min(h, dst.height - y);
1995
+ if (w <= 0) return;
1996
+ if (h <= 0) return;
1997
+ const mPitch = mw ?? width;
1998
+ if (mPitch <= 0) return;
1999
+ const maskHeight = mask.length / mPitch | 0;
2000
+ const startX = mx + (x - targetX);
2001
+ const startY = my + (y - targetY);
2002
+ const sX0 = Math.max(0, startX);
2003
+ const sY0 = Math.max(0, startY);
2004
+ const sX1 = Math.min(mPitch, startX + w);
2005
+ const sY1 = Math.min(maskHeight, startY + h);
2006
+ const finalW = sX1 - sX0;
2007
+ const finalH = sY1 - sY0;
2008
+ if (finalW <= 0) return;
2009
+ if (finalH <= 0) return;
2010
+ const xShift = sX0 - startX;
2011
+ const yShift = sY0 - startY;
2012
+ const dst32 = dst.data32;
2013
+ const dw = dst.width;
2014
+ const dStride = dw - finalW;
2015
+ const mStride = mPitch - finalW;
2016
+ let dIdx = (y + yShift) * dw + (x + xShift);
2017
+ let mIdx = sY0 * mPitch + sX0;
2018
+ for (let iy = 0; iy < h; iy++) {
2019
+ for (let ix = 0; ix < w; ix++) {
2020
+ const mVal = mask[mIdx];
2021
+ const isMaskedOut = invertMask ? mVal !== 0 : mVal === 0;
2022
+ if (isMaskedOut) {
2023
+ dst32[dIdx] = (dst32[dIdx] & 16777215) >>> 0;
2024
+ } else if (alpha !== 255) {
2025
+ const d = dst32[dIdx];
2026
+ const da = d >>> 24;
2027
+ if (da !== 0) {
2028
+ const finalAlpha = da === 255 ? alpha : da * alpha + 128 >> 8;
2029
+ dst32[dIdx] = (d & 16777215 | finalAlpha << 24) >>> 0;
2030
+ }
2031
+ }
2032
+ dIdx++;
2033
+ mIdx++;
2034
+ }
2035
+ dIdx += dStride;
2036
+ mIdx += mStride;
2037
+ }
2038
+ }
2039
+
2040
+ // src/History/PixelMutator/mutatorApplyBinaryMask.ts
2041
+ var defaults2 = {
2042
+ applyBinaryMaskToPixelData
2043
+ };
2044
+ var mutatorApplyBinaryMask = ((writer, deps = defaults2) => {
2045
+ const {
2046
+ applyBinaryMaskToPixelData: applyBinaryMaskToPixelData2 = defaults2.applyBinaryMaskToPixelData
2047
+ } = deps;
2048
+ return {
2049
+ applyBinaryMask: (mask, opts = {}) => {
2050
+ let target = writer.target;
2051
+ const {
2052
+ x = 0,
2053
+ y = 0,
2054
+ w = writer.target.width,
2055
+ h = writer.target.height
2056
+ } = opts;
2057
+ writer.accumulator.storeRegionBeforeState(x, y, w, h);
2058
+ applyBinaryMaskToPixelData2(target, mask, opts);
2059
+ }
2060
+ };
2061
+ });
2062
+
2063
+ // src/Rect/getCircleBrushOrPencilBounds.ts
2064
+ function getCircleBrushOrPencilBounds(centerX, centerY, brushSize, targetWidth, targetHeight, out) {
2065
+ const r = brushSize / 2;
2066
+ const minOffset = -Math.ceil(r - 0.5);
2067
+ const maxOffset = Math.floor(r - 0.5);
2068
+ const startX = Math.floor(centerX + minOffset);
2069
+ const startY = Math.floor(centerY + minOffset);
2070
+ const endX = Math.floor(centerX + maxOffset) + 1;
2071
+ const endY = Math.floor(centerY + maxOffset) + 1;
2072
+ const res = out ?? {
2073
+ x: 0,
2074
+ y: 0,
2075
+ w: 0,
2076
+ h: 0
2077
+ };
2078
+ const cStartX = Math.max(0, startX);
2079
+ const cStartY = Math.max(0, startY);
2080
+ const cEndX = Math.min(targetWidth, endX);
2081
+ const cEndY = Math.min(targetHeight, endY);
2082
+ const w = cEndX - cStartX;
2083
+ const h = cEndY - cStartY;
2084
+ res.x = cStartX;
2085
+ res.y = cStartY;
2086
+ res.w = w < 0 ? 0 : w;
2087
+ res.h = h < 0 ? 0 : h;
2088
+ return res;
2089
+ }
2090
+
2091
+ // src/PixelData/applyCircleBrushToPixelData.ts
2092
+ function applyCircleBrushToPixelData(target, color, centerX, centerY, brushSize, alpha = 255, fallOff, blendFn = sourceOverPerfect, bounds) {
2093
+ const targetWidth = target.width;
2094
+ const targetHeight = target.height;
2095
+ const b = bounds ?? getCircleBrushOrPencilBounds(centerX, centerY, brushSize, targetWidth, targetHeight);
2096
+ if (b.w <= 0 || b.h <= 0) return;
2097
+ const data32 = target.data32;
2098
+ const r = brushSize / 2;
2099
+ const rSqr = r * r;
2100
+ const invR = 1 / r;
2101
+ const centerOffset = brushSize % 2 === 0 ? 0.5 : 0;
2102
+ const endX = b.x + b.w;
2103
+ const endY = b.y + b.h;
2104
+ const fCenterX = Math.floor(centerX);
2105
+ const fCenterY = Math.floor(centerY);
2106
+ const baseSrcAlpha = color >>> 24;
2107
+ const colorRGB = color & 16777215;
2108
+ const isOpaque = alpha === 255;
2109
+ const isOverwrite = blendFn.isOverwrite;
2110
+ for (let cy = b.y; cy < endY; cy++) {
2111
+ const relY = cy - fCenterY + centerOffset;
2112
+ const dySqr = relY * relY;
2113
+ const rowOffset = cy * targetWidth;
2114
+ for (let cx = b.x; cx < endX; cx++) {
2115
+ const relX = cx - fCenterX + centerOffset;
2116
+ const dSqr = relX * relX + dySqr;
2117
+ if (dSqr <= rSqr) {
2118
+ const idx = rowOffset + cx;
2119
+ let weight = alpha;
2120
+ const strength = fallOff(1 - Math.sqrt(dSqr) * invR);
2121
+ const maskVal = strength * 255 | 0;
2122
+ if (maskVal === 0) continue;
2123
+ if (isOpaque) {
2124
+ weight = maskVal;
2125
+ } else if (maskVal !== 255) {
2126
+ weight = maskVal * alpha + 128 >> 8;
2127
+ }
2128
+ let finalCol = color;
2129
+ if (weight < 255) {
2130
+ const a = baseSrcAlpha * weight + 128 >> 8;
2131
+ if (a === 0 && !isOverwrite) continue;
2132
+ finalCol = (colorRGB | a << 24) >>> 0;
2133
+ }
2134
+ data32[idx] = blendFn(finalCol, data32[idx]);
2135
+ }
2136
+ }
2137
+ }
2138
+ }
2139
+
2140
+ // src/History/PixelMutator/mutatorApplyCircleBrush.ts
2141
+ var defaults3 = {
2142
+ applyCircleBrushToPixelData,
2143
+ getCircleBrushOrPencilBounds
2144
+ };
2145
+ var mutatorApplyCircleBrush = ((writer, deps = defaults3) => {
2146
+ const {
2147
+ applyCircleBrushToPixelData: applyCircleBrushToPixelData2 = defaults3.applyCircleBrushToPixelData,
2148
+ getCircleBrushOrPencilBounds: getCircleBrushOrPencilBounds2 = defaults3.getCircleBrushOrPencilBounds
2149
+ } = deps;
2150
+ const boundsOut = {
2151
+ x: 0,
2152
+ y: 0,
2153
+ w: 0,
2154
+ h: 0
2155
+ };
2156
+ return {
2157
+ applyCircleBrush(color, centerX, centerY, brushSize, alpha = 255, fallOff, blendFn) {
2158
+ const bounds = getCircleBrushOrPencilBounds2(centerX, centerY, brushSize, writer.target.width, writer.target.height, boundsOut);
2159
+ const {
2160
+ x,
2161
+ y,
2162
+ w,
2163
+ h
2164
+ } = bounds;
2165
+ writer.accumulator.storeRegionBeforeState(x, y, w, h);
2166
+ applyCircleBrushToPixelData2(writer.target, color, centerX, centerY, brushSize, alpha, fallOff, blendFn, bounds);
2167
+ }
2168
+ };
2169
+ });
2170
+
2171
+ // src/Algorithm/forEachLinePoint.ts
2172
+ function forEachLinePoint(x0, y0, x1, y1, callback) {
2173
+ const dx = x1 - x0;
2174
+ const dy = y1 - y0;
2175
+ const steps = Math.max(Math.abs(dx), Math.abs(dy));
2176
+ if (steps === 0) {
2177
+ callback(x0, y0);
1792
2178
  return;
1793
2179
  }
2180
+ const xInc = dx / steps;
2181
+ const yInc = dy / steps;
2182
+ let curX = x0;
2183
+ let curY = y0;
2184
+ for (let i = 0; i <= steps; i++) {
2185
+ callback(curX, curY);
2186
+ curX += xInc;
2187
+ curY += yInc;
2188
+ }
2189
+ }
2190
+
2191
+ // src/PixelData/blendColorPixelDataAlphaMask.ts
2192
+ function blendColorPixelDataAlphaMask(dst, color, mask, opts) {
2193
+ const {
2194
+ x: targetX = 0,
2195
+ y: targetY = 0,
2196
+ w: width = dst.width,
2197
+ h: height = dst.height,
2198
+ alpha: globalAlpha = 255,
2199
+ blendFn = sourceOverPerfect,
2200
+ mw = width,
2201
+ mx = 0,
2202
+ my = 0,
2203
+ invertMask = false
2204
+ } = opts;
2205
+ if (globalAlpha === 0 || !mask) return;
2206
+ const baseSrcAlpha = color >>> 24;
2207
+ const isOverwrite = blendFn.isOverwrite || false;
2208
+ if (baseSrcAlpha === 0 && !isOverwrite) return;
2209
+ let x = targetX;
2210
+ let y = targetY;
2211
+ let w = width;
2212
+ let h = height;
2213
+ if (x < 0) {
2214
+ w += x;
2215
+ x = 0;
2216
+ }
2217
+ if (y < 0) {
2218
+ h += y;
2219
+ y = 0;
2220
+ }
2221
+ const actualW = Math.min(w, dst.width - x);
2222
+ const actualH = Math.min(h, dst.height - y);
2223
+ if (actualW <= 0 || actualH <= 0) return;
2224
+ const dx = x - targetX | 0;
2225
+ const dy = y - targetY | 0;
1794
2226
  const dst32 = dst.data32;
1795
2227
  const dw = dst.width;
1796
- const mPitch = mw ?? width;
1797
- const isAlpha = maskType === 0 /* ALPHA */;
1798
- const dx = x - targetX;
1799
- const dy = y - targetY;
1800
- let dIdx = y * dw + x;
1801
- let mIdx = (my + dy) * mPitch + (mx + dx);
1802
- const dStride = dw - actualW;
1803
- const mStride = mPitch - actualW;
2228
+ const mPitch = mw;
2229
+ let dIdx = y * dw + x | 0;
2230
+ let mIdx = (my + dy) * mPitch + (mx + dx) | 0;
2231
+ const dStride = dw - actualW | 0;
2232
+ let mStride = mPitch - actualW | 0;
2233
+ const isOpaque = globalAlpha === 255;
2234
+ const colorRGB = color & 16777215;
1804
2235
  for (let iy = 0; iy < actualH; iy++) {
1805
2236
  for (let ix = 0; ix < actualW; ix++) {
1806
2237
  const mVal = mask[mIdx];
2238
+ const effM = invertMask ? 255 - mVal : mVal;
2239
+ if (effM === 0) {
2240
+ dIdx++;
2241
+ mIdx++;
2242
+ continue;
2243
+ }
1807
2244
  let weight = globalAlpha;
1808
- if (isAlpha) {
1809
- const effectiveM = invertMask ? 255 - mVal : mVal;
1810
- if (effectiveM === 0) {
1811
- dst32[dIdx] = (dst32[dIdx] & 16777215) >>> 0;
1812
- dIdx++;
1813
- mIdx++;
1814
- continue;
1815
- }
1816
- weight = globalAlpha === 255 ? effectiveM : effectiveM * globalAlpha + 128 >> 8;
1817
- } else {
1818
- const isHit = invertMask ? mVal === 0 : mVal === 1;
1819
- if (!isHit) {
1820
- dst32[dIdx] = (dst32[dIdx] & 16777215) >>> 0;
2245
+ if (isOpaque) {
2246
+ weight = effM;
2247
+ } else if (effM !== 255) {
2248
+ weight = effM * globalAlpha + 128 >> 8;
2249
+ }
2250
+ if (weight === 0) {
2251
+ dIdx++;
2252
+ mIdx++;
2253
+ continue;
2254
+ }
2255
+ let finalCol = color;
2256
+ if (weight < 255) {
2257
+ const a = baseSrcAlpha * weight + 128 >> 8;
2258
+ if (a === 0 && !isOverwrite) {
1821
2259
  dIdx++;
1822
2260
  mIdx++;
1823
2261
  continue;
1824
2262
  }
1825
- weight = globalAlpha;
2263
+ finalCol = (colorRGB | a << 24) >>> 0;
1826
2264
  }
1827
- if (weight === 0) {
1828
- dst32[dIdx] = (dst32[dIdx] & 16777215) >>> 0;
1829
- } else {
1830
- const d = dst32[dIdx];
1831
- const da = d >>> 24;
1832
- let finalAlpha = da;
1833
- if (da === 0) {
1834
- } else if (weight === 255) {
1835
- } else if (da === 255) {
1836
- finalAlpha = weight;
1837
- } else {
1838
- finalAlpha = da * weight + 128 >> 8;
2265
+ dst32[dIdx] = blendFn(finalCol, dst32[dIdx]);
2266
+ dIdx++;
2267
+ mIdx++;
2268
+ }
2269
+ dIdx += dStride;
2270
+ mIdx += mStride;
2271
+ }
2272
+ }
2273
+
2274
+ // src/Rect/getCircleBrushOrPencilStrokeBounds.ts
2275
+ function getCircleBrushOrPencilStrokeBounds(x0, y0, x1, y1, brushSize, result) {
2276
+ const r = Math.ceil(brushSize / 2);
2277
+ const minX = Math.min(x0, x1) - r;
2278
+ const minY = Math.min(y0, y1) - r;
2279
+ const maxX = Math.max(x0, x1) + r;
2280
+ const maxY = Math.max(x0, y1) + r;
2281
+ result.x = Math.floor(minX);
2282
+ result.y = Math.floor(minY);
2283
+ result.w = Math.ceil(maxX - minX);
2284
+ result.h = Math.ceil(maxY - minY);
2285
+ return result;
2286
+ }
2287
+
2288
+ // src/History/PixelMutator/mutatorApplyCircleBrushStroke.ts
2289
+ var defaults4 = {
2290
+ forEachLinePoint,
2291
+ blendColorPixelDataAlphaMask,
2292
+ getCircleBrushOrPencilBounds,
2293
+ getCircleBrushOrPencilStrokeBounds
2294
+ };
2295
+ var mutatorApplyCircleBrushStroke = ((writer, deps = defaults4) => {
2296
+ const {
2297
+ forEachLinePoint: forEachLinePoint2 = defaults4.forEachLinePoint,
2298
+ blendColorPixelDataAlphaMask: blendColorPixelDataAlphaMask2 = defaults4.blendColorPixelDataAlphaMask,
2299
+ getCircleBrushOrPencilBounds: getCircleBrushOrPencilBounds2 = defaults4.getCircleBrushOrPencilBounds,
2300
+ getCircleBrushOrPencilStrokeBounds: getCircleBrushOrPencilStrokeBounds2 = defaults4.getCircleBrushOrPencilStrokeBounds
2301
+ } = deps;
2302
+ const strokeBoundsOut = {
2303
+ x: 0,
2304
+ y: 0,
2305
+ w: 0,
2306
+ h: 0
2307
+ };
2308
+ const circleBrushBounds = {
2309
+ x: 0,
2310
+ y: 0,
2311
+ w: 0,
2312
+ h: 0
2313
+ };
2314
+ const blendColorPixelOptions = {
2315
+ alpha: 255,
2316
+ blendFn: sourceOverPerfect,
2317
+ x: 0,
2318
+ y: 0,
2319
+ w: 0,
2320
+ h: 0
2321
+ };
2322
+ return {
2323
+ applyCircleBrushStroke(color, x0, y0, x1, y1, brushSize, alpha = 255, fallOff, blendFn = sourceOverPerfect) {
2324
+ const {
2325
+ x: bx,
2326
+ y: by,
2327
+ w: bw,
2328
+ h: bh
2329
+ } = getCircleBrushOrPencilStrokeBounds2(x0, y0, x1, y1, brushSize, strokeBoundsOut);
2330
+ if (bw <= 0 || bh <= 0) return;
2331
+ const mask = new Uint8Array(bw * bh);
2332
+ const r = brushSize / 2;
2333
+ const rSqr = r * r;
2334
+ const invR = 1 / r;
2335
+ const centerOffset = brushSize % 2 === 0 ? 0.5 : 0;
2336
+ const targetWidth = writer.target.width;
2337
+ const targetHeight = writer.target.height;
2338
+ forEachLinePoint2(x0, y0, x1, y1, (px, py) => {
2339
+ const {
2340
+ x: cbx,
2341
+ y: cby,
2342
+ w: cbw,
2343
+ h: cbh
2344
+ } = getCircleBrushOrPencilBounds2(px, py, brushSize, targetWidth, targetHeight, circleBrushBounds);
2345
+ writer.accumulator.storeRegionBeforeState(cbx, cby, cbw, cbh);
2346
+ const startX = Math.max(bx, cbx);
2347
+ const startY = Math.max(by, cby);
2348
+ const endX = Math.min(bx + bw, cbx + cbw);
2349
+ const endY = Math.min(by + bh, cby + cbh);
2350
+ const fPx = Math.floor(px);
2351
+ const fPy = Math.floor(py);
2352
+ for (let my = startY; my < endY; my++) {
2353
+ const dy = my - fPy + centerOffset;
2354
+ const dySqr = dy * dy;
2355
+ const maskRowOffset = (my - by) * bw;
2356
+ for (let mx = startX; mx < endX; mx++) {
2357
+ const dx = mx - fPx + centerOffset;
2358
+ const dSqr = dx * dx + dySqr;
2359
+ if (dSqr <= rSqr) {
2360
+ const maskIdx = maskRowOffset + (mx - bx);
2361
+ const dist = Math.sqrt(dSqr) * invR;
2362
+ const intensity = fallOff(1 - dist) * 255 | 0;
2363
+ if (intensity > mask[maskIdx]) {
2364
+ mask[maskIdx] = intensity;
2365
+ }
2366
+ }
2367
+ }
1839
2368
  }
1840
- dst32[dIdx] = (d & 16777215 | finalAlpha << 24) >>> 0;
2369
+ });
2370
+ blendColorPixelOptions.blendFn = blendFn;
2371
+ blendColorPixelOptions.alpha = alpha;
2372
+ blendColorPixelOptions.x = bx;
2373
+ blendColorPixelOptions.y = by;
2374
+ blendColorPixelOptions.w = bw;
2375
+ blendColorPixelOptions.h = bh;
2376
+ blendColorPixelDataAlphaMask2(writer.target, color, mask, blendColorPixelOptions);
2377
+ }
2378
+ };
2379
+ });
2380
+
2381
+ // src/PixelData/blendColorPixelDataBinaryMask.ts
2382
+ function blendColorPixelDataBinaryMask(dst, color, mask, opts = {}) {
2383
+ const {
2384
+ x: targetX = 0,
2385
+ y: targetY = 0,
2386
+ w: width = dst.width,
2387
+ h: height = dst.height,
2388
+ alpha: globalAlpha = 255,
2389
+ blendFn = sourceOverPerfect,
2390
+ mw = width,
2391
+ mx = 0,
2392
+ my = 0,
2393
+ invertMask = false
2394
+ } = opts;
2395
+ if (globalAlpha === 0 || !mask) return;
2396
+ const baseSrcAlpha = color >>> 24;
2397
+ const isOverwrite = blendFn.isOverwrite || false;
2398
+ if (baseSrcAlpha === 0 && !isOverwrite) return;
2399
+ let x = targetX;
2400
+ let y = targetY;
2401
+ let w = width;
2402
+ let h = height;
2403
+ if (x < 0) {
2404
+ w += x;
2405
+ x = 0;
2406
+ }
2407
+ if (y < 0) {
2408
+ h += y;
2409
+ y = 0;
2410
+ }
2411
+ const actualW = Math.min(w, dst.width - x);
2412
+ const actualH = Math.min(h, dst.height - y);
2413
+ if (actualW <= 0 || actualH <= 0) return;
2414
+ let baseColorWithGlobalAlpha = color;
2415
+ if (globalAlpha < 255) {
2416
+ const a = baseSrcAlpha * globalAlpha + 128 >> 8;
2417
+ if (a === 0 && !isOverwrite) return;
2418
+ baseColorWithGlobalAlpha = (color & 16777215 | a << 24) >>> 0;
2419
+ }
2420
+ const dx = x - targetX | 0;
2421
+ const dy = y - targetY | 0;
2422
+ const dst32 = dst.data32;
2423
+ const dw = dst.width;
2424
+ const mPitch = mw;
2425
+ let dIdx = y * dw + x | 0;
2426
+ let mIdx = (my + dy) * mPitch + (mx + dx) | 0;
2427
+ const dStride = dw - actualW | 0;
2428
+ const mStride = mPitch - actualW | 0;
2429
+ const skipVal = invertMask ? 1 : 0;
2430
+ for (let iy = 0; iy < actualH; iy++) {
2431
+ for (let ix = 0; ix < actualW; ix++) {
2432
+ if (mask[mIdx] === skipVal) {
2433
+ dIdx++;
2434
+ mIdx++;
2435
+ continue;
1841
2436
  }
2437
+ dst32[dIdx] = blendFn(baseColorWithGlobalAlpha, dst32[dIdx]);
1842
2438
  dIdx++;
1843
2439
  mIdx++;
1844
2440
  }
@@ -1847,63 +2443,424 @@ function applyMaskToPixelData(dst, mask, opts = {}) {
1847
2443
  }
1848
2444
  }
1849
2445
 
1850
- // src/History/PixelWriter.ts
1851
- var PixelWriter = class {
1852
- target;
1853
- historyManager;
1854
- accumulator;
1855
- config;
1856
- mutator;
1857
- constructor(target, mutatorFactory, {
1858
- tileSize = 256,
1859
- maxHistorySteps = 50,
1860
- historyManager = new HistoryManager(maxHistorySteps)
1861
- } = {}) {
1862
- this.target = target;
1863
- this.config = new PixelEngineConfig(tileSize);
1864
- this.historyManager = historyManager;
1865
- this.accumulator = new PixelAccumulator(target, this.config);
1866
- this.mutator = mutatorFactory(this);
1867
- }
1868
- withHistory(cb) {
1869
- cb(this.mutator);
1870
- const beforeTiles = this.accumulator.beforeTiles;
1871
- if (beforeTiles.length === 0) return;
1872
- const afterTiles = this.accumulator.extractAfterTiles();
1873
- const patch = {
1874
- beforeTiles,
1875
- afterTiles
1876
- };
1877
- const target = this.target;
1878
- const tileSize = this.config.tileSize;
1879
- const accumulator = this.accumulator;
1880
- const action = {
1881
- undo: () => applyPatchTiles(target, patch.beforeTiles, tileSize),
1882
- redo: () => applyPatchTiles(target, patch.afterTiles, tileSize),
1883
- dispose: () => accumulator.recyclePatch(patch)
1884
- };
1885
- this.historyManager.commit(action);
1886
- this.accumulator.reset();
1887
- }
2446
+ // src/History/PixelMutator/mutatorApplyCirclePencilStroke.ts
2447
+ var defaults5 = {
2448
+ forEachLinePoint,
2449
+ blendColorPixelDataBinaryMask,
2450
+ getCircleBrushOrPencilBounds,
2451
+ getCircleBrushOrPencilStrokeBounds
1888
2452
  };
2453
+ var mutatorApplyCirclePencilStroke = ((writer, deps = defaults5) => {
2454
+ const {
2455
+ forEachLinePoint: forEachLinePoint2 = defaults5.forEachLinePoint,
2456
+ blendColorPixelDataBinaryMask: blendColorPixelDataBinaryMask2 = defaults5.blendColorPixelDataBinaryMask,
2457
+ getCircleBrushOrPencilStrokeBounds: getCircleBrushOrPencilStrokeBounds2 = defaults5.getCircleBrushOrPencilStrokeBounds,
2458
+ getCircleBrushOrPencilBounds: getCircleBrushOrPencilBounds2 = defaults5.getCircleBrushOrPencilBounds
2459
+ } = deps;
2460
+ const strokeBoundsOut = {
2461
+ x: 0,
2462
+ y: 0,
2463
+ w: 0,
2464
+ h: 0
2465
+ };
2466
+ const circlePencilBounds = {
2467
+ x: 0,
2468
+ y: 0,
2469
+ w: 0,
2470
+ h: 0
2471
+ };
2472
+ const blendColorPixelOptions = {
2473
+ alpha: 255,
2474
+ blendFn: sourceOverPerfect,
2475
+ x: 0,
2476
+ y: 0,
2477
+ w: 0,
2478
+ h: 0
2479
+ };
2480
+ return {
2481
+ applyCirclePencilStroke(color, x0, y0, x1, y1, brushSize, alpha = 255, blendFn = sourceOverPerfect) {
2482
+ const {
2483
+ x: bx,
2484
+ y: by,
2485
+ w: bw,
2486
+ h: bh
2487
+ } = getCircleBrushOrPencilStrokeBounds2(x0, y0, x1, y1, brushSize, strokeBoundsOut);
2488
+ if (bw <= 0 || bh <= 0) return;
2489
+ const mask = new Uint8Array(bw * bh);
2490
+ const r = brushSize / 2;
2491
+ const rSqr = r * r;
2492
+ const centerOffset = brushSize % 2 === 0 ? 0.5 : 0;
2493
+ const targetWidth = writer.target.width;
2494
+ const targetHeight = writer.target.height;
2495
+ forEachLinePoint2(x0, y0, x1, y1, (px, py) => {
2496
+ const {
2497
+ x: cbx,
2498
+ y: cby,
2499
+ w: cbw,
2500
+ h: cbh
2501
+ } = getCircleBrushOrPencilBounds2(px, py, brushSize, targetWidth, targetHeight, circlePencilBounds);
2502
+ writer.accumulator.storeRegionBeforeState(cbx, cby, cbw, cbh);
2503
+ const startX = Math.max(bx, cbx);
2504
+ const startY = Math.max(by, cby);
2505
+ const endX = Math.min(bx + bw, cbx + cbw);
2506
+ const endY = Math.min(by + bh, cby + cbh);
2507
+ const fPx = Math.floor(px);
2508
+ const fPy = Math.floor(py);
2509
+ for (let my = startY; my < endY; my++) {
2510
+ const dy = my - fPy + centerOffset;
2511
+ const dySqr = dy * dy;
2512
+ const maskRowOffset = (my - by) * bw;
2513
+ for (let mx = startX; mx < endX; mx++) {
2514
+ const dx = mx - fPx + centerOffset;
2515
+ const dSqr = dx * dx + dySqr;
2516
+ if (dSqr <= rSqr) {
2517
+ const maskIdx = maskRowOffset + (mx - bx);
2518
+ mask[maskIdx] = 1;
2519
+ }
2520
+ }
2521
+ }
2522
+ });
2523
+ blendColorPixelOptions.blendFn = blendFn;
2524
+ blendColorPixelOptions.alpha = alpha;
2525
+ blendColorPixelOptions.x = bx;
2526
+ blendColorPixelOptions.y = by;
2527
+ blendColorPixelOptions.w = bw;
2528
+ blendColorPixelOptions.h = bh;
2529
+ blendColorPixelDataBinaryMask2(writer.target, color, mask, blendColorPixelOptions);
2530
+ }
2531
+ };
2532
+ });
2533
+
2534
+ // src/Rect/getRectBrushOrPencilBounds.ts
2535
+ function getRectBrushOrPencilBounds(centerX, centerY, brushWidth, brushHeight, targetWidth, targetHeight, out) {
2536
+ const startX = Math.floor(centerX - brushWidth / 2);
2537
+ const startY = Math.floor(centerY - brushHeight / 2);
2538
+ const endX = startX + brushWidth;
2539
+ const endY = startY + brushHeight;
2540
+ const res = out ?? {
2541
+ x: 0,
2542
+ y: 0,
2543
+ w: 0,
2544
+ h: 0
2545
+ };
2546
+ const cStartX = Math.max(0, startX);
2547
+ const cStartY = Math.max(0, startY);
2548
+ const cEndX = Math.min(targetWidth, endX);
2549
+ const cEndY = Math.min(targetHeight, endY);
2550
+ const w = cEndX - cStartX;
2551
+ const h = cEndY - cStartY;
2552
+ res.x = cStartX;
2553
+ res.y = cStartY;
2554
+ res.w = w < 0 ? 0 : w;
2555
+ res.h = h < 0 ? 0 : h;
2556
+ return res;
2557
+ }
2558
+
2559
+ // src/PixelData/applyRectBrushToPixelData.ts
2560
+ function applyRectBrushToPixelData(target, color, centerX, centerY, brushWidth, brushHeight, alpha = 255, fallOff, blendFn = sourceOverPerfect, bounds) {
2561
+ const targetWidth = target.width;
2562
+ const targetHeight = target.height;
2563
+ const b = bounds ?? getRectBrushOrPencilBounds(centerX, centerY, brushWidth, brushHeight, targetWidth, targetHeight);
2564
+ if (b.w <= 0 || b.h <= 0) return;
2565
+ const data32 = target.data32;
2566
+ const baseColor = color & 16777215;
2567
+ const baseSrcAlpha = color >>> 24;
2568
+ const isOpaque = alpha === 255;
2569
+ const invHalfW = 1 / (brushWidth / 2);
2570
+ const invHalfH = 1 / (brushHeight / 2);
2571
+ const centerOffsetX = brushWidth % 2 === 0 ? 0.5 : 0;
2572
+ const centerOffsetY = brushHeight % 2 === 0 ? 0.5 : 0;
2573
+ const fCenterX = Math.floor(centerX);
2574
+ const fCenterY = Math.floor(centerY);
2575
+ const endX = b.x + b.w;
2576
+ const endY = b.y + b.h;
2577
+ const isOverwrite = blendFn.isOverwrite;
2578
+ for (let py = b.y; py < endY; py++) {
2579
+ const rowOffset = py * targetWidth;
2580
+ const dy = Math.abs(py - fCenterY + centerOffsetY) * invHalfH;
2581
+ for (let px = b.x; px < endX; px++) {
2582
+ const idx = rowOffset + px;
2583
+ const dx = Math.abs(px - fCenterX + centerOffsetX) * invHalfW;
2584
+ const dist = dx > dy ? dx : dy;
2585
+ const strength = fallOff(dist);
2586
+ const maskVal = strength * 255 | 0;
2587
+ if (maskVal <= 0) continue;
2588
+ let weight = alpha;
2589
+ if (isOpaque) {
2590
+ weight = maskVal;
2591
+ } else if (maskVal !== 255) {
2592
+ weight = maskVal * alpha + 128 >> 8;
2593
+ }
2594
+ let finalCol = color;
2595
+ if (weight < 255) {
2596
+ const a = baseSrcAlpha * weight + 128 >> 8;
2597
+ if (a === 0 && !isOverwrite) continue;
2598
+ finalCol = (a << 24 | baseColor) >>> 0;
2599
+ }
2600
+ data32[idx] = blendFn(finalCol, data32[idx]);
2601
+ }
2602
+ }
2603
+ }
1889
2604
 
1890
- // src/History/PixelMutator/mutatorApplyMask.ts
1891
- function mutatorApplyMask(writer) {
2605
+ // src/History/PixelMutator/mutatorApplyRectBrush.ts
2606
+ var defaults6 = {
2607
+ applyRectBrushToPixelData,
2608
+ getRectBrushOrPencilBounds
2609
+ };
2610
+ var mutatorApplyRectBrush = ((writer, deps = defaults6) => {
2611
+ const {
2612
+ applyRectBrushToPixelData: applyRectBrushToPixelData2 = defaults6.applyRectBrushToPixelData,
2613
+ getRectBrushOrPencilBounds: getRectBrushOrPencilBounds2 = defaults6.getRectBrushOrPencilBounds
2614
+ } = deps;
2615
+ const boundsOut = {
2616
+ x: 0,
2617
+ y: 0,
2618
+ w: 0,
2619
+ h: 0
2620
+ };
1892
2621
  return {
1893
- applyMask: (mask, opts = {}) => {
1894
- let target = writer.target;
2622
+ applyRectBrush(color, centerX, centerY, brushWidth, brushHeight, alpha = 255, fallOff, blendFn) {
2623
+ const bounds = getRectBrushOrPencilBounds2(centerX, centerY, brushWidth, brushHeight, writer.target.width, writer.target.height, boundsOut);
1895
2624
  const {
1896
- x = 0,
1897
- y = 0,
1898
- w = writer.target.width,
1899
- h = writer.target.height
1900
- } = opts;
2625
+ x,
2626
+ y,
2627
+ w,
2628
+ h
2629
+ } = bounds;
1901
2630
  writer.accumulator.storeRegionBeforeState(x, y, w, h);
1902
- applyMaskToPixelData(target, mask, opts);
2631
+ applyRectBrushToPixelData2(writer.target, color, centerX, centerY, brushWidth, brushHeight, alpha, fallOff, blendFn, bounds);
1903
2632
  }
1904
2633
  };
2634
+ });
2635
+
2636
+ // src/Rect/getRectBrushOrPencilStrokeBounds.ts
2637
+ function getRectBrushOrPencilStrokeBounds(x0, y0, x1, y1, brushWidth, brushHeight, result) {
2638
+ const halfW = brushWidth / 2;
2639
+ const halfH = brushHeight / 2;
2640
+ const minX = Math.min(x0, x1) - halfW;
2641
+ const minY = Math.min(y0, y1) - halfH;
2642
+ const maxX = Math.max(x0, x1) + halfW;
2643
+ const maxY = Math.max(y0, y1) + halfH;
2644
+ result.x = Math.floor(minX);
2645
+ result.y = Math.floor(minY);
2646
+ result.w = Math.ceil(maxX - minX);
2647
+ result.h = Math.ceil(maxY - minY);
2648
+ return result;
1905
2649
  }
1906
2650
 
2651
+ // src/History/PixelMutator/mutatorApplyRectBrushStroke.ts
2652
+ var defaults7 = {
2653
+ forEachLinePoint,
2654
+ blendColorPixelDataAlphaMask,
2655
+ getRectBrushOrPencilBounds,
2656
+ getRectBrushOrPencilStrokeBounds
2657
+ };
2658
+ var mutatorApplyRectBrushStroke = ((writer, deps = defaults7) => {
2659
+ const {
2660
+ forEachLinePoint: forEachLinePoint2 = defaults7.forEachLinePoint,
2661
+ blendColorPixelDataAlphaMask: blendColorPixelDataAlphaMask2 = defaults7.blendColorPixelDataAlphaMask,
2662
+ getRectBrushOrPencilBounds: getRectBrushOrPencilBounds2 = defaults7.getRectBrushOrPencilBounds,
2663
+ getRectBrushOrPencilStrokeBounds: getRectBrushOrPencilStrokeBounds2 = defaults7.getRectBrushOrPencilStrokeBounds
2664
+ } = deps;
2665
+ const strokeBoundsOut = {
2666
+ x: 0,
2667
+ y: 0,
2668
+ w: 0,
2669
+ h: 0
2670
+ };
2671
+ const rectBrushBounds = {
2672
+ x: 0,
2673
+ y: 0,
2674
+ w: 0,
2675
+ h: 0
2676
+ };
2677
+ const blendColorPixelOptions = {
2678
+ alpha: 255,
2679
+ blendFn: sourceOverPerfect,
2680
+ x: 0,
2681
+ y: 0,
2682
+ w: 0,
2683
+ h: 0
2684
+ };
2685
+ return {
2686
+ applyRectBrushStroke(color, x0, y0, x1, y1, brushWidth, brushHeight, alpha = 255, fallOff, blendFn = sourceOverPerfect) {
2687
+ const {
2688
+ x: bx,
2689
+ y: by,
2690
+ w: bw,
2691
+ h: bh
2692
+ } = getRectBrushOrPencilStrokeBounds2(x0, y0, x1, y1, brushWidth, brushHeight, strokeBoundsOut);
2693
+ if (bw <= 0 || bh <= 0) return;
2694
+ const mask = new Uint8Array(bw * bh);
2695
+ const halfW = brushWidth / 2;
2696
+ const halfH = brushHeight / 2;
2697
+ const invHalfW = 1 / halfW;
2698
+ const invHalfH = 1 / halfH;
2699
+ const centerOffsetX = brushWidth % 2 === 0 ? 0.5 : 0;
2700
+ const centerOffsetY = brushHeight % 2 === 0 ? 0.5 : 0;
2701
+ const targetWidth = writer.target.width;
2702
+ const targetHeight = writer.target.height;
2703
+ forEachLinePoint2(x0, y0, x1, y1, (px, py) => {
2704
+ const {
2705
+ x: rbx,
2706
+ y: rby,
2707
+ w: rbw,
2708
+ h: rbh
2709
+ } = getRectBrushOrPencilBounds2(px, py, brushWidth, brushHeight, targetWidth, targetHeight, rectBrushBounds);
2710
+ writer.accumulator.storeRegionBeforeState(rbx, rby, rbw, rbh);
2711
+ const startX = Math.max(bx, rbx);
2712
+ const startY = Math.max(by, rby);
2713
+ const endX = Math.min(bx + bw, rbx + rbw);
2714
+ const endY = Math.min(by + bh, rby + rbh);
2715
+ const fPx = Math.floor(px);
2716
+ const fPy = Math.floor(py);
2717
+ for (let my = startY; my < endY; my++) {
2718
+ const dy = Math.abs(my - fPy + centerOffsetY) * invHalfH;
2719
+ const maskRowOffset = (my - by) * bw;
2720
+ for (let mx = startX; mx < endX; mx++) {
2721
+ const dx = Math.abs(mx - fPx + centerOffsetX) * invHalfW;
2722
+ const maskIdx = maskRowOffset + (mx - bx);
2723
+ const dist = dx > dy ? dx : dy;
2724
+ const strength = fallOff(dist);
2725
+ if (strength > 0) {
2726
+ const intensity = strength * 255 | 0;
2727
+ if (intensity > mask[maskIdx]) {
2728
+ mask[maskIdx] = intensity;
2729
+ }
2730
+ }
2731
+ }
2732
+ }
2733
+ });
2734
+ blendColorPixelOptions.blendFn = blendFn;
2735
+ blendColorPixelOptions.alpha = alpha;
2736
+ blendColorPixelOptions.x = bx;
2737
+ blendColorPixelOptions.y = by;
2738
+ blendColorPixelOptions.w = bw;
2739
+ blendColorPixelOptions.h = bh;
2740
+ blendColorPixelDataAlphaMask2(writer.target, color, mask, blendColorPixelOptions);
2741
+ }
2742
+ };
2743
+ });
2744
+
2745
+ // src/History/PixelMutator/mutatorApplyRectPencil.ts
2746
+ var defaults8 = {
2747
+ applyRectBrushToPixelData,
2748
+ getRectBrushOrPencilBounds,
2749
+ fallOff: () => 1
2750
+ };
2751
+ var mutatorApplyRectPencil = ((writer, deps = defaults8) => {
2752
+ const {
2753
+ applyRectBrushToPixelData: applyRectBrushToPixelData2 = defaults8.applyRectBrushToPixelData,
2754
+ getRectBrushOrPencilBounds: getRectBrushOrPencilBounds2 = defaults8.getRectBrushOrPencilBounds,
2755
+ fallOff = defaults8.fallOff
2756
+ } = deps;
2757
+ const boundsOut = {
2758
+ x: 0,
2759
+ y: 0,
2760
+ w: 0,
2761
+ h: 0
2762
+ };
2763
+ return {
2764
+ applyRectPencil(color, centerX, centerY, brushWidth, brushHeight, alpha = 255, blendFn) {
2765
+ const bounds = getRectBrushOrPencilBounds2(centerX, centerY, brushWidth, brushHeight, writer.target.width, writer.target.height, boundsOut);
2766
+ const {
2767
+ x,
2768
+ y,
2769
+ w,
2770
+ h
2771
+ } = bounds;
2772
+ writer.accumulator.storeRegionBeforeState(x, y, w, h);
2773
+ applyRectBrushToPixelData2(writer.target, color, centerX, centerY, brushWidth, brushHeight, alpha, fallOff, blendFn, bounds);
2774
+ }
2775
+ };
2776
+ });
2777
+
2778
+ // src/History/PixelMutator/mutatorApplyRectPencilStroke.ts
2779
+ var defaults9 = {
2780
+ forEachLinePoint,
2781
+ getRectBrushOrPencilBounds,
2782
+ getRectBrushOrPencilStrokeBounds,
2783
+ blendColorPixelDataBinaryMask
2784
+ };
2785
+ var mutatorApplyRectPencilStroke = ((writer, deps = defaults9) => {
2786
+ const {
2787
+ forEachLinePoint: forEachLinePoint2 = defaults9.forEachLinePoint,
2788
+ blendColorPixelDataBinaryMask: blendColorPixelDataBinaryMask2 = defaults9.blendColorPixelDataBinaryMask,
2789
+ getRectBrushOrPencilBounds: getRectBrushOrPencilBounds2 = defaults9.getRectBrushOrPencilBounds,
2790
+ getRectBrushOrPencilStrokeBounds: getRectBrushOrPencilStrokeBounds2 = defaults9.getRectBrushOrPencilStrokeBounds
2791
+ } = deps;
2792
+ const strokeBoundsOut = {
2793
+ x: 0,
2794
+ y: 0,
2795
+ w: 0,
2796
+ h: 0
2797
+ };
2798
+ const rectPencilBounds = {
2799
+ x: 0,
2800
+ y: 0,
2801
+ w: 0,
2802
+ h: 0
2803
+ };
2804
+ const blendColorPixelOptions = {
2805
+ alpha: 255,
2806
+ blendFn: sourceOverPerfect,
2807
+ x: 0,
2808
+ y: 0,
2809
+ w: 0,
2810
+ h: 0
2811
+ };
2812
+ return {
2813
+ applyRectPencilStroke(color, x0, y0, x1, y1, brushWidth, brushHeight, alpha = 255, blendFn = sourceOverPerfect) {
2814
+ const {
2815
+ x: bx,
2816
+ y: by,
2817
+ w: bw,
2818
+ h: bh
2819
+ } = getRectBrushOrPencilStrokeBounds2(x0, y0, x1, y1, brushWidth, brushHeight, strokeBoundsOut);
2820
+ if (bw <= 0 || bh <= 0) return;
2821
+ const mask = new Uint8Array(bw * bh);
2822
+ const halfW = brushWidth / 2;
2823
+ const halfH = brushHeight / 2;
2824
+ const centerOffset = brushWidth % 2 === 0 ? 0.5 : 0;
2825
+ const targetWidth = writer.target.width;
2826
+ const targetHeight = writer.target.height;
2827
+ forEachLinePoint2(x0, y0, x1, y1, (px, py) => {
2828
+ const {
2829
+ x: rbx,
2830
+ y: rby,
2831
+ w: rbw,
2832
+ h: rbh
2833
+ } = getRectBrushOrPencilBounds2(px, py, brushWidth, brushHeight, targetWidth, targetHeight, rectPencilBounds);
2834
+ writer.accumulator.storeRegionBeforeState(rbx, rby, rbw, rbh);
2835
+ const startX = Math.max(bx, rbx);
2836
+ const startY = Math.max(by, rby);
2837
+ const endX = Math.min(bx + bw, rbx + rbw);
2838
+ const endY = Math.min(by + bh, rby + rbh);
2839
+ const fPx = Math.floor(px);
2840
+ const fPy = Math.floor(py);
2841
+ for (let my = startY; my < endY; my++) {
2842
+ const dy = Math.abs(my - fPy + centerOffset);
2843
+ const maskRowOffset = (my - by) * bw;
2844
+ for (let mx = startX; mx < endX; mx++) {
2845
+ const dx = Math.abs(mx - fPx + centerOffset);
2846
+ const maskIdx = maskRowOffset + (mx - bx);
2847
+ if (dx <= halfW && dy <= halfH) {
2848
+ mask[maskIdx] = 1;
2849
+ }
2850
+ }
2851
+ }
2852
+ });
2853
+ blendColorPixelOptions.blendFn = blendFn;
2854
+ blendColorPixelOptions.alpha = alpha;
2855
+ blendColorPixelOptions.x = bx;
2856
+ blendColorPixelOptions.y = by;
2857
+ blendColorPixelOptions.w = bw;
2858
+ blendColorPixelOptions.h = bh;
2859
+ blendColorPixelDataBinaryMask2(writer.target, color, mask, blendColorPixelOptions);
2860
+ }
2861
+ };
2862
+ });
2863
+
1907
2864
  // src/PixelData/blendColorPixelData.ts
1908
2865
  function blendColorPixelData(dst, color, opts = {}) {
1909
2866
  const {
@@ -1912,22 +2869,13 @@ function blendColorPixelData(dst, color, opts = {}) {
1912
2869
  w: width = dst.width,
1913
2870
  h: height = dst.height,
1914
2871
  alpha: globalAlpha = 255,
1915
- blendFn = sourceOverFast,
1916
- mask,
1917
- maskType = 0 /* ALPHA */,
1918
- mw,
1919
- mx = 0,
1920
- my = 0,
1921
- invertMask = false
2872
+ blendFn = sourceOverPerfect
1922
2873
  } = opts;
1923
2874
  if (globalAlpha === 0) return;
1924
2875
  const baseSrcAlpha = color >>> 24;
1925
- const isOverwrite = blendFn.isOverwrite;
2876
+ const isOverwrite = blendFn.isOverwrite || false;
1926
2877
  if (baseSrcAlpha === 0 && !isOverwrite) return;
1927
- let x = targetX;
1928
- let y = targetY;
1929
- let w = width;
1930
- let h = height;
2878
+ let x = targetX, y = targetY, w = width, h = height;
1931
2879
  if (x < 0) {
1932
2880
  w += x;
1933
2881
  x = 0;
@@ -1939,76 +2887,33 @@ function blendColorPixelData(dst, color, opts = {}) {
1939
2887
  const actualW = Math.min(w, dst.width - x);
1940
2888
  const actualH = Math.min(h, dst.height - y);
1941
2889
  if (actualW <= 0 || actualH <= 0) return;
2890
+ let finalSrcColor = color;
2891
+ if (globalAlpha < 255) {
2892
+ const a = baseSrcAlpha * globalAlpha + 128 >> 8;
2893
+ if (a === 0 && !isOverwrite) return;
2894
+ finalSrcColor = (color & 16777215 | a << 24) >>> 0;
2895
+ }
1942
2896
  const dst32 = dst.data32;
1943
2897
  const dw = dst.width;
1944
- const mPitch = mw ?? width;
1945
- const isAlphaMask = maskType === 0 /* ALPHA */;
1946
- const dx = x - targetX;
1947
- const dy = y - targetY;
1948
- let dIdx = y * dw + x;
1949
- let mIdx = (my + dy) * mPitch + (mx + dx);
1950
- const dStride = dw - actualW;
1951
- const mStride = mPitch - actualW;
2898
+ let dIdx = y * dw + x | 0;
2899
+ const dStride = dw - actualW | 0;
1952
2900
  for (let iy = 0; iy < actualH; iy++) {
1953
2901
  for (let ix = 0; ix < actualW; ix++) {
1954
- let weight = globalAlpha;
1955
- if (mask) {
1956
- const mVal = mask[mIdx];
1957
- if (isAlphaMask) {
1958
- const effectiveM = invertMask ? 255 - mVal : mVal;
1959
- if (effectiveM === 0) {
1960
- dIdx++;
1961
- mIdx++;
1962
- continue;
1963
- }
1964
- if (globalAlpha === 255) {
1965
- weight = effectiveM;
1966
- } else if (effectiveM === 255) {
1967
- weight = globalAlpha;
1968
- } else {
1969
- weight = effectiveM * globalAlpha + 128 >> 8;
1970
- }
1971
- } else {
1972
- const isHit = invertMask ? mVal === 0 : mVal === 1;
1973
- if (!isHit) {
1974
- dIdx++;
1975
- mIdx++;
1976
- continue;
1977
- }
1978
- weight = globalAlpha;
1979
- }
1980
- if (weight === 0) {
1981
- dIdx++;
1982
- mIdx++;
1983
- continue;
1984
- }
1985
- }
1986
- let currentSrcColor = color;
1987
- if (weight < 255) {
1988
- let currentSrcAlpha = baseSrcAlpha;
1989
- if (baseSrcAlpha === 255) {
1990
- currentSrcAlpha = weight;
1991
- } else {
1992
- currentSrcAlpha = baseSrcAlpha * weight + 128 >> 8;
1993
- }
1994
- if (!isOverwrite && currentSrcAlpha === 0) {
1995
- dIdx++;
1996
- mIdx++;
1997
- continue;
1998
- }
1999
- currentSrcColor = (color & 16777215 | currentSrcAlpha << 24) >>> 0;
2000
- }
2001
- dst32[dIdx] = blendFn(currentSrcColor, dst32[dIdx]);
2902
+ dst32[dIdx] = blendFn(finalSrcColor, dst32[dIdx]);
2002
2903
  dIdx++;
2003
- mIdx++;
2004
2904
  }
2005
2905
  dIdx += dStride;
2006
- mIdx += mStride;
2007
2906
  }
2008
2907
  }
2009
2908
 
2010
2909
  // src/History/PixelMutator/mutatorBlendColor.ts
2011
- function mutatorBlendColor(writer) {
2910
+ var defaults10 = {
2911
+ blendColorPixelData
2912
+ };
2913
+ var mutatorBlendColor = ((writer, deps = defaults10) => {
2914
+ const {
2915
+ blendColorPixelData: blendColorPixelData2 = defaults10.blendColorPixelData
2916
+ } = deps;
2012
2917
  return {
2013
2918
  blendColor(color, opts = {}) {
2014
2919
  const {
@@ -2018,10 +2923,10 @@ function mutatorBlendColor(writer) {
2018
2923
  h = writer.target.height
2019
2924
  } = opts;
2020
2925
  writer.accumulator.storeRegionBeforeState(x, y, w, h);
2021
- blendColorPixelData(writer.target, color, opts);
2926
+ blendColorPixelData2(writer.target, color, opts);
2022
2927
  }
2023
2928
  };
2024
- }
2929
+ });
2025
2930
 
2026
2931
  // src/History/PixelMutator/mutatorBlendPixel.ts
2027
2932
  function mutatorBlendPixel(writer) {
@@ -2055,13 +2960,7 @@ function blendPixelData(dst, src, opts) {
2055
2960
  w: width = src.width,
2056
2961
  h: height = src.height,
2057
2962
  alpha: globalAlpha = 255,
2058
- blendFn = sourceOverFast,
2059
- mask,
2060
- maskType = 0 /* ALPHA */,
2061
- mw,
2062
- mx = 0,
2063
- my = 0,
2064
- invertMask = false
2963
+ blendFn = sourceOverPerfect
2065
2964
  } = opts;
2066
2965
  if (globalAlpha === 0) return;
2067
2966
  let x = targetX;
@@ -2099,91 +2998,48 @@ function blendPixelData(dst, src, opts) {
2099
2998
  const src32 = src.data32;
2100
2999
  const dw = dst.width;
2101
3000
  const sw = src.width;
2102
- const mPitch = mw ?? width;
2103
- const isAlphaMask = maskType === 0 /* ALPHA */;
2104
- const dx = x - targetX;
2105
- const dy = y - targetY;
2106
- let dIdx = y * dw + x;
2107
- let sIdx = sy * sw + sx;
2108
- let mIdx = (my + dy) * mPitch + (mx + dx);
2109
- const dStride = dw - actualW;
2110
- const sStride = sw - actualW;
2111
- const mStride = mPitch - actualW;
3001
+ let dIdx = y * dw + x | 0;
3002
+ let sIdx = sy * sw + sx | 0;
3003
+ const dStride = dw - actualW | 0;
3004
+ const sStride = sw - actualW | 0;
3005
+ const isOpaque = globalAlpha === 255;
2112
3006
  const isOverwrite = blendFn.isOverwrite;
2113
3007
  for (let iy = 0; iy < actualH; iy++) {
2114
3008
  for (let ix = 0; ix < actualW; ix++) {
2115
- const baseSrcColor = src32[sIdx];
2116
- const baseSrcAlpha = baseSrcColor >>> 24;
2117
- if (baseSrcAlpha === 0 && !isOverwrite) {
3009
+ const srcCol = src32[sIdx];
3010
+ const srcAlpha = srcCol >>> 24;
3011
+ if (srcAlpha === 0 && !isOverwrite) {
2118
3012
  dIdx++;
2119
3013
  sIdx++;
2120
- mIdx++;
2121
3014
  continue;
2122
3015
  }
2123
- let weight = globalAlpha;
2124
- if (mask) {
2125
- const mVal = mask[mIdx];
2126
- if (isAlphaMask) {
2127
- const effectiveM = invertMask ? 255 - mVal : mVal;
2128
- if (effectiveM === 0) {
2129
- dIdx++;
2130
- sIdx++;
2131
- mIdx++;
2132
- continue;
2133
- }
2134
- if (globalAlpha === 255) {
2135
- weight = effectiveM;
2136
- } else if (effectiveM === 255) {
2137
- weight = globalAlpha;
2138
- } else {
2139
- weight = effectiveM * globalAlpha + 128 >> 8;
2140
- }
2141
- } else {
2142
- const isHit = invertMask ? mVal === 0 : mVal === 1;
2143
- if (!isHit) {
2144
- dIdx++;
2145
- sIdx++;
2146
- mIdx++;
2147
- continue;
2148
- }
2149
- weight = globalAlpha;
2150
- }
2151
- if (weight === 0) {
2152
- dIdx++;
2153
- sIdx++;
2154
- mIdx++;
2155
- continue;
2156
- }
2157
- }
2158
- let currentSrcColor = baseSrcColor;
2159
- if (weight < 255) {
2160
- let currentSrcAlpha = baseSrcAlpha;
2161
- if (baseSrcAlpha === 255) {
2162
- currentSrcAlpha = weight;
2163
- } else {
2164
- currentSrcAlpha = baseSrcAlpha * weight + 128 >> 8;
2165
- }
2166
- if (!isOverwrite && currentSrcAlpha === 0) {
3016
+ let finalCol = srcCol;
3017
+ if (!isOpaque) {
3018
+ const a = srcAlpha * globalAlpha + 128 >> 8;
3019
+ if (a === 0 && !isOverwrite) {
2167
3020
  dIdx++;
2168
3021
  sIdx++;
2169
- mIdx++;
2170
3022
  continue;
2171
3023
  }
2172
- currentSrcColor = (baseSrcColor & 16777215 | currentSrcAlpha << 24) >>> 0;
3024
+ finalCol = (srcCol & 16777215 | a << 24) >>> 0;
2173
3025
  }
2174
- dst32[dIdx] = blendFn(currentSrcColor, dst32[dIdx]);
3026
+ dst32[dIdx] = blendFn(finalCol, dst32[dIdx]);
2175
3027
  dIdx++;
2176
3028
  sIdx++;
2177
- mIdx++;
2178
3029
  }
2179
3030
  dIdx += dStride;
2180
3031
  sIdx += sStride;
2181
- mIdx += mStride;
2182
3032
  }
2183
3033
  }
2184
3034
 
2185
3035
  // src/History/PixelMutator/mutatorBlendPixelData.ts
2186
- function mutatorBlendPixelData(writer) {
3036
+ var defaults11 = {
3037
+ blendPixelData
3038
+ };
3039
+ var mutatorBlendPixelData = ((writer, deps = defaults11) => {
3040
+ const {
3041
+ blendPixelData: blendPixelData2 = defaults11.blendPixelData
3042
+ } = deps;
2187
3043
  return {
2188
3044
  blendPixelData(src, opts) {
2189
3045
  const {
@@ -2193,12 +3049,13 @@ function mutatorBlendPixelData(writer) {
2193
3049
  h = src.height
2194
3050
  } = opts;
2195
3051
  writer.accumulator.storeRegionBeforeState(x, y, w, h);
2196
- blendPixelData(writer.target, src, opts);
3052
+ blendPixelData2(writer.target, src, opts);
2197
3053
  }
2198
3054
  };
2199
- }
3055
+ });
2200
3056
 
2201
3057
  // src/PixelData/fillPixelData.ts
3058
+ var SCRATCH_RECT = makeClippedRect();
2202
3059
  function fillPixelData(dst, color, _x, _y, _w, _h) {
2203
3060
  let x;
2204
3061
  let y;
@@ -2220,34 +3077,57 @@ function fillPixelData(dst, color, _x, _y, _w, _h) {
2220
3077
  w = dst.width;
2221
3078
  h = dst.height;
2222
3079
  }
2223
- if (x < 0) {
2224
- w += x;
2225
- x = 0;
2226
- }
2227
- if (y < 0) {
2228
- h += y;
2229
- y = 0;
2230
- }
2231
- const actualW = Math.min(w, dst.width - x);
2232
- const actualH = Math.min(h, dst.height - y);
2233
- if (actualW <= 0 || actualH <= 0) {
2234
- return;
2235
- }
3080
+ const clip = resolveRectClipping(x, y, w, h, dst.width, dst.height, SCRATCH_RECT);
3081
+ if (!clip.inBounds) return;
3082
+ const {
3083
+ x: finalX,
3084
+ y: finalY,
3085
+ w: actualW,
3086
+ h: actualH
3087
+ } = clip;
2236
3088
  const dst32 = dst.data32;
2237
3089
  const dw = dst.width;
2238
- if (actualW === dw && actualH === dst.height && x === 0 && y === 0) {
3090
+ if (actualW === dw && actualH === dst.height && finalX === 0 && finalY === 0) {
2239
3091
  dst32.fill(color);
2240
3092
  return;
2241
3093
  }
2242
3094
  for (let iy = 0; iy < actualH; iy++) {
2243
- const start = (y + iy) * dw + x;
3095
+ const start = (finalY + iy) * dw + finalX;
2244
3096
  const end = start + actualW;
2245
3097
  dst32.fill(color, start, end);
2246
3098
  }
2247
3099
  }
2248
3100
 
2249
- // src/History/PixelMutator/mutatorFillPixelData.ts
2250
- function mutatorFill(writer) {
3101
+ // src/History/PixelMutator/mutatorClear.ts
3102
+ var defaults12 = {
3103
+ fillPixelData
3104
+ };
3105
+ var mutatorClear = ((writer, deps = defaults12) => {
3106
+ const {
3107
+ fillPixelData: fillPixelData2 = defaults12.fillPixelData
3108
+ } = deps;
3109
+ return {
3110
+ clear(rect = {}) {
3111
+ const {
3112
+ x = 0,
3113
+ y = 0,
3114
+ w = writer.target.width,
3115
+ h = writer.target.height
3116
+ } = rect;
3117
+ writer.accumulator.storeRegionBeforeState(x, y, w, h);
3118
+ fillPixelData2(writer.target, 0, x, y, w, h);
3119
+ }
3120
+ };
3121
+ });
3122
+
3123
+ // src/History/PixelMutator/mutatorFill.ts
3124
+ var defaults13 = {
3125
+ fillPixelData
3126
+ };
3127
+ var mutatorFill = ((writer, deps = defaults13) => {
3128
+ const {
3129
+ fillPixelData: fillPixelData2 = defaults13.fillPixelData
3130
+ } = deps;
2251
3131
  return {
2252
3132
  fill(color, rect = {}) {
2253
3133
  const {
@@ -2257,12 +3137,13 @@ function mutatorFill(writer) {
2257
3137
  h = writer.target.height
2258
3138
  } = rect;
2259
3139
  writer.accumulator.storeRegionBeforeState(x, y, w, h);
2260
- fillPixelData(writer.target, color, x, y, w, h);
3140
+ fillPixelData2(writer.target, color, x, y, w, h);
2261
3141
  }
2262
3142
  };
2263
- }
3143
+ });
2264
3144
 
2265
3145
  // src/PixelData/invertPixelData.ts
3146
+ var SCRATCH_RECT2 = makeClippedRect();
2266
3147
  function invertPixelData(pixelData, opts = {}) {
2267
3148
  const dst = pixelData;
2268
3149
  const {
@@ -2276,21 +3157,14 @@ function invertPixelData(pixelData, opts = {}) {
2276
3157
  my = 0,
2277
3158
  invertMask = false
2278
3159
  } = opts;
2279
- let x = targetX;
2280
- let y = targetY;
2281
- let w = width;
2282
- let h = height;
2283
- if (x < 0) {
2284
- w += x;
2285
- x = 0;
2286
- }
2287
- if (y < 0) {
2288
- h += y;
2289
- y = 0;
2290
- }
2291
- const actualW = Math.min(w, dst.width - x);
2292
- const actualH = Math.min(h, dst.height - y);
2293
- if (actualW <= 0 || actualH <= 0) return;
3160
+ const clip = resolveRectClipping(targetX, targetY, width, height, dst.width, dst.height, SCRATCH_RECT2);
3161
+ if (!clip.inBounds) return;
3162
+ const {
3163
+ x,
3164
+ y,
3165
+ w: actualW,
3166
+ h: actualH
3167
+ } = clip;
2294
3168
  const dst32 = dst.data32;
2295
3169
  const dw = dst.width;
2296
3170
  const mPitch = mw ?? width;
@@ -2326,7 +3200,13 @@ function invertPixelData(pixelData, opts = {}) {
2326
3200
  }
2327
3201
 
2328
3202
  // src/History/PixelMutator/mutatorInvert.ts
2329
- function mutatorInvert(writer) {
3203
+ var defaults14 = {
3204
+ invertPixelData
3205
+ };
3206
+ var mutatorInvert = ((writer, deps = defaults14) => {
3207
+ const {
3208
+ invertPixelData: invertPixelData2 = defaults14.invertPixelData
3209
+ } = deps;
2330
3210
  return {
2331
3211
  invert(opts = {}) {
2332
3212
  const {
@@ -2336,44 +3216,121 @@ function mutatorInvert(writer) {
2336
3216
  h = writer.target.height
2337
3217
  } = opts;
2338
3218
  writer.accumulator.storeRegionBeforeState(x, y, w, h);
2339
- invertPixelData(writer.target, opts);
3219
+ invertPixelData2(writer.target, opts);
2340
3220
  }
2341
3221
  };
2342
- }
3222
+ });
2343
3223
 
2344
3224
  // src/History/PixelMutator.ts
2345
3225
  function makeFullPixelMutator(writer) {
2346
3226
  return {
2347
- ...mutatorApplyMask(writer),
3227
+ ...mutatorApplyAlphaMask(writer),
3228
+ ...mutatorApplyBinaryMask(writer),
2348
3229
  ...mutatorBlendPixelData(writer),
2349
3230
  ...mutatorBlendColor(writer),
2350
3231
  ...mutatorBlendPixel(writer),
2351
3232
  ...mutatorFill(writer),
2352
- ...mutatorInvert(writer)
3233
+ ...mutatorInvert(writer),
3234
+ ...mutatorApplyCircleBrush(writer),
3235
+ ...mutatorApplyCircleBrushStroke(writer),
3236
+ ...mutatorApplyCirclePencilStroke(writer),
3237
+ ...mutatorApplyRectBrush(writer),
3238
+ ...mutatorApplyRectBrushStroke(writer),
3239
+ ...mutatorApplyRectPencil(writer),
3240
+ ...mutatorApplyRectPencilStroke(writer),
3241
+ ...mutatorClear(writer)
2353
3242
  };
2354
3243
  }
2355
3244
 
2356
- // src/ImageData/ReusableImageData.ts
2357
- function makeReusableImageData() {
2358
- let imageData = null;
2359
- let buffer = null;
2360
- return function getReusableImageData(width, height) {
2361
- const hasInstance = !!imageData;
2362
- const widthMatches = hasInstance && imageData.width === width;
2363
- const heightMatches = hasInstance && imageData.height === height;
2364
- if (!widthMatches || !heightMatches) {
2365
- const buffer2 = new Uint8ClampedArray(width * height * 4);
2366
- imageData = new ImageData(buffer2, width, height);
3245
+ // src/History/PixelWriter.ts
3246
+ var PixelWriter = class {
3247
+ target;
3248
+ historyManager;
3249
+ accumulator;
3250
+ config;
3251
+ mutator;
3252
+ constructor(target, mutatorFactory, {
3253
+ tileSize = 256,
3254
+ maxHistorySteps = 50,
3255
+ historyManager = new HistoryManager(maxHistorySteps)
3256
+ } = {}) {
3257
+ this.target = target;
3258
+ this.config = new PixelEngineConfig(tileSize);
3259
+ this.historyManager = historyManager;
3260
+ this.accumulator = new PixelAccumulator(target, this.config);
3261
+ this.mutator = mutatorFactory(this);
3262
+ }
3263
+ withHistory(cb) {
3264
+ cb(this.mutator);
3265
+ this.captureHistory();
3266
+ }
3267
+ captureHistory() {
3268
+ const beforeTiles = this.accumulator.beforeTiles;
3269
+ if (beforeTiles.length === 0) return;
3270
+ const afterTiles = this.accumulator.extractAfterTiles();
3271
+ const patch = {
3272
+ beforeTiles,
3273
+ afterTiles
3274
+ };
3275
+ const target = this.target;
3276
+ const tileSize = this.config.tileSize;
3277
+ const accumulator = this.accumulator;
3278
+ const action = {
3279
+ undo: () => applyPatchTiles(target, patch.beforeTiles, tileSize),
3280
+ redo: () => applyPatchTiles(target, patch.afterTiles, tileSize),
3281
+ dispose: () => accumulator.recyclePatch(patch)
3282
+ };
3283
+ this.historyManager.commit(action);
3284
+ this.accumulator.reset();
3285
+ }
3286
+ };
3287
+
3288
+ // src/History/PixelMutator/mutatorApplyCirclePencil.ts
3289
+ var defaults15 = {
3290
+ applyCircleBrushToPixelData,
3291
+ getCircleBrushOrPencilBounds,
3292
+ fallOff: () => 1
3293
+ };
3294
+ var mutatorApplyCirclePencil = ((writer, deps = defaults15) => {
3295
+ const {
3296
+ applyCircleBrushToPixelData: applyCircleBrushToPixelData2 = defaults15.applyCircleBrushToPixelData,
3297
+ getCircleBrushOrPencilBounds: getCircleBrushOrPencilBounds2 = defaults15.getCircleBrushOrPencilBounds,
3298
+ fallOff = defaults15.fallOff
3299
+ } = deps;
3300
+ const boundsOut = {
3301
+ x: 0,
3302
+ y: 0,
3303
+ w: 0,
3304
+ h: 0
3305
+ };
3306
+ return {
3307
+ applyCirclePencil(color, centerX, centerY, brushSize, alpha = 255, blendFn) {
3308
+ const bounds = getCircleBrushOrPencilBounds2(centerX, centerY, brushSize, writer.target.width, writer.target.height, boundsOut);
3309
+ const {
3310
+ x,
3311
+ y,
3312
+ w,
3313
+ h
3314
+ } = bounds;
3315
+ writer.accumulator.storeRegionBeforeState(x, y, w, h);
3316
+ applyCircleBrushToPixelData2(writer.target, color, centerX, centerY, brushSize, alpha, fallOff, blendFn, bounds);
2367
3317
  }
2368
- return imageData;
2369
3318
  };
2370
- }
3319
+ });
2371
3320
 
2372
3321
  // src/ImageData/copyImageData.ts
2373
- function copyImageData({ data, width, height }) {
3322
+ function copyImageData({
3323
+ data,
3324
+ width,
3325
+ height
3326
+ }) {
2374
3327
  return new ImageData(data.slice(), width, height);
2375
3328
  }
2376
- function copyImageDataLike({ data, width, height }) {
3329
+ function copyImageDataLike({
3330
+ data,
3331
+ width,
3332
+ height
3333
+ }) {
2377
3334
  return {
2378
3335
  data: data.slice(),
2379
3336
  width,
@@ -2381,20 +3338,15 @@ function copyImageDataLike({ data, width, height }) {
2381
3338
  };
2382
3339
  }
2383
3340
 
2384
- // src/PixelData/pixelDataToAlphaMask.ts
2385
- function pixelDataToAlphaMask(pixelData) {
2386
- const {
2387
- data32,
3341
+ // src/ImageData/ImageDataLike.ts
3342
+ function makeImageDataLike(width, height, data) {
3343
+ const size = width * height * 4;
3344
+ const buffer = data ? new Uint8ClampedArray(data.buffer, data.byteOffset, size) : new Uint8ClampedArray(size);
3345
+ return {
2388
3346
  width,
2389
- height
2390
- } = pixelData;
2391
- const len = data32.length;
2392
- const mask = new Uint8Array(width * height);
2393
- for (let i = 0; i < len; i++) {
2394
- const val = data32[i];
2395
- mask[i] = val >>> 24 & 255;
2396
- }
2397
- return mask;
3347
+ height,
3348
+ data: buffer
3349
+ };
2398
3350
  }
2399
3351
 
2400
3352
  // src/ImageData/imageDataToAlphaMask.ts
@@ -2404,11 +3356,7 @@ function imageDataToAlphaMask(imageData) {
2404
3356
  height,
2405
3357
  data
2406
3358
  } = imageData;
2407
- const data32 = new Uint32Array(
2408
- data.buffer,
2409
- data.byteOffset,
2410
- data.byteLength >> 2
2411
- );
3359
+ const data32 = new Uint32Array(data.buffer, data.byteOffset, data.byteLength >> 2);
2412
3360
  const len = data32.length;
2413
3361
  const mask = new Uint8Array(width * height);
2414
3362
  for (let i = 0; i < len; i++) {
@@ -2421,7 +3369,10 @@ function imageDataToAlphaMask(imageData) {
2421
3369
  // src/ImageData/imageDataToDataUrl.ts
2422
3370
  var get = makeReusableCanvas();
2423
3371
  function imageDataToDataUrl(imageData) {
2424
- const { canvas, ctx } = get(imageData.width, imageData.height);
3372
+ const {
3373
+ canvas,
3374
+ ctx
3375
+ } = get(imageData.width, imageData.height);
2425
3376
  ctx.putImageData(imageData, 0, 0);
2426
3377
  return canvas.toDataURL();
2427
3378
  }
@@ -2479,7 +3430,11 @@ function resample32(srcData32, srcW, srcH, factor) {
2479
3430
  // src/ImageData/resampleImageData.ts
2480
3431
  function resampleImageData(source, factor) {
2481
3432
  const src32 = new Uint32Array(source.data.buffer);
2482
- const { data, width, height } = resample32(src32, source.width, source.height, factor);
3433
+ const {
3434
+ data,
3435
+ width,
3436
+ height
3437
+ } = resample32(src32, source.width, source.height, factor);
2483
3438
  const uint8ClampedArray = new Uint8ClampedArray(data.buffer);
2484
3439
  return new ImageData(uint8ClampedArray, width, height);
2485
3440
  }
@@ -2508,14 +3463,25 @@ function resizeImageData(current, newWidth, newHeight, offsetX = 0, offsetY = 0)
2508
3463
  const srcX = x0 - offsetX;
2509
3464
  const dstStart = (dstY * newWidth + x0) * 4;
2510
3465
  const srcStart = (srcY * oldW + srcX) * 4;
2511
- newData.set(
2512
- oldData.subarray(srcStart, srcStart + rowLen),
2513
- dstStart
2514
- );
3466
+ newData.set(oldData.subarray(srcStart, srcStart + rowLen), dstStart);
2515
3467
  }
2516
3468
  return result;
2517
3469
  }
2518
3470
 
3471
+ // src/ImageData/ReusableImageData.ts
3472
+ function makeReusableImageData() {
3473
+ let imageData = null;
3474
+ return function getReusableImageData(width, height) {
3475
+ const hasInstance = !!imageData;
3476
+ const widthMatches = hasInstance && imageData.width === width;
3477
+ const heightMatches = hasInstance && imageData.height === height;
3478
+ if (!widthMatches || !heightMatches) {
3479
+ imageData = new ImageData(width, height);
3480
+ }
3481
+ return imageData;
3482
+ };
3483
+ }
3484
+
2519
3485
  // src/ImageData/serialization.ts
2520
3486
  function base64EncodeArrayBuffer(buffer) {
2521
3487
  const uint8 = new Uint8Array(buffer);
@@ -2558,32 +3524,53 @@ function deserializeNullableImageData(serialized) {
2558
3524
  return deserializeImageData(serialized);
2559
3525
  }
2560
3526
 
3527
+ // src/ImageData/uInt32ArrayToImageData.ts
3528
+ function uInt32ArrayToImageData(data, width, height) {
3529
+ const buffer = data.buffer;
3530
+ const byteOffset = data.byteOffset;
3531
+ const byteLength = data.byteLength;
3532
+ const clampedArray = new Uint8ClampedArray(buffer, byteOffset, byteLength);
3533
+ return new ImageData(clampedArray, width, height);
3534
+ }
3535
+ function uInt32ArrayToImageDataLike(data, width, height) {
3536
+ const buffer = data.buffer;
3537
+ const byteOffset = data.byteOffset;
3538
+ const byteLength = data.byteLength;
3539
+ const clampedArray = new Uint8ClampedArray(buffer, byteOffset, byteLength);
3540
+ return {
3541
+ width,
3542
+ height,
3543
+ data: clampedArray
3544
+ };
3545
+ }
3546
+
2561
3547
  // src/ImageData/writeImageData.ts
3548
+ var SCRATCH_BLIT2 = makeClippedBlit();
2562
3549
  function writeImageData(target, source, x, y, sx = 0, sy = 0, sw = source.width, sh = source.height, mask = null, maskType = 1 /* BINARY */) {
2563
3550
  const dstW = target.width;
2564
3551
  const dstH = target.height;
2565
3552
  const dstData = target.data;
2566
3553
  const srcW = source.width;
2567
3554
  const srcData = source.data;
2568
- const x0 = Math.max(0, x);
2569
- const y0 = Math.max(0, y);
2570
- const x1 = Math.min(dstW, x + sw);
2571
- const y1 = Math.min(dstH, y + sh);
2572
- if (x1 <= x0 || y1 <= y0) {
2573
- return;
2574
- }
3555
+ const clip = resolveBlitClipping(x, y, sx, sy, sw, sh, dstW, dstH, srcW, source.height, SCRATCH_BLIT2);
3556
+ if (!clip.inBounds) return;
3557
+ const {
3558
+ x: dstX,
3559
+ y: dstY,
3560
+ sx: srcX,
3561
+ sy: srcY,
3562
+ w: copyW,
3563
+ h: copyH
3564
+ } = clip;
2575
3565
  const useMask = !!mask;
2576
- const rowCount = y1 - y0;
2577
- const rowLenPixels = x1 - x0;
2578
- for (let row = 0; row < rowCount; row++) {
2579
- const dstY = y0 + row;
2580
- const srcY = sy + (dstY - y);
2581
- const srcXBase = sx + (x0 - x);
2582
- const dstStart = (dstY * dstW + x0) * 4;
2583
- const srcStart = (srcY * srcW + srcXBase) * 4;
3566
+ for (let row = 0; row < copyH; row++) {
3567
+ const currentDstY = dstY + row;
3568
+ const currentSrcY = srcY + row;
3569
+ const dstStart = (currentDstY * dstW + dstX) * 4;
3570
+ const srcStart = (currentSrcY * srcW + srcX) * 4;
2584
3571
  if (useMask && mask) {
2585
- for (let ix = 0; ix < rowLenPixels; ix++) {
2586
- const mi = srcY * srcW + (srcXBase + ix);
3572
+ for (let ix = 0; ix < copyW; ix++) {
3573
+ const mi = currentSrcY * srcW + (srcX + ix);
2587
3574
  const alpha = mask[mi];
2588
3575
  if (alpha === 0) {
2589
3576
  continue;
@@ -2605,7 +3592,7 @@ function writeImageData(target, source, x, y, sx = 0, sy = 0, sw = source.width,
2605
3592
  }
2606
3593
  }
2607
3594
  } else {
2608
- const byteLen = rowLenPixels * 4;
3595
+ const byteLen = copyW * 4;
2609
3596
  const sub = srcData.subarray(srcStart, srcStart + byteLen);
2610
3597
  dstData.set(sub, dstStart);
2611
3598
  }
@@ -2613,24 +3600,52 @@ function writeImageData(target, source, x, y, sx = 0, sy = 0, sw = source.width,
2613
3600
  }
2614
3601
 
2615
3602
  // src/ImageData/writeImageDataBuffer.ts
3603
+ var SCRATCH_BLIT3 = makeClippedBlit();
2616
3604
  function writeImageDataBuffer(imageData, data, _x, _y, _w, _h) {
2617
- const { x, y, w, h } = typeof _x === "object" ? _x : { x: _x, y: _y, w: _w, h: _h };
2618
- const { width: dstW, height: dstH, data: dst } = imageData;
2619
- const x0 = Math.max(0, x);
2620
- const y0 = Math.max(0, y);
2621
- const x1 = Math.min(dstW, x + w);
2622
- const y1 = Math.min(dstH, y + h);
2623
- if (x1 <= x0 || y1 <= y0) return;
2624
- const rowLen = (x1 - x0) * 4;
2625
- const srcCol = x0 - x;
2626
- const srcYOffset = y0 - y;
2627
- const actualH = y1 - y0;
2628
- for (let row = 0; row < actualH; row++) {
2629
- const dstStart = ((y0 + row) * dstW + x0) * 4;
2630
- const srcRow = srcYOffset + row;
2631
- const o = (srcRow * w + srcCol) * 4;
2632
- dst.set(data.subarray(o, o + rowLen), dstStart);
3605
+ const {
3606
+ x,
3607
+ y,
3608
+ w,
3609
+ h
3610
+ } = typeof _x === "object" ? _x : {
3611
+ x: _x,
3612
+ y: _y,
3613
+ w: _w,
3614
+ h: _h
3615
+ };
3616
+ const {
3617
+ width: dstW,
3618
+ height: dstH,
3619
+ data: dst
3620
+ } = imageData;
3621
+ const clip = resolveBlitClipping(x, y, 0, 0, w, h, dstW, dstH, w, h, SCRATCH_BLIT3);
3622
+ if (!clip.inBounds) return;
3623
+ const {
3624
+ x: dstX,
3625
+ y: dstY,
3626
+ sx: srcX,
3627
+ sy: srcY,
3628
+ w: copyW,
3629
+ h: copyH
3630
+ } = clip;
3631
+ const rowLen = copyW * 4;
3632
+ for (let row = 0; row < copyH; row++) {
3633
+ const dstStart = ((dstY + row) * dstW + dstX) * 4;
3634
+ const srcStart = ((srcY + row) * w + srcX) * 4;
3635
+ dst.set(data.subarray(srcStart, srcStart + rowLen), dstStart);
3636
+ }
3637
+ }
3638
+
3639
+ // src/IndexedImage/getIndexedImageColorCounts.ts
3640
+ function getIndexedImageColorCounts(indexedImage) {
3641
+ const data = indexedImage.data;
3642
+ const palette = indexedImage.palette;
3643
+ const frequencies = new Int32Array(palette.length);
3644
+ for (let i = 0; i < data.length; i++) {
3645
+ const colorIndex = data[i];
3646
+ frequencies[colorIndex]++;
2633
3647
  }
3648
+ return frequencies;
2634
3649
  }
2635
3650
 
2636
3651
  // src/IndexedImage/IndexedImage.ts
@@ -2696,13 +3711,7 @@ var IndexedImage = class _IndexedImage {
2696
3711
  indexedData[i] = id;
2697
3712
  }
2698
3713
  const palette = Uint32Array.from(colorMap.keys());
2699
- return new _IndexedImage(
2700
- width,
2701
- height,
2702
- indexedData,
2703
- palette,
2704
- transparentPalletIndex
2705
- );
3714
+ return new _IndexedImage(width, height, indexedData, palette, transparentPalletIndex);
2706
3715
  }
2707
3716
  /**
2708
3717
  * Retrieves the 32-bit packed color value at the given coordinates.
@@ -2717,21 +3726,13 @@ var IndexedImage = class _IndexedImage {
2717
3726
  }
2718
3727
  };
2719
3728
 
2720
- // src/IndexedImage/getIndexedImageColorCounts.ts
2721
- function getIndexedImageColorCounts(indexedImage) {
2722
- const data = indexedImage.data;
2723
- const palette = indexedImage.palette;
2724
- const frequencies = new Int32Array(palette.length);
2725
- for (let i = 0; i < data.length; i++) {
2726
- const colorIndex = data[i];
2727
- frequencies[colorIndex]++;
2728
- }
2729
- return frequencies;
2730
- }
2731
-
2732
3729
  // src/IndexedImage/indexedImageToAverageColor.ts
2733
3730
  function indexedImageToAverageColor(indexedImage, includeTransparent = false) {
2734
- const { data, palette, transparentPalletIndex } = indexedImage;
3731
+ const {
3732
+ data,
3733
+ palette,
3734
+ transparentPalletIndex
3735
+ } = indexedImage;
2735
3736
  const counts = new Uint32Array(palette.length);
2736
3737
  for (let i = 0; i < data.length; i++) {
2737
3738
  const id = data[i];
@@ -2771,26 +3772,14 @@ function indexedImageToAverageColor(indexedImage, includeTransparent = false) {
2771
3772
  return packColor(r, g, b, a);
2772
3773
  }
2773
3774
 
2774
- // src/IndexedImage/resampleIndexedImage.ts
2775
- function resampleIndexedImage(source, factor) {
2776
- const { data, width, height } = resample32(
2777
- source.data,
2778
- source.width,
2779
- source.height,
2780
- factor
2781
- );
2782
- return new IndexedImage(
3775
+ // src/IndexedImage/indexedImageToImageData.ts
3776
+ function indexedImageToImageData(indexedImage) {
3777
+ const {
2783
3778
  width,
2784
3779
  height,
2785
3780
  data,
2786
- source.palette,
2787
- source.transparentPalletIndex
2788
- );
2789
- }
2790
-
2791
- // src/IndexedImage/indexedImageToImageData.ts
2792
- function indexedImageToImageData(indexedImage) {
2793
- const { width, height, data, palette } = indexedImage;
3781
+ palette
3782
+ } = indexedImage;
2794
3783
  const result = new ImageData(width, height);
2795
3784
  const data32 = new Uint32Array(result.data.buffer);
2796
3785
  for (let i = 0; i < data.length; i++) {
@@ -2801,6 +3790,16 @@ function indexedImageToImageData(indexedImage) {
2801
3790
  return result;
2802
3791
  }
2803
3792
 
3793
+ // src/IndexedImage/resampleIndexedImage.ts
3794
+ function resampleIndexedImage(source, factor) {
3795
+ const {
3796
+ data,
3797
+ width,
3798
+ height
3799
+ } = resample32(source.data, source.width, source.height, factor);
3800
+ return new IndexedImage(width, height, data, source.palette, source.transparentPalletIndex);
3801
+ }
3802
+
2804
3803
  // src/Input/fileInputChangeToImageData.ts
2805
3804
  async function fileInputChangeToImageData(event) {
2806
3805
  const target = event.target;
@@ -2824,23 +3823,11 @@ async function fileToImageData(file) {
2824
3823
  let bitmap = null;
2825
3824
  try {
2826
3825
  bitmap = await createImageBitmap(file);
2827
- const canvas = new OffscreenCanvas(
2828
- bitmap.width,
2829
- bitmap.height
2830
- );
3826
+ const canvas = new OffscreenCanvas(bitmap.width, bitmap.height);
2831
3827
  const ctx = canvas.getContext("2d");
2832
3828
  if (!ctx) throw new Error(OFFSCREEN_CANVAS_CTX_FAILED);
2833
- ctx.drawImage(
2834
- bitmap,
2835
- 0,
2836
- 0
2837
- );
2838
- return ctx.getImageData(
2839
- 0,
2840
- 0,
2841
- bitmap.width,
2842
- bitmap.height
2843
- );
3829
+ ctx.drawImage(bitmap, 0, 0);
3830
+ return ctx.getImageData(0, 0, bitmap.width, bitmap.height);
2844
3831
  } finally {
2845
3832
  bitmap?.close();
2846
3833
  }
@@ -2848,32 +3835,23 @@ async function fileToImageData(file) {
2848
3835
 
2849
3836
  // src/Input/getSupportedRasterFormats.ts
2850
3837
  var formatsPromise = null;
2851
- var defaultRasterMimes = [
2852
- "image/png",
2853
- "image/jpeg",
2854
- "image/webp",
2855
- "image/avif",
2856
- "image/gif",
2857
- "image/bmp"
2858
- ];
3838
+ var defaultRasterMimes = ["image/png", "image/jpeg", "image/webp", "image/avif", "image/gif", "image/bmp"];
2859
3839
  async function getSupportedPixelFormats(rasterMimes = defaultRasterMimes) {
2860
3840
  if (formatsPromise) {
2861
3841
  return formatsPromise;
2862
3842
  }
2863
3843
  const probeCanvas = async () => {
2864
3844
  const canvas = new OffscreenCanvas(1, 1);
2865
- const results = await Promise.all(
2866
- rasterMimes.map(async (mime) => {
2867
- try {
2868
- const blob = await canvas.convertToBlob({
2869
- type: mime
2870
- });
2871
- return blob.type === mime ? mime : null;
2872
- } catch {
2873
- return null;
2874
- }
2875
- })
2876
- );
3845
+ const results = await Promise.all(rasterMimes.map(async (mime) => {
3846
+ try {
3847
+ const blob = await canvas.convertToBlob({
3848
+ type: mime
3849
+ });
3850
+ return blob.type === mime ? mime : null;
3851
+ } catch {
3852
+ return null;
3853
+ }
3854
+ }));
2877
3855
  return results.filter((type) => {
2878
3856
  return type !== null;
2879
3857
  });
@@ -2885,6 +3863,73 @@ async function getSupportedPixelFormats(rasterMimes = defaultRasterMimes) {
2885
3863
  return formatsPromise;
2886
3864
  }
2887
3865
 
3866
+ // src/Mask/applyBinaryMaskToAlphaMask.ts
3867
+ function applyBinaryMaskToAlphaMask(alphaMaskDst, dstWidth, binaryMaskSrc, srcWidth, opts = {}) {
3868
+ const {
3869
+ x: targetX = 0,
3870
+ y: targetY = 0,
3871
+ w: reqWidth = 0,
3872
+ h: reqHeight = 0,
3873
+ mx = 0,
3874
+ my = 0,
3875
+ invertMask = false
3876
+ } = opts;
3877
+ if (dstWidth <= 0) return;
3878
+ if (binaryMaskSrc.length === 0) return;
3879
+ if (srcWidth <= 0) return;
3880
+ const dstHeight = alphaMaskDst.length / dstWidth | 0;
3881
+ const srcHeight = binaryMaskSrc.length / srcWidth | 0;
3882
+ if (dstHeight <= 0) return;
3883
+ if (srcHeight <= 0) return;
3884
+ const dstX0 = Math.max(0, targetX);
3885
+ const dstY0 = Math.max(0, targetY);
3886
+ const dstX1 = reqWidth > 0 ? Math.min(dstWidth, targetX + reqWidth) : dstWidth;
3887
+ const dstY1 = reqHeight > 0 ? Math.min(dstHeight, targetY + reqHeight) : dstHeight;
3888
+ if (dstX0 >= dstX1) return;
3889
+ if (dstY0 >= dstY1) return;
3890
+ const srcX0 = mx + (dstX0 - targetX);
3891
+ const srcY0 = my + (dstY0 - targetY);
3892
+ if (srcX0 >= srcWidth) return;
3893
+ if (srcY0 >= srcHeight) return;
3894
+ if (srcX0 + (dstX1 - dstX0) <= 0) return;
3895
+ if (srcY0 + (dstY1 - dstY0) <= 0) return;
3896
+ const iterW = Math.min(dstX1 - dstX0, srcWidth - srcX0);
3897
+ const iterH = Math.min(dstY1 - dstY0, srcHeight - srcY0);
3898
+ let dstIdx = dstY0 * dstWidth + dstX0;
3899
+ let srcIdx = srcY0 * srcWidth + srcX0;
3900
+ if (invertMask) {
3901
+ for (let row = 0; row < iterH; row++) {
3902
+ const dstEnd = dstIdx + iterW;
3903
+ let d = dstIdx;
3904
+ let s = srcIdx;
3905
+ while (d < dstEnd) {
3906
+ if (binaryMaskSrc[s] !== 0) {
3907
+ alphaMaskDst[d] = 0;
3908
+ }
3909
+ d++;
3910
+ s++;
3911
+ }
3912
+ dstIdx += dstWidth;
3913
+ srcIdx += srcWidth;
3914
+ }
3915
+ } else {
3916
+ for (let row = 0; row < iterH; row++) {
3917
+ const dstEnd = dstIdx + iterW;
3918
+ let d = dstIdx;
3919
+ let s = srcIdx;
3920
+ while (d < dstEnd) {
3921
+ if (binaryMaskSrc[s] === 0) {
3922
+ alphaMaskDst[d] = 0;
3923
+ }
3924
+ d++;
3925
+ s++;
3926
+ }
3927
+ dstIdx += dstWidth;
3928
+ srcIdx += srcWidth;
3929
+ }
3930
+ }
3931
+ }
3932
+
2888
3933
  // src/Mask/copyMask.ts
2889
3934
  function copyMask(src) {
2890
3935
  return src.slice();
@@ -2904,69 +3949,124 @@ function invertAlphaMask(dst) {
2904
3949
  }
2905
3950
  }
2906
3951
 
2907
- // src/Mask/mergeMasks.ts
2908
- function mergeMasks(dst, dstWidth, src, opts) {
3952
+ // src/Mask/mergeAlphaMasks.ts
3953
+ function mergeAlphaMasks(dst, dstWidth, src, srcWidth, opts) {
2909
3954
  const {
2910
3955
  x: targetX = 0,
2911
3956
  y: targetY = 0,
2912
3957
  w: width = 0,
2913
3958
  h: height = 0,
2914
3959
  alpha: globalAlpha = 255,
2915
- maskType = 0 /* ALPHA */,
2916
- mw,
2917
3960
  mx = 0,
2918
3961
  my = 0,
2919
3962
  invertMask = false
2920
3963
  } = opts;
2921
- if (width <= 0 || height <= 0 || globalAlpha === 0) {
2922
- return;
3964
+ const dstHeight = dst.length / dstWidth | 0;
3965
+ const srcHeight = src.length / srcWidth | 0;
3966
+ if (width <= 0) return;
3967
+ if (height <= 0) return;
3968
+ if (globalAlpha === 0) return;
3969
+ const startX = Math.max(0, -targetX, -mx);
3970
+ const startY = Math.max(0, -targetY, -my);
3971
+ const endX = Math.min(width, dstWidth - targetX, srcWidth - mx);
3972
+ const endY = Math.min(height, dstHeight - targetY, srcHeight - my);
3973
+ if (startX >= endX) return;
3974
+ if (startY >= endY) return;
3975
+ for (let iy = startY; iy < endY; iy++) {
3976
+ const dy = targetY + iy;
3977
+ const sy = my + iy;
3978
+ let dIdx = dy * dstWidth + targetX + startX;
3979
+ let sIdx = sy * srcWidth + mx + startX;
3980
+ for (let ix = startX; ix < endX; ix++) {
3981
+ const rawM = src[sIdx];
3982
+ const effectiveM = invertMask ? 255 - rawM : rawM;
3983
+ let weight = 0;
3984
+ if (effectiveM === 0) {
3985
+ weight = 0;
3986
+ } else if (effectiveM === 255) {
3987
+ weight = globalAlpha;
3988
+ } else if (globalAlpha === 255) {
3989
+ weight = effectiveM;
3990
+ } else {
3991
+ weight = effectiveM * globalAlpha + 128 >> 8;
3992
+ }
3993
+ if (weight !== 255) {
3994
+ if (weight === 0) {
3995
+ dst[dIdx] = 0;
3996
+ } else {
3997
+ const da = dst[dIdx];
3998
+ if (da === 255) {
3999
+ dst[dIdx] = weight;
4000
+ } else if (da !== 0) {
4001
+ dst[dIdx] = da * weight + 128 >> 8;
4002
+ }
4003
+ }
4004
+ }
4005
+ sIdx++;
4006
+ dIdx++;
4007
+ }
4008
+ }
4009
+ }
4010
+
4011
+ // src/Mask/mergeBinaryMasks.ts
4012
+ function mergeBinaryMasks(dst, dstWidth, src, srcWidth, opts) {
4013
+ const {
4014
+ x: targetX = 0,
4015
+ y: targetY = 0,
4016
+ w: width = 0,
4017
+ h: height = 0,
4018
+ mx = 0,
4019
+ my = 0,
4020
+ invertMask = false
4021
+ } = opts;
4022
+ if (dstWidth <= 0) return;
4023
+ if (srcWidth <= 0) return;
4024
+ const dstHeight = dst.length / dstWidth | 0;
4025
+ const srcHeight = src.length / srcWidth | 0;
4026
+ let x = targetX;
4027
+ let y = targetY;
4028
+ let w = width;
4029
+ let h = height;
4030
+ if (x < 0) {
4031
+ w += x;
4032
+ x = 0;
4033
+ }
4034
+ if (y < 0) {
4035
+ h += y;
4036
+ y = 0;
2923
4037
  }
2924
- const sPitch = mw ?? width;
2925
- const isAlpha = maskType === 0 /* ALPHA */;
2926
- for (let iy = 0; iy < height; iy++) {
2927
- const dy = targetY + iy;
2928
- const sy = my + iy;
2929
- if (dy < 0 || sy < 0) {
2930
- continue;
2931
- }
2932
- for (let ix = 0; ix < width; ix++) {
2933
- const dx = targetX + ix;
2934
- const sx = mx + ix;
2935
- if (dx < 0 || dx >= dstWidth || sx < 0 || sx >= sPitch) {
2936
- continue;
2937
- }
2938
- const dIdx = dy * dstWidth + dx;
2939
- const sIdx = sy * sPitch + sx;
4038
+ w = Math.min(w, dstWidth - x);
4039
+ h = Math.min(h, dstHeight - y);
4040
+ if (w <= 0) return;
4041
+ if (h <= 0) return;
4042
+ const startX = mx + (x - targetX);
4043
+ const startY = my + (y - targetY);
4044
+ const sX0 = Math.max(0, startX);
4045
+ const sY0 = Math.max(0, startY);
4046
+ const sX1 = Math.min(srcWidth, startX + w);
4047
+ const sY1 = Math.min(srcHeight, startY + h);
4048
+ const finalW = sX1 - sX0;
4049
+ const finalH = sY1 - sY0;
4050
+ if (finalW <= 0) return;
4051
+ if (finalH <= 0) return;
4052
+ const xShift = sX0 - startX;
4053
+ const yShift = sY0 - startY;
4054
+ const dStride = dstWidth - finalW;
4055
+ const sStride = srcWidth - finalW;
4056
+ let dIdx = (y + yShift) * dstWidth + (x + xShift);
4057
+ let sIdx = sY0 * srcWidth + sX0;
4058
+ for (let iy = 0; iy < finalH; iy++) {
4059
+ for (let ix = 0; ix < finalW; ix++) {
2940
4060
  const mVal = src[sIdx];
2941
- let weight = globalAlpha;
2942
- if (isAlpha) {
2943
- const effectiveM = invertMask ? 255 - mVal : mVal;
2944
- if (effectiveM === 0) {
2945
- dst[dIdx] = 0;
2946
- continue;
2947
- }
2948
- weight = globalAlpha === 255 ? effectiveM : effectiveM * globalAlpha + 128 >> 8;
2949
- } else {
2950
- const isHit = invertMask ? mVal === 0 : mVal === 1;
2951
- if (!isHit) {
2952
- dst[dIdx] = 0;
2953
- continue;
2954
- }
2955
- weight = globalAlpha;
2956
- }
2957
- if (weight === 0) {
4061
+ const isMaskedOut = invertMask ? mVal !== 0 : mVal === 0;
4062
+ if (isMaskedOut) {
2958
4063
  dst[dIdx] = 0;
2959
- continue;
2960
- }
2961
- const da = dst[dIdx];
2962
- if (da === 0) {
2963
- } else if (weight === 255) {
2964
- } else if (da === 255) {
2965
- dst[dIdx] = weight;
2966
- } else {
2967
- dst[dIdx] = da * weight + 128 >> 8;
2968
4064
  }
4065
+ dIdx++;
4066
+ sIdx++;
2969
4067
  }
4068
+ dIdx += dStride;
4069
+ sIdx += sStride;
2970
4070
  }
2971
4071
  }
2972
4072
 
@@ -2974,118 +4074,258 @@ function mergeMasks(dst, dstWidth, src, opts) {
2974
4074
  var PixelData = class _PixelData {
2975
4075
  data32;
2976
4076
  imageData;
2977
- get width() {
2978
- return this.imageData.width;
2979
- }
2980
- get height() {
2981
- return this.imageData.height;
2982
- }
4077
+ width;
4078
+ height;
2983
4079
  constructor(imageData) {
2984
4080
  this.data32 = imageDataToUInt32Array(imageData);
2985
4081
  this.imageData = imageData;
4082
+ this.width = imageData.width;
4083
+ this.height = imageData.height;
2986
4084
  }
2987
4085
  set(imageData) {
4086
+ ;
2988
4087
  this.imageData = imageData;
2989
4088
  this.data32 = imageDataToUInt32Array(imageData);
4089
+ this.width = imageData.width;
4090
+ this.height = imageData.height;
2990
4091
  }
2991
- /**
2992
- * Creates a deep copy of the PixelData using the environment's ImageData constructor.
2993
- */
4092
+ // should only be used for debug and testing
2994
4093
  copy() {
2995
- const buffer = new Uint8ClampedArray(this.imageData.data);
2996
- const ImageConstructor = typeof ImageData !== "undefined" ? ImageData : this.imageData.constructor;
2997
- const newImageData = new ImageConstructor(
2998
- buffer,
2999
- this.width,
3000
- this.height
3001
- );
4094
+ const data = this.imageData.data;
4095
+ const buffer = new Uint8ClampedArray(data);
4096
+ const Ctor = this.imageData.constructor;
4097
+ const isCtorValid = typeof Ctor === "function";
4098
+ let newImageData;
4099
+ if (isCtorValid && Ctor !== Object) {
4100
+ const ImageConstructor = Ctor;
4101
+ newImageData = new ImageConstructor(buffer, this.width, this.height);
4102
+ } else {
4103
+ newImageData = {
4104
+ width: this.width,
4105
+ height: this.height,
4106
+ data: buffer
4107
+ };
4108
+ }
3002
4109
  return new _PixelData(newImageData);
3003
4110
  }
3004
4111
  };
3005
4112
 
3006
- // src/PixelData/applyCircleBrushToPixelData.ts
3007
- function applyCircleBrushToPixelData(target, color, centerX, centerY, brushSize, alpha = 255, fallOff, blendFn = sourceOverPerfect) {
3008
- const r = brushSize / 2;
3009
- const rSqr = r * r;
3010
- const centerOffset = brushSize % 2 === 0 ? 0.5 : 0;
3011
- const xStart = Math.max(0, Math.ceil(centerX - r));
3012
- const xEnd = Math.min(target.width - 1, Math.floor(centerX + r));
3013
- const yStart = Math.max(0, Math.ceil(centerY - r));
3014
- const yEnd = Math.min(target.height - 1, Math.floor(centerY + r));
3015
- const data32 = target.data32;
3016
- const targetWidth = target.width;
3017
- const baseColor = color & 16777215;
3018
- const invR = 1 / r;
3019
- const constantSrc = (alpha << 24 | baseColor) >>> 0;
3020
- for (let cy = yStart; cy <= yEnd; cy++) {
3021
- const dy = cy - centerY + centerOffset;
3022
- const dySqr = dy * dy;
3023
- const rowOffset = cy * targetWidth;
3024
- for (let cx = xStart; cx <= xEnd; cx++) {
3025
- const dx = cx - centerX + centerOffset;
3026
- const dSqr = dx * dx + dySqr;
3027
- if (dSqr <= rSqr) {
3028
- const idx = rowOffset + cx;
3029
- if (fallOff) {
3030
- const strength = fallOff(Math.sqrt(dSqr) * invR);
3031
- const fAlpha = alpha * strength & 255;
3032
- const src = (fAlpha << 24 | baseColor) >>> 0;
3033
- data32[idx] = blendFn(src, data32[idx]);
3034
- } else {
3035
- data32[idx] = blendFn(constantSrc, data32[idx]);
4113
+ // src/PixelData/blendPixelDataAlphaMask.ts
4114
+ function blendPixelDataAlphaMask(dst, src, alphaMask, opts = {}) {
4115
+ const {
4116
+ x: targetX = 0,
4117
+ y: targetY = 0,
4118
+ sx: sourceX = 0,
4119
+ sy: sourceY = 0,
4120
+ w: width = src.width,
4121
+ h: height = src.height,
4122
+ alpha: globalAlpha = 255,
4123
+ blendFn = sourceOverPerfect,
4124
+ mw = src.width,
4125
+ mx = 0,
4126
+ my = 0,
4127
+ invertMask = false
4128
+ } = opts;
4129
+ if (globalAlpha === 0) return;
4130
+ let x = targetX;
4131
+ let y = targetY;
4132
+ let sx = sourceX;
4133
+ let sy = sourceY;
4134
+ let w = width;
4135
+ let h = height;
4136
+ if (sx < 0) {
4137
+ x -= sx;
4138
+ w += sx;
4139
+ sx = 0;
4140
+ }
4141
+ if (sy < 0) {
4142
+ y -= sy;
4143
+ h += sy;
4144
+ sy = 0;
4145
+ }
4146
+ w = Math.min(w, src.width - sx);
4147
+ h = Math.min(h, src.height - sy);
4148
+ if (x < 0) {
4149
+ sx -= x;
4150
+ w += x;
4151
+ x = 0;
4152
+ }
4153
+ if (y < 0) {
4154
+ sy -= y;
4155
+ h += y;
4156
+ y = 0;
4157
+ }
4158
+ const actualW = Math.min(w, dst.width - x);
4159
+ const actualH = Math.min(h, dst.height - y);
4160
+ if (actualW <= 0 || actualH <= 0) return;
4161
+ const dw = dst.width;
4162
+ const sw = src.width;
4163
+ const mPitch = mw;
4164
+ const dx = x - targetX | 0;
4165
+ const dy = y - targetY | 0;
4166
+ const dst32 = dst.data32;
4167
+ const src32 = src.data32;
4168
+ let dIdx = y * dw + x | 0;
4169
+ let sIdx = sy * sw + sx | 0;
4170
+ let mIdx = (my + dy) * mPitch + (mx + dx) | 0;
4171
+ const dStride = dw - actualW | 0;
4172
+ const sStride = sw - actualW | 0;
4173
+ const mStride = mPitch - actualW | 0;
4174
+ const isOpaque = globalAlpha === 255;
4175
+ const isOverwrite = blendFn.isOverwrite || false;
4176
+ for (let iy = 0; iy < actualH; iy++) {
4177
+ for (let ix = 0; ix < actualW; ix++) {
4178
+ const mVal = alphaMask[mIdx];
4179
+ const effM = invertMask ? 255 - mVal : mVal;
4180
+ if (effM === 0) {
4181
+ dIdx++;
4182
+ sIdx++;
4183
+ mIdx++;
4184
+ continue;
4185
+ }
4186
+ const srcCol = src32[sIdx];
4187
+ const srcAlpha = srcCol >>> 24;
4188
+ if (srcAlpha === 0 && !isOverwrite) {
4189
+ dIdx++;
4190
+ sIdx++;
4191
+ mIdx++;
4192
+ continue;
4193
+ }
4194
+ let weight = globalAlpha;
4195
+ if (isOpaque) {
4196
+ weight = effM;
4197
+ } else if (effM !== 255) {
4198
+ weight = effM * globalAlpha + 128 >> 8;
4199
+ }
4200
+ if (weight === 0) {
4201
+ dIdx++;
4202
+ sIdx++;
4203
+ mIdx++;
4204
+ continue;
4205
+ }
4206
+ let finalCol = srcCol;
4207
+ if (weight < 255) {
4208
+ const a = srcAlpha * weight + 128 >> 8;
4209
+ if (a === 0 && !isOverwrite) {
4210
+ dIdx++;
4211
+ sIdx++;
4212
+ mIdx++;
4213
+ continue;
3036
4214
  }
4215
+ finalCol = (srcCol & 16777215 | a << 24) >>> 0;
3037
4216
  }
4217
+ dst32[dIdx] = blendFn(finalCol, dst32[dIdx]);
4218
+ dIdx++;
4219
+ sIdx++;
4220
+ mIdx++;
3038
4221
  }
4222
+ dIdx += dStride;
4223
+ sIdx += sStride;
4224
+ mIdx += mStride;
3039
4225
  }
3040
4226
  }
3041
4227
 
3042
- // src/PixelData/applyRectBrushToPixelData.ts
3043
- function applyRectBrushToPixelData(target, color, centerX, centerY, brushWidth, brushHeight, alpha = 255, fallOff, blendFn = sourceOverPerfect) {
3044
- const targetWidth = target.width;
3045
- const targetHeight = target.height;
3046
- const data32 = target.data32;
3047
- const rawStartX = Math.floor(centerX - brushWidth / 2);
3048
- const rawStartY = Math.floor(centerY - brushHeight / 2);
3049
- const endX = Math.min(targetWidth, rawStartX + brushWidth);
3050
- const endY = Math.min(targetHeight, rawStartY + brushHeight);
3051
- const startX = Math.max(0, rawStartX);
3052
- const startY = Math.max(0, rawStartY);
3053
- const baseColor = color & 16777215;
3054
- const constantSrc = (alpha << 24 | baseColor) >>> 0;
3055
- const invHalfW = 1 / (brushWidth / 2);
3056
- const invHalfH = 1 / (brushHeight / 2);
3057
- for (let py = startY; py < endY; py++) {
3058
- const rowOffset = py * targetWidth;
3059
- const dy = Math.abs(py + 0.5 - centerY) * invHalfH;
3060
- for (let px = startX; px < endX; px++) {
3061
- if (fallOff) {
3062
- const dx = Math.abs(px + 0.5 - centerX) * invHalfW;
3063
- const dist = dx > dy ? dx : dy;
3064
- const fAlpha = alpha * fallOff(dist) | 0;
3065
- const src = (fAlpha << 24 | baseColor) >>> 0;
3066
- data32[rowOffset + px] = blendFn(src, data32[rowOffset + px]);
3067
- } else {
3068
- data32[rowOffset + px] = blendFn(constantSrc, data32[rowOffset + px]);
4228
+ // src/PixelData/blendPixelDataBinaryMask.ts
4229
+ function blendPixelDataBinaryMask(dst, src, binaryMask, opts) {
4230
+ const {
4231
+ x: targetX = 0,
4232
+ y: targetY = 0,
4233
+ sx: sourceX = 0,
4234
+ sy: sourceY = 0,
4235
+ w: width = src.width,
4236
+ h: height = src.height,
4237
+ alpha: globalAlpha = 255,
4238
+ blendFn = sourceOverPerfect,
4239
+ mw = src.width,
4240
+ mx = 0,
4241
+ my = 0,
4242
+ invertMask = false
4243
+ } = opts;
4244
+ if (globalAlpha === 0) return;
4245
+ let x = targetX;
4246
+ let y = targetY;
4247
+ let sx = sourceX;
4248
+ let sy = sourceY;
4249
+ let w = width;
4250
+ let h = height;
4251
+ if (sx < 0) {
4252
+ x -= sx;
4253
+ w += sx;
4254
+ sx = 0;
4255
+ }
4256
+ if (sy < 0) {
4257
+ y -= sy;
4258
+ h += sy;
4259
+ sy = 0;
4260
+ }
4261
+ w = Math.min(w, src.width - sx);
4262
+ h = Math.min(h, src.height - sy);
4263
+ if (x < 0) {
4264
+ sx -= x;
4265
+ w += x;
4266
+ x = 0;
4267
+ }
4268
+ if (y < 0) {
4269
+ sy -= y;
4270
+ h += y;
4271
+ y = 0;
4272
+ }
4273
+ const actualW = Math.min(w, dst.width - x);
4274
+ const actualH = Math.min(h, dst.height - y);
4275
+ if (actualW <= 0 || actualH <= 0) return;
4276
+ const dx = x - targetX | 0;
4277
+ const dy = y - targetY | 0;
4278
+ const dst32 = dst.data32;
4279
+ const src32 = src.data32;
4280
+ const dw = dst.width;
4281
+ const sw = src.width;
4282
+ const mPitch = mw;
4283
+ let dIdx = y * dw + x | 0;
4284
+ let sIdx = sy * sw + sx | 0;
4285
+ let mIdx = (my + dy) * mPitch + (mx + dx) | 0;
4286
+ const dStride = dw - actualW | 0;
4287
+ const sStride = sw - actualW | 0;
4288
+ const mStride = mPitch - actualW | 0;
4289
+ const skipVal = invertMask ? 1 : 0;
4290
+ const isOpaque = globalAlpha === 255;
4291
+ const isOverwrite = blendFn.isOverwrite || false;
4292
+ for (let iy = 0; iy < actualH; iy++) {
4293
+ for (let ix = 0; ix < actualW; ix++) {
4294
+ if (binaryMask[mIdx] === skipVal) {
4295
+ dIdx++;
4296
+ sIdx++;
4297
+ mIdx++;
4298
+ continue;
4299
+ }
4300
+ const srcCol = src32[sIdx];
4301
+ const srcAlpha = srcCol >>> 24;
4302
+ if (srcAlpha === 0 && !isOverwrite) {
4303
+ dIdx++;
4304
+ sIdx++;
4305
+ mIdx++;
4306
+ continue;
3069
4307
  }
4308
+ let finalCol = srcCol;
4309
+ if (!isOpaque) {
4310
+ const a = srcAlpha * globalAlpha + 128 >> 8;
4311
+ if (a === 0 && !isOverwrite) {
4312
+ dIdx++;
4313
+ sIdx++;
4314
+ mIdx++;
4315
+ continue;
4316
+ }
4317
+ finalCol = (srcCol & 16777215 | a << 24) >>> 0;
4318
+ }
4319
+ dst32[dIdx] = blendFn(finalCol, dst32[dIdx]);
4320
+ dIdx++;
4321
+ sIdx++;
4322
+ mIdx++;
3070
4323
  }
4324
+ dIdx += dStride;
4325
+ sIdx += sStride;
4326
+ mIdx += mStride;
3071
4327
  }
3072
4328
  }
3073
- function getRectBrushBounds(centerX, centerY, brushWidth, brushHeight, targetWidth, targetHeight) {
3074
- const rawStartX = Math.floor(centerX - brushWidth / 2);
3075
- const rawStartY = Math.floor(centerY - brushHeight / 2);
3076
- const rawEndX = rawStartX + brushWidth;
3077
- const rawEndY = rawStartY + brushHeight;
3078
- const startX = targetWidth !== void 0 ? Math.max(0, rawStartX) : rawStartX;
3079
- const startY = targetHeight !== void 0 ? Math.max(0, rawStartY) : rawStartY;
3080
- const endX = targetWidth !== void 0 ? Math.min(targetWidth, rawEndX) : rawEndX;
3081
- const endY = targetHeight !== void 0 ? Math.min(targetHeight, rawEndY) : rawEndY;
3082
- return {
3083
- x: startX,
3084
- y: startY,
3085
- w: endX - startX,
3086
- h: endY - startY
3087
- };
3088
- }
3089
4329
 
3090
4330
  // src/PixelData/clearPixelData.ts
3091
4331
  function clearPixelData(dst, rect) {
@@ -3093,32 +4333,40 @@ function clearPixelData(dst, rect) {
3093
4333
  }
3094
4334
 
3095
4335
  // src/PixelData/extractPixelDataBuffer.ts
4336
+ var SCRATCH_BLIT4 = makeClippedBlit();
3096
4337
  function extractPixelDataBuffer(source, _x, _y, _w, _h) {
3097
- const { x, y, w, h } = typeof _x === "object" ? _x : { x: _x, y: _y, w: _w, h: _h };
4338
+ const {
4339
+ x,
4340
+ y,
4341
+ w,
4342
+ h
4343
+ } = typeof _x === "object" ? _x : {
4344
+ x: _x,
4345
+ y: _y,
4346
+ w: _w,
4347
+ h: _h
4348
+ };
3098
4349
  const srcW = source.width;
3099
4350
  const srcH = source.height;
3100
4351
  const srcData = source.data32;
3101
4352
  if (w <= 0 || h <= 0) {
3102
4353
  return new Uint32Array(0);
3103
4354
  }
3104
- const dstImageData = new ImageData(w, h);
3105
- const dstData = new Uint32Array(dstImageData.data.buffer);
3106
- const x0 = Math.max(0, x);
3107
- const y0 = Math.max(0, y);
3108
- const x1 = Math.min(srcW, x + w);
3109
- const y1 = Math.min(srcH, y + h);
3110
- if (x1 <= x0 || y1 <= y0) {
3111
- return dstData;
3112
- }
3113
- const copyWidth = x1 - x0;
3114
- const copyHeight = y1 - y0;
3115
- for (let row = 0; row < copyHeight; row++) {
3116
- const srcRow = y0 + row;
3117
- const srcStart = srcRow * srcW + x0;
3118
- const dstRow = y0 - y + row;
3119
- const dstCol = x0 - x;
3120
- const dstStart = dstRow * w + dstCol;
3121
- const chunk = srcData.subarray(srcStart, srcStart + copyWidth);
4355
+ const dstData = new Uint32Array(w * h);
4356
+ const clip = resolveBlitClipping(0, 0, x, y, w, h, w, h, srcW, srcH, SCRATCH_BLIT4);
4357
+ if (!clip.inBounds) return dstData;
4358
+ const {
4359
+ x: dstX,
4360
+ y: dstY,
4361
+ sx: srcX,
4362
+ sy: srcY,
4363
+ w: copyW,
4364
+ h: copyH
4365
+ } = clip;
4366
+ for (let row = 0; row < copyH; row++) {
4367
+ const srcStart = (srcY + row) * srcW + srcX;
4368
+ const dstStart = (dstY + row) * w + dstX;
4369
+ const chunk = srcData.subarray(srcStart, srcStart + copyW);
3122
4370
  dstData.set(chunk, dstStart);
3123
4371
  }
3124
4372
  return dstData;
@@ -3126,13 +4374,59 @@ function extractPixelDataBuffer(source, _x, _y, _w, _h) {
3126
4374
 
3127
4375
  // src/PixelData/extractPixelData.ts
3128
4376
  function extractPixelData(source, _x, _y, _w, _h) {
3129
- const { x, y, w, h } = typeof _x === "object" ? _x : { x: _x, y: _y, w: _w, h: _h };
4377
+ const {
4378
+ x,
4379
+ y,
4380
+ w,
4381
+ h
4382
+ } = typeof _x === "object" ? _x : {
4383
+ x: _x,
4384
+ y: _y,
4385
+ w: _w,
4386
+ h: _h
4387
+ };
3130
4388
  const result = new PixelData(new ImageData(w, h));
3131
4389
  const buffer = extractPixelDataBuffer(source, x, y, w, h);
3132
4390
  result.data32.set(buffer);
3133
4391
  return result;
3134
4392
  }
3135
4393
 
4394
+ // src/PixelData/PixelBuffer32.ts
4395
+ var PixelBuffer32 = class _PixelBuffer32 {
4396
+ constructor(width, height, data32) {
4397
+ this.width = width;
4398
+ this.height = height;
4399
+ this.data32 = data32 ?? new Uint32Array(width * height);
4400
+ }
4401
+ data32;
4402
+ set(width, height, data32) {
4403
+ ;
4404
+ this.data32 = data32 ?? new Uint32Array(width * height);
4405
+ this.width = width;
4406
+ this.height = height;
4407
+ }
4408
+ copy() {
4409
+ const newData32 = new Uint32Array(this.data32);
4410
+ return new _PixelBuffer32(this.width, this.height, newData32);
4411
+ }
4412
+ };
4413
+
4414
+ // src/PixelData/pixelDataToAlphaMask.ts
4415
+ function pixelDataToAlphaMask(pixelData) {
4416
+ const {
4417
+ data32,
4418
+ width,
4419
+ height
4420
+ } = pixelData;
4421
+ const len = data32.length;
4422
+ const mask = new Uint8Array(width * height);
4423
+ for (let i = 0; i < len; i++) {
4424
+ const val = data32[i];
4425
+ mask[i] = val >>> 24 & 255;
4426
+ }
4427
+ return mask;
4428
+ }
4429
+
3136
4430
  // src/PixelData/reflectPixelData.ts
3137
4431
  function reflectPixelDataHorizontal(pixelData) {
3138
4432
  const width = pixelData.width;
@@ -3170,12 +4464,12 @@ function reflectPixelDataVertical(pixelData) {
3170
4464
 
3171
4465
  // src/PixelData/resamplePixelData.ts
3172
4466
  function resamplePixelData(pixelData, factor) {
3173
- const { data, width, height } = resample32(pixelData.data32, pixelData.width, pixelData.height, factor);
3174
- return new PixelData(new ImageData(
3175
- new Uint8ClampedArray(data.buffer),
4467
+ const {
4468
+ data,
3176
4469
  width,
3177
4470
  height
3178
- ));
4471
+ } = resample32(pixelData.data32, pixelData.width, pixelData.height, factor);
4472
+ return new PixelData(new ImageData(new Uint8ClampedArray(data.buffer), width, height));
3179
4473
  }
3180
4474
 
3181
4475
  // src/PixelData/rotatePixelData.ts
@@ -3199,11 +4493,7 @@ function rotatePixelData(pixelData) {
3199
4493
  newData32[newIdx] = data[oldIdx];
3200
4494
  }
3201
4495
  }
3202
- const newImageData = new ImageData(
3203
- new Uint8ClampedArray(newData32.buffer),
3204
- newWidth,
3205
- newHeight
3206
- );
4496
+ const newImageData = new ImageData(new Uint8ClampedArray(newData32.buffer), newWidth, newHeight);
3207
4497
  pixelData.set(newImageData);
3208
4498
  }
3209
4499
  function rotateSquareInPlace(pixelData) {
@@ -3225,8 +4515,14 @@ function rotateSquareInPlace(pixelData) {
3225
4515
  }
3226
4516
 
3227
4517
  // src/PixelData/writePixelDataBuffer.ts
4518
+ var SCRATCH_BLIT5 = makeClippedBlit();
3228
4519
  function writePixelDataBuffer(target, data, _x, _y, _w, _h) {
3229
- const { x, y, w, h } = typeof _x === "object" ? _x : {
4520
+ const {
4521
+ x,
4522
+ y,
4523
+ w,
4524
+ h
4525
+ } = typeof _x === "object" ? _x : {
3230
4526
  x: _x,
3231
4527
  y: _y,
3232
4528
  w: _w,
@@ -3235,45 +4531,52 @@ function writePixelDataBuffer(target, data, _x, _y, _w, _h) {
3235
4531
  const dstW = target.width;
3236
4532
  const dstH = target.height;
3237
4533
  const dstData = target.data32;
3238
- const x0 = Math.max(0, x);
3239
- const y0 = Math.max(0, y);
3240
- const x1 = Math.min(dstW, x + w);
3241
- const y1 = Math.min(dstH, y + h);
3242
- if (x1 <= x0 || y1 <= y0) {
3243
- return;
3244
- }
3245
- const rowLen = x1 - x0;
3246
- const srcCol = x0 - x;
3247
- const srcYOffset = y0 - y;
3248
- const actualH = y1 - y0;
3249
- for (let row = 0; row < actualH; row++) {
3250
- const dstStart = (y0 + row) * dstW + x0;
3251
- const srcRow = srcYOffset + row;
3252
- const srcStart = srcRow * w + srcCol;
3253
- dstData.set(data.subarray(srcStart, srcStart + rowLen), dstStart);
4534
+ const clip = resolveBlitClipping(x, y, 0, 0, w, h, dstW, dstH, w, h, SCRATCH_BLIT5);
4535
+ if (!clip.inBounds) return;
4536
+ const {
4537
+ x: dstX,
4538
+ y: dstY,
4539
+ sx: srcX,
4540
+ sy: srcY,
4541
+ w: copyW,
4542
+ h: copyH
4543
+ } = clip;
4544
+ for (let row = 0; row < copyH; row++) {
4545
+ const dstStart = (dstY + row) * dstW + dstX;
4546
+ const srcStart = (srcY + row) * w + srcX;
4547
+ dstData.set(data.subarray(srcStart, srcStart + copyW), dstStart);
3254
4548
  }
3255
4549
  }
3256
4550
  export {
3257
4551
  BASE_FAST_BLEND_MODE_FUNCTIONS,
3258
4552
  BASE_PERFECT_BLEND_MODE_FUNCTIONS,
3259
4553
  BaseBlendMode,
4554
+ CANVAS_CTX_FAILED,
3260
4555
  HistoryManager,
3261
4556
  IndexedImage,
3262
4557
  MaskType,
4558
+ OFFSCREEN_CANVAS_CTX_FAILED,
3263
4559
  PixelAccumulator,
4560
+ PixelBuffer32,
3264
4561
  PixelData,
3265
4562
  PixelEngineConfig,
3266
4563
  PixelTile,
3267
4564
  PixelWriter,
3268
4565
  UnsupportedFormatError,
4566
+ applyAlphaMaskToPixelData,
4567
+ applyBinaryMaskToAlphaMask,
4568
+ applyBinaryMaskToPixelData,
3269
4569
  applyCircleBrushToPixelData,
3270
- applyMaskToPixelData,
3271
4570
  applyPatchTiles,
3272
4571
  applyRectBrushToPixelData,
3273
4572
  base64DecodeArrayBuffer,
3274
4573
  base64EncodeArrayBuffer,
3275
4574
  blendColorPixelData,
4575
+ blendColorPixelDataAlphaMask,
4576
+ blendColorPixelDataBinaryMask,
3276
4577
  blendPixelData,
4578
+ blendPixelDataAlphaMask,
4579
+ blendPixelDataBinaryMask,
3277
4580
  clearPixelData,
3278
4581
  color32ToCssRGBA,
3279
4582
  color32ToHex,
@@ -3306,9 +4609,13 @@ export {
3306
4609
  fileToImageData,
3307
4610
  fillPixelData,
3308
4611
  floodFillSelection,
4612
+ forEachLinePoint,
4613
+ getCircleBrushOrPencilBounds,
4614
+ getCircleBrushOrPencilStrokeBounds,
3309
4615
  getImageDataFromClipboard,
3310
4616
  getIndexedImageColorCounts,
3311
- getRectBrushBounds,
4617
+ getRectBrushOrPencilBounds,
4618
+ getRectBrushOrPencilStrokeBounds,
3312
4619
  getSupportedPixelFormats,
3313
4620
  hardLightFast,
3314
4621
  hardLightPerfect,
@@ -3340,17 +4647,29 @@ export {
3340
4647
  makeBlendModeRegistry,
3341
4648
  makeFastBlendModeRegistry,
3342
4649
  makeFullPixelMutator,
4650
+ makeImageDataLike,
3343
4651
  makePerfectBlendModeRegistry,
3344
4652
  makePixelCanvas,
3345
4653
  makeReusableCanvas,
3346
4654
  makeReusableImageData,
3347
- mergeMasks,
4655
+ mergeAlphaMasks,
4656
+ mergeBinaryMasks,
3348
4657
  multiplyFast,
3349
4658
  multiplyPerfect,
3350
- mutatorApplyMask,
4659
+ mutatorApplyAlphaMask,
4660
+ mutatorApplyBinaryMask,
4661
+ mutatorApplyCircleBrush,
4662
+ mutatorApplyCircleBrushStroke,
4663
+ mutatorApplyCirclePencil,
4664
+ mutatorApplyCirclePencilStroke,
4665
+ mutatorApplyRectBrush,
4666
+ mutatorApplyRectBrushStroke,
4667
+ mutatorApplyRectPencil,
4668
+ mutatorApplyRectPencilStroke,
3351
4669
  mutatorBlendColor,
3352
4670
  mutatorBlendPixel,
3353
4671
  mutatorBlendPixelData,
4672
+ mutatorClear,
3354
4673
  mutatorFill,
3355
4674
  mutatorInvert,
3356
4675
  overlayFast,
@@ -3380,7 +4699,10 @@ export {
3380
4699
  sourceOverPerfect,
3381
4700
  subtractFast,
3382
4701
  subtractPerfect,
4702
+ toBlendModeIndexAndName,
3383
4703
  trimRectBounds,
4704
+ uInt32ArrayToImageData,
4705
+ uInt32ArrayToImageDataLike,
3384
4706
  unpackAlpha,
3385
4707
  unpackBlue,
3386
4708
  unpackColor,