pixel-data-js 0.18.0 → 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 (77) hide show
  1. package/README.md +6 -1
  2. package/dist/index.dev.cjs +2723 -1487
  3. package/dist/index.dev.cjs.map +1 -1
  4. package/dist/index.dev.js +2690 -1481
  5. package/dist/index.dev.js.map +1 -1
  6. package/dist/index.prod.cjs +2723 -1487
  7. package/dist/index.prod.cjs.map +1 -1
  8. package/dist/index.prod.d.ts +400 -246
  9. package/dist/index.prod.js +2690 -1481
  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 +23 -9
  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 +20 -7
  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 +23 -3
  34. package/src/History/PixelPatchTiles.ts +2 -2
  35. package/src/History/PixelWriter.ts +3 -3
  36. package/src/ImageData/ImageDataLike.ts +13 -0
  37. package/src/ImageData/extractImageDataBuffer.ts +22 -15
  38. package/src/ImageData/serialization.ts +4 -4
  39. package/src/ImageData/uInt32ArrayToImageData.ts +29 -0
  40. package/src/ImageData/writeImageData.ts +26 -18
  41. package/src/ImageData/writeImageDataBuffer.ts +30 -18
  42. package/src/IndexedImage/indexedImageToAverageColor.ts +1 -1
  43. package/src/Internal/resolveClipping.ts +140 -0
  44. package/src/Mask/applyBinaryMaskToAlphaMask.ts +89 -0
  45. package/src/Mask/copyMask.ts +1 -3
  46. package/src/Mask/mergeAlphaMasks.ts +81 -0
  47. package/src/Mask/mergeBinaryMasks.ts +89 -0
  48. package/src/PixelData/PixelBuffer32.ts +28 -0
  49. package/src/PixelData/PixelData.ts +38 -33
  50. package/src/PixelData/applyAlphaMaskToPixelData.ts +119 -0
  51. package/src/PixelData/applyBinaryMaskToPixelData.ts +111 -0
  52. package/src/PixelData/applyCircleBrushToPixelData.ts +31 -56
  53. package/src/PixelData/applyRectBrushToPixelData.ts +39 -71
  54. package/src/PixelData/blendColorPixelData.ts +18 -111
  55. package/src/PixelData/blendColorPixelDataAlphaMask.ts +111 -0
  56. package/src/PixelData/blendColorPixelDataBinaryMask.ts +89 -0
  57. package/src/PixelData/blendPixelData.ts +19 -107
  58. package/src/PixelData/blendPixelDataAlphaMask.ts +149 -0
  59. package/src/PixelData/blendPixelDataBinaryMask.ts +133 -0
  60. package/src/PixelData/clearPixelData.ts +2 -3
  61. package/src/PixelData/extractPixelData.ts +4 -4
  62. package/src/PixelData/extractPixelDataBuffer.ts +38 -26
  63. package/src/PixelData/fillPixelData.ts +18 -20
  64. package/src/PixelData/invertPixelData.ts +13 -21
  65. package/src/PixelData/pixelDataToAlphaMask.ts +2 -3
  66. package/src/PixelData/reflectPixelData.ts +3 -3
  67. package/src/PixelData/resamplePixelData.ts +2 -6
  68. package/src/PixelData/writePixelDataBuffer.ts +34 -20
  69. package/src/Rect/getCircleBrushOrPencilBounds.ts +43 -0
  70. package/src/Rect/getCircleBrushOrPencilStrokeBounds.ts +24 -0
  71. package/src/Rect/getRectBrushOrPencilBounds.ts +38 -0
  72. package/src/Rect/getRectBrushOrPencilStrokeBounds.ts +26 -0
  73. package/src/_types.ts +49 -33
  74. package/src/index.ts +47 -11
  75. package/src/History/PixelMutator/mutatorApplyMask.ts +0 -20
  76. package/src/Mask/mergeMasks.ts +0 -100
  77. 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 {
@@ -819,35 +416,514 @@ function floodFillSelection(img, startX, startY, {
819
416
  finalMask[my * sw + mx] = 1;
820
417
  }
821
418
  }
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
- );
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
+ }
833
489
  return {
834
- startX,
835
- startY,
836
- selectionRect,
837
- pixels: extracted
490
+ registryName,
491
+ nameToBlend,
492
+ nameToIndex,
493
+ blendToIndex,
494
+ blendToName,
495
+ indexToBlend,
496
+ indexToName,
497
+ indexType: null,
498
+ nameType: null
838
499
  };
839
500
  }
840
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;
603
+ }
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;
694
+ }
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);
914
+ }
915
+
841
916
  // src/BlendModes/blend-modes-perfect.ts
842
917
  var overwritePerfect = overwriteBase;
843
918
  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);
@@ -1668,11 +1828,7 @@ var PixelAccumulator = class {
1668
1828
  for (let i = 0; i < length; i++) {
1669
1829
  let beforeTile = this.beforeTiles[i];
1670
1830
  if (beforeTile) {
1671
- let afterTile = this.getTile(
1672
- beforeTile.id,
1673
- beforeTile.tx,
1674
- beforeTile.ty
1675
- );
1831
+ let afterTile = this.getTile(beforeTile.id, beforeTile.tx, beforeTile.ty);
1676
1832
  this.extractState(afterTile);
1677
1833
  afterTiles.push(afterTile);
1678
1834
  }
@@ -1685,64 +1841,6 @@ var PixelAccumulator = class {
1685
1841
  }
1686
1842
  };
1687
1843
 
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();
1733
- }
1734
- clearRedoStack() {
1735
- let length = this.redoStack.length;
1736
- for (let i = 0; i < length; i++) {
1737
- let action = this.redoStack[i];
1738
- if (action) {
1739
- action.dispose?.();
1740
- }
1741
- }
1742
- this.redoStack.length = 0;
1743
- }
1744
- };
1745
-
1746
1844
  // src/History/PixelEngineConfig.ts
1747
1845
  var PixelEngineConfig = class {
1748
1846
  tileSize;
@@ -1760,51 +1858,210 @@ var PixelEngineConfig = class {
1760
1858
  }
1761
1859
  };
1762
1860
 
1763
- // src/PixelData/applyCircleBrushToPixelData.ts
1764
- function applyCircleBrushToPixelData(target, color, centerX, centerY, brushSize, alpha = 255, fallOff, blendFn = sourceOverPerfect, bounds) {
1765
- const targetWidth = target.width;
1766
- const targetHeight = target.height;
1767
- const b = bounds ?? getCircleBrushBounds(
1768
- centerX,
1769
- centerY,
1770
- brushSize,
1771
- targetWidth,
1772
- targetHeight
1773
- );
1774
- if (b.w <= 0 || b.h <= 0) return;
1775
- const data32 = target.data32;
1776
- const r = brushSize / 2;
1777
- const rSqr = r * r;
1778
- const invR = 1 / r;
1779
- const centerOffset = brushSize % 2 === 0 ? 0.5 : 0;
1780
- const baseColor = color & 16777215;
1781
- const constantSrc = (alpha << 24 | baseColor) >>> 0;
1782
- const endX = b.x + b.w;
1783
- const endY = b.y + b.h;
1784
- const fCenterX = Math.floor(centerX);
1785
- const fCenterY = Math.floor(centerY);
1786
- for (let cy = b.y; cy < endY; cy++) {
1787
- const relY = cy - fCenterY + centerOffset;
1788
- const dySqr = relY * relY;
1789
- const rowOffset = cy * targetWidth;
1790
- for (let cx = b.x; cx < endX; cx++) {
1791
- const relX = cx - fCenterX + centerOffset;
1792
- const dSqr = relX * relX + dySqr;
1793
- if (dSqr <= rSqr) {
1794
- const idx = rowOffset + cx;
1795
- if (fallOff) {
1796
- const strength = fallOff(Math.sqrt(dSqr) * invR);
1797
- const fAlpha = alpha * strength & 255;
1798
- const src = (fAlpha << 24 | baseColor) >>> 0;
1799
- data32[idx] = blendFn(src, data32[idx]);
1800
- } else {
1801
- data32[idx] = blendFn(constantSrc, data32[idx]);
1861
+ // src/PixelData/applyAlphaMaskToPixelData.ts
1862
+ function applyAlphaMaskToPixelData(dst, mask, opts = {}) {
1863
+ const {
1864
+ x: targetX = 0,
1865
+ y: targetY = 0,
1866
+ w: width = dst.width,
1867
+ h: height = dst.height,
1868
+ alpha: globalAlpha = 255,
1869
+ mw,
1870
+ mx = 0,
1871
+ my = 0,
1872
+ invertMask = false
1873
+ } = opts;
1874
+ if (globalAlpha === 0) return;
1875
+ let x = targetX;
1876
+ let y = targetY;
1877
+ let w = width;
1878
+ let h = height;
1879
+ if (x < 0) {
1880
+ w += x;
1881
+ x = 0;
1882
+ }
1883
+ if (y < 0) {
1884
+ h += y;
1885
+ y = 0;
1886
+ }
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;
1802
2030
  }
1803
2031
  }
2032
+ dIdx++;
2033
+ mIdx++;
1804
2034
  }
2035
+ dIdx += dStride;
2036
+ mIdx += mStride;
1805
2037
  }
1806
2038
  }
1807
- function getCircleBrushBounds(centerX, centerY, brushSize, targetWidth, targetHeight, out) {
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) {
1808
2065
  const r = brushSize / 2;
1809
2066
  const minOffset = -Math.ceil(r - 0.5);
1810
2067
  const maxOffset = Math.floor(r - 0.5);
@@ -1818,104 +2075,137 @@ function getCircleBrushBounds(centerX, centerY, brushSize, targetWidth, targetHe
1818
2075
  w: 0,
1819
2076
  h: 0
1820
2077
  };
1821
- const cStartX = targetWidth !== void 0 ? Math.max(0, startX) : startX;
1822
- const cStartY = targetHeight !== void 0 ? Math.max(0, startY) : startY;
1823
- const cEndX = targetWidth !== void 0 ? Math.min(targetWidth, endX) : endX;
1824
- const cEndY = targetHeight !== void 0 ? Math.min(targetHeight, endY) : endY;
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;
1825
2084
  res.x = cStartX;
1826
2085
  res.y = cStartY;
1827
- res.w = Math.max(0, cEndX - cStartX);
1828
- res.h = Math.max(0, cEndY - cStartY);
2086
+ res.w = w < 0 ? 0 : w;
2087
+ res.h = h < 0 ? 0 : h;
1829
2088
  return res;
1830
2089
  }
1831
2090
 
1832
- // src/History/PixelWriter.ts
1833
- var PixelWriter = class {
1834
- target;
1835
- historyManager;
1836
- accumulator;
1837
- config;
1838
- mutator;
1839
- constructor(target, mutatorFactory, {
1840
- tileSize = 256,
1841
- maxHistorySteps = 50,
1842
- historyManager = new HistoryManager(maxHistorySteps)
1843
- } = {}) {
1844
- this.target = target;
1845
- this.config = new PixelEngineConfig(tileSize);
1846
- this.historyManager = historyManager;
1847
- this.accumulator = new PixelAccumulator(target, this.config);
1848
- this.mutator = mutatorFactory(this);
1849
- }
1850
- withHistory(cb) {
1851
- cb(this.mutator);
1852
- this.captureHistory();
1853
- }
1854
- captureHistory() {
1855
- const beforeTiles = this.accumulator.beforeTiles;
1856
- if (beforeTiles.length === 0) return;
1857
- const afterTiles = this.accumulator.extractAfterTiles();
1858
- const patch = {
1859
- beforeTiles,
1860
- afterTiles
1861
- };
1862
- const target = this.target;
1863
- const tileSize = this.config.tileSize;
1864
- const accumulator = this.accumulator;
1865
- const action = {
1866
- undo: () => applyPatchTiles(target, patch.beforeTiles, tileSize),
1867
- redo: () => applyPatchTiles(target, patch.afterTiles, tileSize),
1868
- dispose: () => accumulator.recyclePatch(patch)
1869
- };
1870
- this.historyManager.commit(action);
1871
- this.accumulator.reset();
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
+ }
1872
2137
  }
1873
- };
2138
+ }
1874
2139
 
1875
2140
  // src/History/PixelMutator/mutatorApplyCircleBrush.ts
1876
- var boundsOut = { x: 0, y: 0, w: 0, h: 0 };
1877
- function mutatorApplyCircleBrush(writer) {
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
+ };
1878
2156
  return {
1879
2157
  applyCircleBrush(color, centerX, centerY, brushSize, alpha = 255, fallOff, blendFn) {
1880
- const circleBounds = getCircleBrushBounds(
1881
- centerX,
1882
- centerY,
1883
- brushSize,
1884
- writer.target.width,
1885
- writer.target.height,
1886
- boundsOut
1887
- );
1888
- const { x, y, w, h } = circleBounds;
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;
1889
2165
  writer.accumulator.storeRegionBeforeState(x, y, w, h);
1890
- applyCircleBrushToPixelData(
1891
- writer.target,
1892
- color,
1893
- centerX,
1894
- centerY,
1895
- brushSize,
1896
- alpha,
1897
- fallOff,
1898
- blendFn,
1899
- circleBounds
1900
- );
2166
+ applyCircleBrushToPixelData2(writer.target, color, centerX, centerY, brushSize, alpha, fallOff, blendFn, bounds);
1901
2167
  }
1902
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);
2178
+ return;
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
+ }
1903
2189
  }
1904
2190
 
1905
- // src/PixelData/applyMaskToPixelData.ts
1906
- function applyMaskToPixelData(dst, mask, opts = {}) {
2191
+ // src/PixelData/blendColorPixelDataAlphaMask.ts
2192
+ function blendColorPixelDataAlphaMask(dst, color, mask, opts) {
1907
2193
  const {
1908
2194
  x: targetX = 0,
1909
2195
  y: targetY = 0,
1910
2196
  w: width = dst.width,
1911
2197
  h: height = dst.height,
1912
2198
  alpha: globalAlpha = 255,
1913
- maskType = 0 /* ALPHA */,
1914
- mw,
2199
+ blendFn = sourceOverPerfect,
2200
+ mw = width,
1915
2201
  mx = 0,
1916
2202
  my = 0,
1917
2203
  invertMask = false
1918
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;
1919
2209
  let x = targetX;
1920
2210
  let y = targetY;
1921
2211
  let w = width;
@@ -1930,57 +2220,49 @@ function applyMaskToPixelData(dst, mask, opts = {}) {
1930
2220
  }
1931
2221
  const actualW = Math.min(w, dst.width - x);
1932
2222
  const actualH = Math.min(h, dst.height - y);
1933
- if (actualW <= 0 || actualH <= 0 || globalAlpha === 0) {
1934
- return;
1935
- }
2223
+ if (actualW <= 0 || actualH <= 0) return;
2224
+ const dx = x - targetX | 0;
2225
+ const dy = y - targetY | 0;
1936
2226
  const dst32 = dst.data32;
1937
2227
  const dw = dst.width;
1938
- const mPitch = mw ?? width;
1939
- const isAlpha = maskType === 0 /* ALPHA */;
1940
- const dx = x - targetX;
1941
- const dy = y - targetY;
1942
- let dIdx = y * dw + x;
1943
- let mIdx = (my + dy) * mPitch + (mx + dx);
1944
- const dStride = dw - actualW;
1945
- 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;
1946
2235
  for (let iy = 0; iy < actualH; iy++) {
1947
2236
  for (let ix = 0; ix < actualW; ix++) {
1948
2237
  const mVal = mask[mIdx];
2238
+ const effM = invertMask ? 255 - mVal : mVal;
2239
+ if (effM === 0) {
2240
+ dIdx++;
2241
+ mIdx++;
2242
+ continue;
2243
+ }
1949
2244
  let weight = globalAlpha;
1950
- if (isAlpha) {
1951
- const effectiveM = invertMask ? 255 - mVal : mVal;
1952
- if (effectiveM === 0) {
1953
- dst32[dIdx] = (dst32[dIdx] & 16777215) >>> 0;
1954
- dIdx++;
1955
- mIdx++;
1956
- continue;
1957
- }
1958
- weight = globalAlpha === 255 ? effectiveM : effectiveM * globalAlpha + 128 >> 8;
1959
- } else {
1960
- const isHit = invertMask ? mVal === 0 : mVal === 1;
1961
- if (!isHit) {
1962
- 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) {
1963
2259
  dIdx++;
1964
2260
  mIdx++;
1965
2261
  continue;
1966
2262
  }
1967
- weight = globalAlpha;
1968
- }
1969
- if (weight === 0) {
1970
- dst32[dIdx] = (dst32[dIdx] & 16777215) >>> 0;
1971
- } else {
1972
- const d = dst32[dIdx];
1973
- const da = d >>> 24;
1974
- let finalAlpha = da;
1975
- if (da === 0) {
1976
- } else if (weight === 255) {
1977
- } else if (da === 255) {
1978
- finalAlpha = weight;
1979
- } else {
1980
- finalAlpha = da * weight + 128 >> 8;
1981
- }
1982
- dst32[dIdx] = (d & 16777215 | finalAlpha << 24) >>> 0;
2263
+ finalCol = (colorRGB | a << 24) >>> 0;
1983
2264
  }
2265
+ dst32[dIdx] = blendFn(finalCol, dst32[dIdx]);
1984
2266
  dIdx++;
1985
2267
  mIdx++;
1986
2268
  }
@@ -1989,159 +2271,595 @@ function applyMaskToPixelData(dst, mask, opts = {}) {
1989
2271
  }
1990
2272
  }
1991
2273
 
1992
- // src/History/PixelMutator/mutatorApplyMask.ts
1993
- function mutatorApplyMask(writer) {
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
+ };
1994
2322
  return {
1995
- applyMask: (mask, opts = {}) => {
1996
- let target = writer.target;
2323
+ applyCircleBrushStroke(color, x0, y0, x1, y1, brushSize, alpha = 255, fallOff, blendFn = sourceOverPerfect) {
1997
2324
  const {
1998
- x = 0,
1999
- y = 0,
2000
- w = writer.target.width,
2001
- h = writer.target.height
2002
- } = opts;
2003
- writer.accumulator.storeRegionBeforeState(x, y, w, h);
2004
- applyMaskToPixelData(target, mask, opts);
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
+ }
2368
+ }
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);
2005
2377
  }
2006
2378
  };
2007
- }
2008
-
2009
- // src/ImageData/imageDataToUInt32Array.ts
2010
- function imageDataToUInt32Array(imageData) {
2011
- return new Uint32Array(
2012
- imageData.data.buffer,
2013
- imageData.data.byteOffset,
2014
- // Shift right by 2 is a fast bitwise division by 4.
2015
- imageData.data.byteLength >> 2
2016
- );
2017
- }
2379
+ });
2018
2380
 
2019
- // src/PixelData/PixelData.ts
2020
- var PixelData = class _PixelData {
2021
- data32;
2022
- imageData;
2023
- get width() {
2024
- return this.imageData.width;
2025
- }
2026
- get height() {
2027
- return this.imageData.height;
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;
2028
2406
  }
2029
- constructor(imageData) {
2030
- this.data32 = imageDataToUInt32Array(imageData);
2031
- this.imageData = imageData;
2407
+ if (y < 0) {
2408
+ h += y;
2409
+ y = 0;
2032
2410
  }
2033
- set(imageData) {
2034
- this.imageData = imageData;
2035
- this.data32 = imageDataToUInt32Array(imageData);
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;
2036
2419
  }
2037
- /**
2038
- * Creates a deep copy of the PixelData using the environment's ImageData constructor.
2039
- */
2040
- copy() {
2041
- const buffer = new Uint8ClampedArray(this.imageData.data);
2042
- const ImageConstructor = typeof ImageData !== "undefined" ? ImageData : this.imageData.constructor;
2043
- const newImageData = new ImageConstructor(
2044
- buffer,
2045
- this.width,
2046
- this.height
2047
- );
2048
- return new _PixelData(newImageData);
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;
2436
+ }
2437
+ dst32[dIdx] = blendFn(baseColorWithGlobalAlpha, dst32[dIdx]);
2438
+ dIdx++;
2439
+ mIdx++;
2440
+ }
2441
+ dIdx += dStride;
2442
+ mIdx += mStride;
2049
2443
  }
2444
+ }
2445
+
2446
+ // src/History/PixelMutator/mutatorApplyCirclePencilStroke.ts
2447
+ var defaults5 = {
2448
+ forEachLinePoint,
2449
+ blendColorPixelDataBinaryMask,
2450
+ getCircleBrushOrPencilBounds,
2451
+ getCircleBrushOrPencilStrokeBounds
2050
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
+ }
2051
2558
 
2052
2559
  // src/PixelData/applyRectBrushToPixelData.ts
2053
2560
  function applyRectBrushToPixelData(target, color, centerX, centerY, brushWidth, brushHeight, alpha = 255, fallOff, blendFn = sourceOverPerfect, bounds) {
2054
2561
  const targetWidth = target.width;
2055
2562
  const targetHeight = target.height;
2056
- const b = bounds ?? getRectBrushBounds(
2057
- centerX,
2058
- centerY,
2059
- brushWidth,
2060
- brushHeight,
2061
- targetWidth,
2062
- targetHeight
2063
- );
2563
+ const b = bounds ?? getRectBrushOrPencilBounds(centerX, centerY, brushWidth, brushHeight, targetWidth, targetHeight);
2064
2564
  if (b.w <= 0 || b.h <= 0) return;
2065
2565
  const data32 = target.data32;
2066
2566
  const baseColor = color & 16777215;
2067
- const constantSrc = (alpha << 24 | baseColor) >>> 0;
2567
+ const baseSrcAlpha = color >>> 24;
2568
+ const isOpaque = alpha === 255;
2068
2569
  const invHalfW = 1 / (brushWidth / 2);
2069
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);
2070
2575
  const endX = b.x + b.w;
2071
2576
  const endY = b.y + b.h;
2577
+ const isOverwrite = blendFn.isOverwrite;
2072
2578
  for (let py = b.y; py < endY; py++) {
2073
2579
  const rowOffset = py * targetWidth;
2074
- const dy = fallOff ? Math.abs(py + 0.5 - centerY) * invHalfH : 0;
2580
+ const dy = Math.abs(py - fCenterY + centerOffsetY) * invHalfH;
2075
2581
  for (let px = b.x; px < endX; px++) {
2076
2582
  const idx = rowOffset + px;
2077
- if (fallOff) {
2078
- const dx = Math.abs(px + 0.5 - centerX) * invHalfW;
2079
- const dist = dx > dy ? dx : dy;
2080
- const strength = fallOff(dist);
2081
- const fAlpha = alpha * strength | 0;
2082
- const src = (fAlpha << 24 | baseColor) >>> 0;
2083
- data32[idx] = blendFn(src, data32[idx]);
2084
- } else {
2085
- data32[idx] = blendFn(constantSrc, data32[idx]);
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;
2086
2599
  }
2600
+ data32[idx] = blendFn(finalCol, data32[idx]);
2087
2601
  }
2088
2602
  }
2089
2603
  }
2090
- function getRectBrushBounds(centerX, centerY, brushWidth, brushHeight, targetWidth, targetHeight, out) {
2091
- const startX = Math.floor(centerX - brushWidth / 2);
2092
- const startY = Math.floor(centerY - brushHeight / 2);
2093
- const endX = startX + brushWidth;
2094
- const endY = startY + brushHeight;
2095
- const res = out ?? {
2604
+
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
+ };
2621
+ return {
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);
2624
+ const {
2625
+ x,
2626
+ y,
2627
+ w,
2628
+ h
2629
+ } = bounds;
2630
+ writer.accumulator.storeRegionBeforeState(x, y, w, h);
2631
+ applyRectBrushToPixelData2(writer.target, color, centerX, centerY, brushWidth, brushHeight, alpha, fallOff, blendFn, bounds);
2632
+ }
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;
2649
+ }
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,
2096
2680
  x: 0,
2097
2681
  y: 0,
2098
2682
  w: 0,
2099
2683
  h: 0
2100
2684
  };
2101
- const cStartX = targetWidth !== void 0 ? Math.max(0, startX) : startX;
2102
- const cStartY = targetHeight !== void 0 ? Math.max(0, startY) : startY;
2103
- const cEndX = targetWidth !== void 0 ? Math.min(targetWidth, endX) : endX;
2104
- const cEndY = targetHeight !== void 0 ? Math.min(targetHeight, endY) : endY;
2105
- const w = cEndX - cStartX;
2106
- const h = cEndY - cStartY;
2107
- res.x = cStartX;
2108
- res.y = cStartY;
2109
- res.w = w < 0 ? 0 : w;
2110
- res.h = h < 0 ? 0 : h;
2111
- return res;
2112
- }
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
+ });
2113
2744
 
2114
- // src/History/PixelMutator/mutatorApplyRectBrush.ts
2115
- var boundsOut2 = { x: 0, y: 0, w: 0, h: 0 };
2116
- function mutatorApplyRectBrush(writer) {
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
+ };
2117
2763
  return {
2118
- applyRectBrush(color, centerX, centerY, brushWidth, brushHeight, alpha = 255, fallOff, blendFn) {
2119
- const bounds = getRectBrushBounds(
2120
- centerX,
2121
- centerY,
2122
- brushWidth,
2123
- brushHeight,
2124
- writer.target.width,
2125
- writer.target.height,
2126
- boundsOut2
2127
- );
2128
- const { x, y, w, h } = bounds;
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;
2129
2772
  writer.accumulator.storeRegionBeforeState(x, y, w, h);
2130
- applyRectBrushToPixelData(
2131
- writer.target,
2132
- color,
2133
- centerX,
2134
- centerY,
2135
- brushWidth,
2136
- brushHeight,
2137
- alpha,
2138
- fallOff,
2139
- blendFn,
2140
- bounds
2141
- );
2773
+ applyRectBrushToPixelData2(writer.target, color, centerX, centerY, brushWidth, brushHeight, alpha, fallOff, blendFn, bounds);
2142
2774
  }
2143
2775
  };
2144
- }
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
+ });
2145
2863
 
2146
2864
  // src/PixelData/blendColorPixelData.ts
2147
2865
  function blendColorPixelData(dst, color, opts = {}) {
@@ -2151,22 +2869,13 @@ function blendColorPixelData(dst, color, opts = {}) {
2151
2869
  w: width = dst.width,
2152
2870
  h: height = dst.height,
2153
2871
  alpha: globalAlpha = 255,
2154
- blendFn = sourceOverFast,
2155
- mask,
2156
- maskType = 0 /* ALPHA */,
2157
- mw,
2158
- mx = 0,
2159
- my = 0,
2160
- invertMask = false
2872
+ blendFn = sourceOverPerfect
2161
2873
  } = opts;
2162
2874
  if (globalAlpha === 0) return;
2163
2875
  const baseSrcAlpha = color >>> 24;
2164
- const isOverwrite = blendFn.isOverwrite;
2876
+ const isOverwrite = blendFn.isOverwrite || false;
2165
2877
  if (baseSrcAlpha === 0 && !isOverwrite) return;
2166
- let x = targetX;
2167
- let y = targetY;
2168
- let w = width;
2169
- let h = height;
2878
+ let x = targetX, y = targetY, w = width, h = height;
2170
2879
  if (x < 0) {
2171
2880
  w += x;
2172
2881
  x = 0;
@@ -2178,76 +2887,33 @@ function blendColorPixelData(dst, color, opts = {}) {
2178
2887
  const actualW = Math.min(w, dst.width - x);
2179
2888
  const actualH = Math.min(h, dst.height - y);
2180
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
+ }
2181
2896
  const dst32 = dst.data32;
2182
2897
  const dw = dst.width;
2183
- const mPitch = mw ?? width;
2184
- const isAlphaMask = maskType === 0 /* ALPHA */;
2185
- const dx = x - targetX;
2186
- const dy = y - targetY;
2187
- let dIdx = y * dw + x;
2188
- let mIdx = (my + dy) * mPitch + (mx + dx);
2189
- const dStride = dw - actualW;
2190
- const mStride = mPitch - actualW;
2898
+ let dIdx = y * dw + x | 0;
2899
+ const dStride = dw - actualW | 0;
2191
2900
  for (let iy = 0; iy < actualH; iy++) {
2192
2901
  for (let ix = 0; ix < actualW; ix++) {
2193
- let weight = globalAlpha;
2194
- if (mask) {
2195
- const mVal = mask[mIdx];
2196
- if (isAlphaMask) {
2197
- const effectiveM = invertMask ? 255 - mVal : mVal;
2198
- if (effectiveM === 0) {
2199
- dIdx++;
2200
- mIdx++;
2201
- continue;
2202
- }
2203
- if (globalAlpha === 255) {
2204
- weight = effectiveM;
2205
- } else if (effectiveM === 255) {
2206
- weight = globalAlpha;
2207
- } else {
2208
- weight = effectiveM * globalAlpha + 128 >> 8;
2209
- }
2210
- } else {
2211
- const isHit = invertMask ? mVal === 0 : mVal === 1;
2212
- if (!isHit) {
2213
- dIdx++;
2214
- mIdx++;
2215
- continue;
2216
- }
2217
- weight = globalAlpha;
2218
- }
2219
- if (weight === 0) {
2220
- dIdx++;
2221
- mIdx++;
2222
- continue;
2223
- }
2224
- }
2225
- let currentSrcColor = color;
2226
- if (weight < 255) {
2227
- let currentSrcAlpha = baseSrcAlpha;
2228
- if (baseSrcAlpha === 255) {
2229
- currentSrcAlpha = weight;
2230
- } else {
2231
- currentSrcAlpha = baseSrcAlpha * weight + 128 >> 8;
2232
- }
2233
- if (!isOverwrite && currentSrcAlpha === 0) {
2234
- dIdx++;
2235
- mIdx++;
2236
- continue;
2237
- }
2238
- currentSrcColor = (color & 16777215 | currentSrcAlpha << 24) >>> 0;
2239
- }
2240
- dst32[dIdx] = blendFn(currentSrcColor, dst32[dIdx]);
2902
+ dst32[dIdx] = blendFn(finalSrcColor, dst32[dIdx]);
2241
2903
  dIdx++;
2242
- mIdx++;
2243
2904
  }
2244
2905
  dIdx += dStride;
2245
- mIdx += mStride;
2246
2906
  }
2247
2907
  }
2248
2908
 
2249
2909
  // src/History/PixelMutator/mutatorBlendColor.ts
2250
- function mutatorBlendColor(writer) {
2910
+ var defaults10 = {
2911
+ blendColorPixelData
2912
+ };
2913
+ var mutatorBlendColor = ((writer, deps = defaults10) => {
2914
+ const {
2915
+ blendColorPixelData: blendColorPixelData2 = defaults10.blendColorPixelData
2916
+ } = deps;
2251
2917
  return {
2252
2918
  blendColor(color, opts = {}) {
2253
2919
  const {
@@ -2257,10 +2923,10 @@ function mutatorBlendColor(writer) {
2257
2923
  h = writer.target.height
2258
2924
  } = opts;
2259
2925
  writer.accumulator.storeRegionBeforeState(x, y, w, h);
2260
- blendColorPixelData(writer.target, color, opts);
2926
+ blendColorPixelData2(writer.target, color, opts);
2261
2927
  }
2262
2928
  };
2263
- }
2929
+ });
2264
2930
 
2265
2931
  // src/History/PixelMutator/mutatorBlendPixel.ts
2266
2932
  function mutatorBlendPixel(writer) {
@@ -2294,13 +2960,7 @@ function blendPixelData(dst, src, opts) {
2294
2960
  w: width = src.width,
2295
2961
  h: height = src.height,
2296
2962
  alpha: globalAlpha = 255,
2297
- blendFn = sourceOverFast,
2298
- mask,
2299
- maskType = 0 /* ALPHA */,
2300
- mw,
2301
- mx = 0,
2302
- my = 0,
2303
- invertMask = false
2963
+ blendFn = sourceOverPerfect
2304
2964
  } = opts;
2305
2965
  if (globalAlpha === 0) return;
2306
2966
  let x = targetX;
@@ -2338,91 +2998,48 @@ function blendPixelData(dst, src, opts) {
2338
2998
  const src32 = src.data32;
2339
2999
  const dw = dst.width;
2340
3000
  const sw = src.width;
2341
- const mPitch = mw ?? width;
2342
- const isAlphaMask = maskType === 0 /* ALPHA */;
2343
- const dx = x - targetX;
2344
- const dy = y - targetY;
2345
- let dIdx = y * dw + x;
2346
- let sIdx = sy * sw + sx;
2347
- let mIdx = (my + dy) * mPitch + (mx + dx);
2348
- const dStride = dw - actualW;
2349
- const sStride = sw - actualW;
2350
- 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;
2351
3006
  const isOverwrite = blendFn.isOverwrite;
2352
3007
  for (let iy = 0; iy < actualH; iy++) {
2353
3008
  for (let ix = 0; ix < actualW; ix++) {
2354
- const baseSrcColor = src32[sIdx];
2355
- const baseSrcAlpha = baseSrcColor >>> 24;
2356
- if (baseSrcAlpha === 0 && !isOverwrite) {
3009
+ const srcCol = src32[sIdx];
3010
+ const srcAlpha = srcCol >>> 24;
3011
+ if (srcAlpha === 0 && !isOverwrite) {
2357
3012
  dIdx++;
2358
3013
  sIdx++;
2359
- mIdx++;
2360
3014
  continue;
2361
3015
  }
2362
- let weight = globalAlpha;
2363
- if (mask) {
2364
- const mVal = mask[mIdx];
2365
- if (isAlphaMask) {
2366
- const effectiveM = invertMask ? 255 - mVal : mVal;
2367
- if (effectiveM === 0) {
2368
- dIdx++;
2369
- sIdx++;
2370
- mIdx++;
2371
- continue;
2372
- }
2373
- if (globalAlpha === 255) {
2374
- weight = effectiveM;
2375
- } else if (effectiveM === 255) {
2376
- weight = globalAlpha;
2377
- } else {
2378
- weight = effectiveM * globalAlpha + 128 >> 8;
2379
- }
2380
- } else {
2381
- const isHit = invertMask ? mVal === 0 : mVal === 1;
2382
- if (!isHit) {
2383
- dIdx++;
2384
- sIdx++;
2385
- mIdx++;
2386
- continue;
2387
- }
2388
- weight = globalAlpha;
2389
- }
2390
- if (weight === 0) {
2391
- dIdx++;
2392
- sIdx++;
2393
- mIdx++;
2394
- continue;
2395
- }
2396
- }
2397
- let currentSrcColor = baseSrcColor;
2398
- if (weight < 255) {
2399
- let currentSrcAlpha = baseSrcAlpha;
2400
- if (baseSrcAlpha === 255) {
2401
- currentSrcAlpha = weight;
2402
- } else {
2403
- currentSrcAlpha = baseSrcAlpha * weight + 128 >> 8;
2404
- }
2405
- if (!isOverwrite && currentSrcAlpha === 0) {
3016
+ let finalCol = srcCol;
3017
+ if (!isOpaque) {
3018
+ const a = srcAlpha * globalAlpha + 128 >> 8;
3019
+ if (a === 0 && !isOverwrite) {
2406
3020
  dIdx++;
2407
3021
  sIdx++;
2408
- mIdx++;
2409
3022
  continue;
2410
3023
  }
2411
- currentSrcColor = (baseSrcColor & 16777215 | currentSrcAlpha << 24) >>> 0;
3024
+ finalCol = (srcCol & 16777215 | a << 24) >>> 0;
2412
3025
  }
2413
- dst32[dIdx] = blendFn(currentSrcColor, dst32[dIdx]);
3026
+ dst32[dIdx] = blendFn(finalCol, dst32[dIdx]);
2414
3027
  dIdx++;
2415
3028
  sIdx++;
2416
- mIdx++;
2417
3029
  }
2418
3030
  dIdx += dStride;
2419
3031
  sIdx += sStride;
2420
- mIdx += mStride;
2421
3032
  }
2422
3033
  }
2423
3034
 
2424
3035
  // src/History/PixelMutator/mutatorBlendPixelData.ts
2425
- function mutatorBlendPixelData(writer) {
3036
+ var defaults11 = {
3037
+ blendPixelData
3038
+ };
3039
+ var mutatorBlendPixelData = ((writer, deps = defaults11) => {
3040
+ const {
3041
+ blendPixelData: blendPixelData2 = defaults11.blendPixelData
3042
+ } = deps;
2426
3043
  return {
2427
3044
  blendPixelData(src, opts) {
2428
3045
  const {
@@ -2432,12 +3049,13 @@ function mutatorBlendPixelData(writer) {
2432
3049
  h = src.height
2433
3050
  } = opts;
2434
3051
  writer.accumulator.storeRegionBeforeState(x, y, w, h);
2435
- blendPixelData(writer.target, src, opts);
3052
+ blendPixelData2(writer.target, src, opts);
2436
3053
  }
2437
3054
  };
2438
- }
3055
+ });
2439
3056
 
2440
3057
  // src/PixelData/fillPixelData.ts
3058
+ var SCRATCH_RECT = makeClippedRect();
2441
3059
  function fillPixelData(dst, color, _x, _y, _w, _h) {
2442
3060
  let x;
2443
3061
  let y;
@@ -2459,34 +3077,57 @@ function fillPixelData(dst, color, _x, _y, _w, _h) {
2459
3077
  w = dst.width;
2460
3078
  h = dst.height;
2461
3079
  }
2462
- if (x < 0) {
2463
- w += x;
2464
- x = 0;
2465
- }
2466
- if (y < 0) {
2467
- h += y;
2468
- y = 0;
2469
- }
2470
- const actualW = Math.min(w, dst.width - x);
2471
- const actualH = Math.min(h, dst.height - y);
2472
- if (actualW <= 0 || actualH <= 0) {
2473
- return;
2474
- }
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;
2475
3088
  const dst32 = dst.data32;
2476
3089
  const dw = dst.width;
2477
- if (actualW === dw && actualH === dst.height && x === 0 && y === 0) {
3090
+ if (actualW === dw && actualH === dst.height && finalX === 0 && finalY === 0) {
2478
3091
  dst32.fill(color);
2479
3092
  return;
2480
3093
  }
2481
3094
  for (let iy = 0; iy < actualH; iy++) {
2482
- const start = (y + iy) * dw + x;
3095
+ const start = (finalY + iy) * dw + finalX;
2483
3096
  const end = start + actualW;
2484
3097
  dst32.fill(color, start, end);
2485
3098
  }
2486
3099
  }
2487
3100
 
2488
- // src/History/PixelMutator/mutatorFillPixelData.ts
2489
- 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;
2490
3131
  return {
2491
3132
  fill(color, rect = {}) {
2492
3133
  const {
@@ -2496,12 +3137,13 @@ function mutatorFill(writer) {
2496
3137
  h = writer.target.height
2497
3138
  } = rect;
2498
3139
  writer.accumulator.storeRegionBeforeState(x, y, w, h);
2499
- fillPixelData(writer.target, color, x, y, w, h);
3140
+ fillPixelData2(writer.target, color, x, y, w, h);
2500
3141
  }
2501
3142
  };
2502
- }
3143
+ });
2503
3144
 
2504
3145
  // src/PixelData/invertPixelData.ts
3146
+ var SCRATCH_RECT2 = makeClippedRect();
2505
3147
  function invertPixelData(pixelData, opts = {}) {
2506
3148
  const dst = pixelData;
2507
3149
  const {
@@ -2515,21 +3157,14 @@ function invertPixelData(pixelData, opts = {}) {
2515
3157
  my = 0,
2516
3158
  invertMask = false
2517
3159
  } = opts;
2518
- let x = targetX;
2519
- let y = targetY;
2520
- let w = width;
2521
- let h = height;
2522
- if (x < 0) {
2523
- w += x;
2524
- x = 0;
2525
- }
2526
- if (y < 0) {
2527
- h += y;
2528
- y = 0;
2529
- }
2530
- const actualW = Math.min(w, dst.width - x);
2531
- const actualH = Math.min(h, dst.height - y);
2532
- 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;
2533
3168
  const dst32 = dst.data32;
2534
3169
  const dw = dst.width;
2535
3170
  const mPitch = mw ?? width;
@@ -2565,54 +3200,137 @@ function invertPixelData(pixelData, opts = {}) {
2565
3200
  }
2566
3201
 
2567
3202
  // src/History/PixelMutator/mutatorInvert.ts
2568
- function mutatorInvert(writer) {
3203
+ var defaults14 = {
3204
+ invertPixelData
3205
+ };
3206
+ var mutatorInvert = ((writer, deps = defaults14) => {
3207
+ const {
3208
+ invertPixelData: invertPixelData2 = defaults14.invertPixelData
3209
+ } = deps;
3210
+ return {
3211
+ invert(opts = {}) {
3212
+ const {
3213
+ x = 0,
3214
+ y = 0,
3215
+ w = writer.target.width,
3216
+ h = writer.target.height
3217
+ } = opts;
3218
+ writer.accumulator.storeRegionBeforeState(x, y, w, h);
3219
+ invertPixelData2(writer.target, opts);
3220
+ }
3221
+ };
3222
+ });
3223
+
3224
+ // src/History/PixelMutator.ts
3225
+ function makeFullPixelMutator(writer) {
3226
+ return {
3227
+ ...mutatorApplyAlphaMask(writer),
3228
+ ...mutatorApplyBinaryMask(writer),
3229
+ ...mutatorBlendPixelData(writer),
3230
+ ...mutatorBlendColor(writer),
3231
+ ...mutatorBlendPixel(writer),
3232
+ ...mutatorFill(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)
3242
+ };
3243
+ }
3244
+
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
+ };
2569
3306
  return {
2570
- invert(opts = {}) {
3307
+ applyCirclePencil(color, centerX, centerY, brushSize, alpha = 255, blendFn) {
3308
+ const bounds = getCircleBrushOrPencilBounds2(centerX, centerY, brushSize, writer.target.width, writer.target.height, boundsOut);
2571
3309
  const {
2572
- x = 0,
2573
- y = 0,
2574
- w = writer.target.width,
2575
- h = writer.target.height
2576
- } = opts;
3310
+ x,
3311
+ y,
3312
+ w,
3313
+ h
3314
+ } = bounds;
2577
3315
  writer.accumulator.storeRegionBeforeState(x, y, w, h);
2578
- invertPixelData(writer.target, opts);
2579
- }
2580
- };
2581
- }
2582
-
2583
- // src/History/PixelMutator.ts
2584
- function makeFullPixelMutator(writer) {
2585
- return {
2586
- ...mutatorApplyMask(writer),
2587
- ...mutatorBlendPixelData(writer),
2588
- ...mutatorBlendColor(writer),
2589
- ...mutatorBlendPixel(writer),
2590
- ...mutatorFill(writer),
2591
- ...mutatorInvert(writer),
2592
- ...mutatorApplyCircleBrush(writer),
2593
- ...mutatorApplyRectBrush(writer)
2594
- };
2595
- }
2596
-
2597
- // src/ImageData/ReusableImageData.ts
2598
- function makeReusableImageData() {
2599
- let imageData = null;
2600
- return function getReusableImageData(width, height) {
2601
- const hasInstance = !!imageData;
2602
- const widthMatches = hasInstance && imageData.width === width;
2603
- const heightMatches = hasInstance && imageData.height === height;
2604
- if (!widthMatches || !heightMatches) {
2605
- imageData = new ImageData(width, height);
3316
+ applyCircleBrushToPixelData2(writer.target, color, centerX, centerY, brushSize, alpha, fallOff, blendFn, bounds);
2606
3317
  }
2607
- return imageData;
2608
3318
  };
2609
- }
3319
+ });
2610
3320
 
2611
3321
  // src/ImageData/copyImageData.ts
2612
- function copyImageData({ data, width, height }) {
3322
+ function copyImageData({
3323
+ data,
3324
+ width,
3325
+ height
3326
+ }) {
2613
3327
  return new ImageData(data.slice(), width, height);
2614
3328
  }
2615
- function copyImageDataLike({ data, width, height }) {
3329
+ function copyImageDataLike({
3330
+ data,
3331
+ width,
3332
+ height
3333
+ }) {
2616
3334
  return {
2617
3335
  data: data.slice(),
2618
3336
  width,
@@ -2620,20 +3338,15 @@ function copyImageDataLike({ data, width, height }) {
2620
3338
  };
2621
3339
  }
2622
3340
 
2623
- // src/PixelData/pixelDataToAlphaMask.ts
2624
- function pixelDataToAlphaMask(pixelData) {
2625
- const {
2626
- 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 {
2627
3346
  width,
2628
- height
2629
- } = pixelData;
2630
- const len = data32.length;
2631
- const mask = new Uint8Array(width * height);
2632
- for (let i = 0; i < len; i++) {
2633
- const val = data32[i];
2634
- mask[i] = val >>> 24 & 255;
2635
- }
2636
- return mask;
3347
+ height,
3348
+ data: buffer
3349
+ };
2637
3350
  }
2638
3351
 
2639
3352
  // src/ImageData/imageDataToAlphaMask.ts
@@ -2643,11 +3356,7 @@ function imageDataToAlphaMask(imageData) {
2643
3356
  height,
2644
3357
  data
2645
3358
  } = imageData;
2646
- const data32 = new Uint32Array(
2647
- data.buffer,
2648
- data.byteOffset,
2649
- data.byteLength >> 2
2650
- );
3359
+ const data32 = new Uint32Array(data.buffer, data.byteOffset, data.byteLength >> 2);
2651
3360
  const len = data32.length;
2652
3361
  const mask = new Uint8Array(width * height);
2653
3362
  for (let i = 0; i < len; i++) {
@@ -2660,12 +3369,25 @@ function imageDataToAlphaMask(imageData) {
2660
3369
  // src/ImageData/imageDataToDataUrl.ts
2661
3370
  var get = makeReusableCanvas();
2662
3371
  function imageDataToDataUrl(imageData) {
2663
- const { canvas, ctx } = get(imageData.width, imageData.height);
3372
+ const {
3373
+ canvas,
3374
+ ctx
3375
+ } = get(imageData.width, imageData.height);
2664
3376
  ctx.putImageData(imageData, 0, 0);
2665
3377
  return canvas.toDataURL();
2666
3378
  }
2667
3379
  imageDataToDataUrl.reset = get.reset;
2668
3380
 
3381
+ // src/ImageData/imageDataToUInt32Array.ts
3382
+ function imageDataToUInt32Array(imageData) {
3383
+ return new Uint32Array(
3384
+ imageData.data.buffer,
3385
+ imageData.data.byteOffset,
3386
+ // Shift right by 2 is a fast bitwise division by 4.
3387
+ imageData.data.byteLength >> 2
3388
+ );
3389
+ }
3390
+
2669
3391
  // src/ImageData/invertImageData.ts
2670
3392
  function invertImageData(imageData) {
2671
3393
  const data = imageData.data;
@@ -2708,7 +3430,11 @@ function resample32(srcData32, srcW, srcH, factor) {
2708
3430
  // src/ImageData/resampleImageData.ts
2709
3431
  function resampleImageData(source, factor) {
2710
3432
  const src32 = new Uint32Array(source.data.buffer);
2711
- 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);
2712
3438
  const uint8ClampedArray = new Uint8ClampedArray(data.buffer);
2713
3439
  return new ImageData(uint8ClampedArray, width, height);
2714
3440
  }
@@ -2737,14 +3463,25 @@ function resizeImageData(current, newWidth, newHeight, offsetX = 0, offsetY = 0)
2737
3463
  const srcX = x0 - offsetX;
2738
3464
  const dstStart = (dstY * newWidth + x0) * 4;
2739
3465
  const srcStart = (srcY * oldW + srcX) * 4;
2740
- newData.set(
2741
- oldData.subarray(srcStart, srcStart + rowLen),
2742
- dstStart
2743
- );
3466
+ newData.set(oldData.subarray(srcStart, srcStart + rowLen), dstStart);
2744
3467
  }
2745
3468
  return result;
2746
3469
  }
2747
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
+
2748
3485
  // src/ImageData/serialization.ts
2749
3486
  function base64EncodeArrayBuffer(buffer) {
2750
3487
  const uint8 = new Uint8Array(buffer);
@@ -2787,32 +3524,53 @@ function deserializeNullableImageData(serialized) {
2787
3524
  return deserializeImageData(serialized);
2788
3525
  }
2789
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
+
2790
3547
  // src/ImageData/writeImageData.ts
3548
+ var SCRATCH_BLIT2 = makeClippedBlit();
2791
3549
  function writeImageData(target, source, x, y, sx = 0, sy = 0, sw = source.width, sh = source.height, mask = null, maskType = 1 /* BINARY */) {
2792
3550
  const dstW = target.width;
2793
3551
  const dstH = target.height;
2794
3552
  const dstData = target.data;
2795
3553
  const srcW = source.width;
2796
3554
  const srcData = source.data;
2797
- const x0 = Math.max(0, x);
2798
- const y0 = Math.max(0, y);
2799
- const x1 = Math.min(dstW, x + sw);
2800
- const y1 = Math.min(dstH, y + sh);
2801
- if (x1 <= x0 || y1 <= y0) {
2802
- return;
2803
- }
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;
2804
3565
  const useMask = !!mask;
2805
- const rowCount = y1 - y0;
2806
- const rowLenPixels = x1 - x0;
2807
- for (let row = 0; row < rowCount; row++) {
2808
- const dstY = y0 + row;
2809
- const srcY = sy + (dstY - y);
2810
- const srcXBase = sx + (x0 - x);
2811
- const dstStart = (dstY * dstW + x0) * 4;
2812
- 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;
2813
3571
  if (useMask && mask) {
2814
- for (let ix = 0; ix < rowLenPixels; ix++) {
2815
- const mi = srcY * srcW + (srcXBase + ix);
3572
+ for (let ix = 0; ix < copyW; ix++) {
3573
+ const mi = currentSrcY * srcW + (srcX + ix);
2816
3574
  const alpha = mask[mi];
2817
3575
  if (alpha === 0) {
2818
3576
  continue;
@@ -2834,7 +3592,7 @@ function writeImageData(target, source, x, y, sx = 0, sy = 0, sw = source.width,
2834
3592
  }
2835
3593
  }
2836
3594
  } else {
2837
- const byteLen = rowLenPixels * 4;
3595
+ const byteLen = copyW * 4;
2838
3596
  const sub = srcData.subarray(srcStart, srcStart + byteLen);
2839
3597
  dstData.set(sub, dstStart);
2840
3598
  }
@@ -2842,24 +3600,52 @@ function writeImageData(target, source, x, y, sx = 0, sy = 0, sw = source.width,
2842
3600
  }
2843
3601
 
2844
3602
  // src/ImageData/writeImageDataBuffer.ts
3603
+ var SCRATCH_BLIT3 = makeClippedBlit();
2845
3604
  function writeImageDataBuffer(imageData, data, _x, _y, _w, _h) {
2846
- const { x, y, w, h } = typeof _x === "object" ? _x : { x: _x, y: _y, w: _w, h: _h };
2847
- const { width: dstW, height: dstH, data: dst } = imageData;
2848
- const x0 = Math.max(0, x);
2849
- const y0 = Math.max(0, y);
2850
- const x1 = Math.min(dstW, x + w);
2851
- const y1 = Math.min(dstH, y + h);
2852
- if (x1 <= x0 || y1 <= y0) return;
2853
- const rowLen = (x1 - x0) * 4;
2854
- const srcCol = x0 - x;
2855
- const srcYOffset = y0 - y;
2856
- const actualH = y1 - y0;
2857
- for (let row = 0; row < actualH; row++) {
2858
- const dstStart = ((y0 + row) * dstW + x0) * 4;
2859
- const srcRow = srcYOffset + row;
2860
- const o = (srcRow * w + srcCol) * 4;
2861
- 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]++;
2862
3647
  }
3648
+ return frequencies;
2863
3649
  }
2864
3650
 
2865
3651
  // src/IndexedImage/IndexedImage.ts
@@ -2925,13 +3711,7 @@ var IndexedImage = class _IndexedImage {
2925
3711
  indexedData[i] = id;
2926
3712
  }
2927
3713
  const palette = Uint32Array.from(colorMap.keys());
2928
- return new _IndexedImage(
2929
- width,
2930
- height,
2931
- indexedData,
2932
- palette,
2933
- transparentPalletIndex
2934
- );
3714
+ return new _IndexedImage(width, height, indexedData, palette, transparentPalletIndex);
2935
3715
  }
2936
3716
  /**
2937
3717
  * Retrieves the 32-bit packed color value at the given coordinates.
@@ -2946,21 +3726,13 @@ var IndexedImage = class _IndexedImage {
2946
3726
  }
2947
3727
  };
2948
3728
 
2949
- // src/IndexedImage/getIndexedImageColorCounts.ts
2950
- function getIndexedImageColorCounts(indexedImage) {
2951
- const data = indexedImage.data;
2952
- const palette = indexedImage.palette;
2953
- const frequencies = new Int32Array(palette.length);
2954
- for (let i = 0; i < data.length; i++) {
2955
- const colorIndex = data[i];
2956
- frequencies[colorIndex]++;
2957
- }
2958
- return frequencies;
2959
- }
2960
-
2961
3729
  // src/IndexedImage/indexedImageToAverageColor.ts
2962
3730
  function indexedImageToAverageColor(indexedImage, includeTransparent = false) {
2963
- const { data, palette, transparentPalletIndex } = indexedImage;
3731
+ const {
3732
+ data,
3733
+ palette,
3734
+ transparentPalletIndex
3735
+ } = indexedImage;
2964
3736
  const counts = new Uint32Array(palette.length);
2965
3737
  for (let i = 0; i < data.length; i++) {
2966
3738
  const id = data[i];
@@ -3000,26 +3772,14 @@ function indexedImageToAverageColor(indexedImage, includeTransparent = false) {
3000
3772
  return packColor(r, g, b, a);
3001
3773
  }
3002
3774
 
3003
- // src/IndexedImage/resampleIndexedImage.ts
3004
- function resampleIndexedImage(source, factor) {
3005
- const { data, width, height } = resample32(
3006
- source.data,
3007
- source.width,
3008
- source.height,
3009
- factor
3010
- );
3011
- return new IndexedImage(
3775
+ // src/IndexedImage/indexedImageToImageData.ts
3776
+ function indexedImageToImageData(indexedImage) {
3777
+ const {
3012
3778
  width,
3013
3779
  height,
3014
3780
  data,
3015
- source.palette,
3016
- source.transparentPalletIndex
3017
- );
3018
- }
3019
-
3020
- // src/IndexedImage/indexedImageToImageData.ts
3021
- function indexedImageToImageData(indexedImage) {
3022
- const { width, height, data, palette } = indexedImage;
3781
+ palette
3782
+ } = indexedImage;
3023
3783
  const result = new ImageData(width, height);
3024
3784
  const data32 = new Uint32Array(result.data.buffer);
3025
3785
  for (let i = 0; i < data.length; i++) {
@@ -3030,6 +3790,16 @@ function indexedImageToImageData(indexedImage) {
3030
3790
  return result;
3031
3791
  }
3032
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
+
3033
3803
  // src/Input/fileInputChangeToImageData.ts
3034
3804
  async function fileInputChangeToImageData(event) {
3035
3805
  const target = event.target;
@@ -3053,23 +3823,11 @@ async function fileToImageData(file) {
3053
3823
  let bitmap = null;
3054
3824
  try {
3055
3825
  bitmap = await createImageBitmap(file);
3056
- const canvas = new OffscreenCanvas(
3057
- bitmap.width,
3058
- bitmap.height
3059
- );
3826
+ const canvas = new OffscreenCanvas(bitmap.width, bitmap.height);
3060
3827
  const ctx = canvas.getContext("2d");
3061
3828
  if (!ctx) throw new Error(OFFSCREEN_CANVAS_CTX_FAILED);
3062
- ctx.drawImage(
3063
- bitmap,
3064
- 0,
3065
- 0
3066
- );
3067
- return ctx.getImageData(
3068
- 0,
3069
- 0,
3070
- bitmap.width,
3071
- bitmap.height
3072
- );
3829
+ ctx.drawImage(bitmap, 0, 0);
3830
+ return ctx.getImageData(0, 0, bitmap.width, bitmap.height);
3073
3831
  } finally {
3074
3832
  bitmap?.close();
3075
3833
  }
@@ -3077,125 +3835,495 @@ async function fileToImageData(file) {
3077
3835
 
3078
3836
  // src/Input/getSupportedRasterFormats.ts
3079
3837
  var formatsPromise = null;
3080
- var defaultRasterMimes = [
3081
- "image/png",
3082
- "image/jpeg",
3083
- "image/webp",
3084
- "image/avif",
3085
- "image/gif",
3086
- "image/bmp"
3087
- ];
3838
+ var defaultRasterMimes = ["image/png", "image/jpeg", "image/webp", "image/avif", "image/gif", "image/bmp"];
3088
3839
  async function getSupportedPixelFormats(rasterMimes = defaultRasterMimes) {
3089
3840
  if (formatsPromise) {
3090
3841
  return formatsPromise;
3091
3842
  }
3092
- const probeCanvas = async () => {
3093
- const canvas = new OffscreenCanvas(1, 1);
3094
- const results = await Promise.all(
3095
- rasterMimes.map(async (mime) => {
3096
- try {
3097
- const blob = await canvas.convertToBlob({
3098
- type: mime
3099
- });
3100
- return blob.type === mime ? mime : null;
3101
- } catch {
3102
- return null;
3103
- }
3104
- })
3105
- );
3106
- return results.filter((type) => {
3107
- return type !== null;
3108
- });
3109
- };
3110
- formatsPromise = probeCanvas().catch((error) => {
3111
- formatsPromise = null;
3112
- throw error;
3113
- });
3114
- return formatsPromise;
3115
- }
3116
-
3117
- // src/Mask/copyMask.ts
3118
- function copyMask(src) {
3119
- return src.slice();
3843
+ const probeCanvas = async () => {
3844
+ const canvas = new OffscreenCanvas(1, 1);
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
+ }));
3855
+ return results.filter((type) => {
3856
+ return type !== null;
3857
+ });
3858
+ };
3859
+ formatsPromise = probeCanvas().catch((error) => {
3860
+ formatsPromise = null;
3861
+ throw error;
3862
+ });
3863
+ return formatsPromise;
3864
+ }
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
+
3933
+ // src/Mask/copyMask.ts
3934
+ function copyMask(src) {
3935
+ return src.slice();
3936
+ }
3937
+
3938
+ // src/Mask/invertMask.ts
3939
+ function invertBinaryMask(dst) {
3940
+ const len = dst.length;
3941
+ for (let i = 0; i < len; i++) {
3942
+ dst[i] = dst[i] === 0 ? 1 : 0;
3943
+ }
3944
+ }
3945
+ function invertAlphaMask(dst) {
3946
+ const len = dst.length;
3947
+ for (let i = 0; i < len; i++) {
3948
+ dst[i] = 255 - dst[i];
3949
+ }
3950
+ }
3951
+
3952
+ // src/Mask/mergeAlphaMasks.ts
3953
+ function mergeAlphaMasks(dst, dstWidth, src, srcWidth, opts) {
3954
+ const {
3955
+ x: targetX = 0,
3956
+ y: targetY = 0,
3957
+ w: width = 0,
3958
+ h: height = 0,
3959
+ alpha: globalAlpha = 255,
3960
+ mx = 0,
3961
+ my = 0,
3962
+ invertMask = false
3963
+ } = opts;
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;
4037
+ }
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++) {
4060
+ const mVal = src[sIdx];
4061
+ const isMaskedOut = invertMask ? mVal !== 0 : mVal === 0;
4062
+ if (isMaskedOut) {
4063
+ dst[dIdx] = 0;
4064
+ }
4065
+ dIdx++;
4066
+ sIdx++;
4067
+ }
4068
+ dIdx += dStride;
4069
+ sIdx += sStride;
4070
+ }
3120
4071
  }
3121
4072
 
3122
- // src/Mask/invertMask.ts
3123
- function invertBinaryMask(dst) {
3124
- const len = dst.length;
3125
- for (let i = 0; i < len; i++) {
3126
- dst[i] = dst[i] === 0 ? 1 : 0;
4073
+ // src/PixelData/PixelData.ts
4074
+ var PixelData = class _PixelData {
4075
+ data32;
4076
+ imageData;
4077
+ width;
4078
+ height;
4079
+ constructor(imageData) {
4080
+ this.data32 = imageDataToUInt32Array(imageData);
4081
+ this.imageData = imageData;
4082
+ this.width = imageData.width;
4083
+ this.height = imageData.height;
3127
4084
  }
3128
- }
3129
- function invertAlphaMask(dst) {
3130
- const len = dst.length;
3131
- for (let i = 0; i < len; i++) {
3132
- dst[i] = 255 - dst[i];
4085
+ set(imageData) {
4086
+ ;
4087
+ this.imageData = imageData;
4088
+ this.data32 = imageDataToUInt32Array(imageData);
4089
+ this.width = imageData.width;
4090
+ this.height = imageData.height;
3133
4091
  }
3134
- }
4092
+ // should only be used for debug and testing
4093
+ copy() {
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
+ }
4109
+ return new _PixelData(newImageData);
4110
+ }
4111
+ };
3135
4112
 
3136
- // src/Mask/mergeMasks.ts
3137
- function mergeMasks(dst, dstWidth, src, opts) {
4113
+ // src/PixelData/blendPixelDataAlphaMask.ts
4114
+ function blendPixelDataAlphaMask(dst, src, alphaMask, opts = {}) {
3138
4115
  const {
3139
4116
  x: targetX = 0,
3140
4117
  y: targetY = 0,
3141
- w: width = 0,
3142
- h: height = 0,
4118
+ sx: sourceX = 0,
4119
+ sy: sourceY = 0,
4120
+ w: width = src.width,
4121
+ h: height = src.height,
3143
4122
  alpha: globalAlpha = 255,
3144
- maskType = 0 /* ALPHA */,
3145
- mw,
4123
+ blendFn = sourceOverPerfect,
4124
+ mw = src.width,
3146
4125
  mx = 0,
3147
4126
  my = 0,
3148
4127
  invertMask = false
3149
4128
  } = opts;
3150
- if (width <= 0 || height <= 0 || globalAlpha === 0) {
3151
- return;
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;
3152
4140
  }
3153
- const sPitch = mw ?? width;
3154
- const isAlpha = maskType === 0 /* ALPHA */;
3155
- for (let iy = 0; iy < height; iy++) {
3156
- const dy = targetY + iy;
3157
- const sy = my + iy;
3158
- if (dy < 0 || sy < 0) {
3159
- continue;
3160
- }
3161
- for (let ix = 0; ix < width; ix++) {
3162
- const dx = targetX + ix;
3163
- const sx = mx + ix;
3164
- if (dx < 0 || dx >= dstWidth || sx < 0 || sx >= sPitch) {
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++;
3165
4192
  continue;
3166
4193
  }
3167
- const dIdx = dy * dstWidth + dx;
3168
- const sIdx = sy * sPitch + sx;
3169
- const mVal = src[sIdx];
3170
4194
  let weight = globalAlpha;
3171
- if (isAlpha) {
3172
- const effectiveM = invertMask ? 255 - mVal : mVal;
3173
- if (effectiveM === 0) {
3174
- dst[dIdx] = 0;
3175
- continue;
3176
- }
3177
- weight = globalAlpha === 255 ? effectiveM : effectiveM * globalAlpha + 128 >> 8;
3178
- } else {
3179
- const isHit = invertMask ? mVal === 0 : mVal === 1;
3180
- if (!isHit) {
3181
- dst[dIdx] = 0;
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++;
3182
4213
  continue;
3183
4214
  }
3184
- weight = globalAlpha;
4215
+ finalCol = (srcCol & 16777215 | a << 24) >>> 0;
3185
4216
  }
3186
- if (weight === 0) {
3187
- dst[dIdx] = 0;
4217
+ dst32[dIdx] = blendFn(finalCol, dst32[dIdx]);
4218
+ dIdx++;
4219
+ sIdx++;
4220
+ mIdx++;
4221
+ }
4222
+ dIdx += dStride;
4223
+ sIdx += sStride;
4224
+ mIdx += mStride;
4225
+ }
4226
+ }
4227
+
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++;
3188
4298
  continue;
3189
4299
  }
3190
- const da = dst[dIdx];
3191
- if (da === 0) {
3192
- } else if (weight === 255) {
3193
- } else if (da === 255) {
3194
- dst[dIdx] = weight;
3195
- } else {
3196
- dst[dIdx] = da * weight + 128 >> 8;
4300
+ const srcCol = src32[sIdx];
4301
+ const srcAlpha = srcCol >>> 24;
4302
+ if (srcAlpha === 0 && !isOverwrite) {
4303
+ dIdx++;
4304
+ sIdx++;
4305
+ mIdx++;
4306
+ continue;
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;
3197
4318
  }
4319
+ dst32[dIdx] = blendFn(finalCol, dst32[dIdx]);
4320
+ dIdx++;
4321
+ sIdx++;
4322
+ mIdx++;
3198
4323
  }
4324
+ dIdx += dStride;
4325
+ sIdx += sStride;
4326
+ mIdx += mStride;
3199
4327
  }
3200
4328
  }
3201
4329
 
@@ -3205,32 +4333,40 @@ function clearPixelData(dst, rect) {
3205
4333
  }
3206
4334
 
3207
4335
  // src/PixelData/extractPixelDataBuffer.ts
4336
+ var SCRATCH_BLIT4 = makeClippedBlit();
3208
4337
  function extractPixelDataBuffer(source, _x, _y, _w, _h) {
3209
- 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
+ };
3210
4349
  const srcW = source.width;
3211
4350
  const srcH = source.height;
3212
4351
  const srcData = source.data32;
3213
4352
  if (w <= 0 || h <= 0) {
3214
4353
  return new Uint32Array(0);
3215
4354
  }
3216
- const dstImageData = new ImageData(w, h);
3217
- const dstData = new Uint32Array(dstImageData.data.buffer);
3218
- const x0 = Math.max(0, x);
3219
- const y0 = Math.max(0, y);
3220
- const x1 = Math.min(srcW, x + w);
3221
- const y1 = Math.min(srcH, y + h);
3222
- if (x1 <= x0 || y1 <= y0) {
3223
- return dstData;
3224
- }
3225
- const copyWidth = x1 - x0;
3226
- const copyHeight = y1 - y0;
3227
- for (let row = 0; row < copyHeight; row++) {
3228
- const srcRow = y0 + row;
3229
- const srcStart = srcRow * srcW + x0;
3230
- const dstRow = y0 - y + row;
3231
- const dstCol = x0 - x;
3232
- const dstStart = dstRow * w + dstCol;
3233
- 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);
3234
4370
  dstData.set(chunk, dstStart);
3235
4371
  }
3236
4372
  return dstData;
@@ -3238,13 +4374,59 @@ function extractPixelDataBuffer(source, _x, _y, _w, _h) {
3238
4374
 
3239
4375
  // src/PixelData/extractPixelData.ts
3240
4376
  function extractPixelData(source, _x, _y, _w, _h) {
3241
- 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
+ };
3242
4388
  const result = new PixelData(new ImageData(w, h));
3243
4389
  const buffer = extractPixelDataBuffer(source, x, y, w, h);
3244
4390
  result.data32.set(buffer);
3245
4391
  return result;
3246
4392
  }
3247
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
+
3248
4430
  // src/PixelData/reflectPixelData.ts
3249
4431
  function reflectPixelDataHorizontal(pixelData) {
3250
4432
  const width = pixelData.width;
@@ -3282,12 +4464,12 @@ function reflectPixelDataVertical(pixelData) {
3282
4464
 
3283
4465
  // src/PixelData/resamplePixelData.ts
3284
4466
  function resamplePixelData(pixelData, factor) {
3285
- const { data, width, height } = resample32(pixelData.data32, pixelData.width, pixelData.height, factor);
3286
- return new PixelData(new ImageData(
3287
- new Uint8ClampedArray(data.buffer),
4467
+ const {
4468
+ data,
3288
4469
  width,
3289
4470
  height
3290
- ));
4471
+ } = resample32(pixelData.data32, pixelData.width, pixelData.height, factor);
4472
+ return new PixelData(new ImageData(new Uint8ClampedArray(data.buffer), width, height));
3291
4473
  }
3292
4474
 
3293
4475
  // src/PixelData/rotatePixelData.ts
@@ -3311,11 +4493,7 @@ function rotatePixelData(pixelData) {
3311
4493
  newData32[newIdx] = data[oldIdx];
3312
4494
  }
3313
4495
  }
3314
- const newImageData = new ImageData(
3315
- new Uint8ClampedArray(newData32.buffer),
3316
- newWidth,
3317
- newHeight
3318
- );
4496
+ const newImageData = new ImageData(new Uint8ClampedArray(newData32.buffer), newWidth, newHeight);
3319
4497
  pixelData.set(newImageData);
3320
4498
  }
3321
4499
  function rotateSquareInPlace(pixelData) {
@@ -3337,8 +4515,14 @@ function rotateSquareInPlace(pixelData) {
3337
4515
  }
3338
4516
 
3339
4517
  // src/PixelData/writePixelDataBuffer.ts
4518
+ var SCRATCH_BLIT5 = makeClippedBlit();
3340
4519
  function writePixelDataBuffer(target, data, _x, _y, _w, _h) {
3341
- const { x, y, w, h } = typeof _x === "object" ? _x : {
4520
+ const {
4521
+ x,
4522
+ y,
4523
+ w,
4524
+ h
4525
+ } = typeof _x === "object" ? _x : {
3342
4526
  x: _x,
3343
4527
  y: _y,
3344
4528
  w: _w,
@@ -3347,45 +4531,52 @@ function writePixelDataBuffer(target, data, _x, _y, _w, _h) {
3347
4531
  const dstW = target.width;
3348
4532
  const dstH = target.height;
3349
4533
  const dstData = target.data32;
3350
- const x0 = Math.max(0, x);
3351
- const y0 = Math.max(0, y);
3352
- const x1 = Math.min(dstW, x + w);
3353
- const y1 = Math.min(dstH, y + h);
3354
- if (x1 <= x0 || y1 <= y0) {
3355
- return;
3356
- }
3357
- const rowLen = x1 - x0;
3358
- const srcCol = x0 - x;
3359
- const srcYOffset = y0 - y;
3360
- const actualH = y1 - y0;
3361
- for (let row = 0; row < actualH; row++) {
3362
- const dstStart = (y0 + row) * dstW + x0;
3363
- const srcRow = srcYOffset + row;
3364
- const srcStart = srcRow * w + srcCol;
3365
- 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);
3366
4548
  }
3367
4549
  }
3368
4550
  export {
3369
4551
  BASE_FAST_BLEND_MODE_FUNCTIONS,
3370
4552
  BASE_PERFECT_BLEND_MODE_FUNCTIONS,
3371
4553
  BaseBlendMode,
4554
+ CANVAS_CTX_FAILED,
3372
4555
  HistoryManager,
3373
4556
  IndexedImage,
3374
4557
  MaskType,
4558
+ OFFSCREEN_CANVAS_CTX_FAILED,
3375
4559
  PixelAccumulator,
4560
+ PixelBuffer32,
3376
4561
  PixelData,
3377
4562
  PixelEngineConfig,
3378
4563
  PixelTile,
3379
4564
  PixelWriter,
3380
4565
  UnsupportedFormatError,
4566
+ applyAlphaMaskToPixelData,
4567
+ applyBinaryMaskToAlphaMask,
4568
+ applyBinaryMaskToPixelData,
3381
4569
  applyCircleBrushToPixelData,
3382
- applyMaskToPixelData,
3383
4570
  applyPatchTiles,
3384
4571
  applyRectBrushToPixelData,
3385
4572
  base64DecodeArrayBuffer,
3386
4573
  base64EncodeArrayBuffer,
3387
4574
  blendColorPixelData,
4575
+ blendColorPixelDataAlphaMask,
4576
+ blendColorPixelDataBinaryMask,
3388
4577
  blendPixelData,
4578
+ blendPixelDataAlphaMask,
4579
+ blendPixelDataBinaryMask,
3389
4580
  clearPixelData,
3390
4581
  color32ToCssRGBA,
3391
4582
  color32ToHex,
@@ -3418,10 +4609,13 @@ export {
3418
4609
  fileToImageData,
3419
4610
  fillPixelData,
3420
4611
  floodFillSelection,
3421
- getCircleBrushBounds,
4612
+ forEachLinePoint,
4613
+ getCircleBrushOrPencilBounds,
4614
+ getCircleBrushOrPencilStrokeBounds,
3422
4615
  getImageDataFromClipboard,
3423
4616
  getIndexedImageColorCounts,
3424
- getRectBrushBounds,
4617
+ getRectBrushOrPencilBounds,
4618
+ getRectBrushOrPencilStrokeBounds,
3425
4619
  getSupportedPixelFormats,
3426
4620
  hardLightFast,
3427
4621
  hardLightPerfect,
@@ -3453,17 +4647,29 @@ export {
3453
4647
  makeBlendModeRegistry,
3454
4648
  makeFastBlendModeRegistry,
3455
4649
  makeFullPixelMutator,
4650
+ makeImageDataLike,
3456
4651
  makePerfectBlendModeRegistry,
3457
4652
  makePixelCanvas,
3458
4653
  makeReusableCanvas,
3459
4654
  makeReusableImageData,
3460
- mergeMasks,
4655
+ mergeAlphaMasks,
4656
+ mergeBinaryMasks,
3461
4657
  multiplyFast,
3462
4658
  multiplyPerfect,
3463
- mutatorApplyMask,
4659
+ mutatorApplyAlphaMask,
4660
+ mutatorApplyBinaryMask,
4661
+ mutatorApplyCircleBrush,
4662
+ mutatorApplyCircleBrushStroke,
4663
+ mutatorApplyCirclePencil,
4664
+ mutatorApplyCirclePencilStroke,
4665
+ mutatorApplyRectBrush,
4666
+ mutatorApplyRectBrushStroke,
4667
+ mutatorApplyRectPencil,
4668
+ mutatorApplyRectPencilStroke,
3464
4669
  mutatorBlendColor,
3465
4670
  mutatorBlendPixel,
3466
4671
  mutatorBlendPixelData,
4672
+ mutatorClear,
3467
4673
  mutatorFill,
3468
4674
  mutatorInvert,
3469
4675
  overlayFast,
@@ -3493,7 +4699,10 @@ export {
3493
4699
  sourceOverPerfect,
3494
4700
  subtractFast,
3495
4701
  subtractPerfect,
4702
+ toBlendModeIndexAndName,
3496
4703
  trimRectBounds,
4704
+ uInt32ArrayToImageData,
4705
+ uInt32ArrayToImageDataLike,
3497
4706
  unpackAlpha,
3498
4707
  unpackBlue,
3499
4708
  unpackColor,