pixel-data-js 0.18.0 → 0.19.1

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 +22 -7
  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
@@ -23,23 +23,32 @@ __export(src_exports, {
23
23
  BASE_FAST_BLEND_MODE_FUNCTIONS: () => BASE_FAST_BLEND_MODE_FUNCTIONS,
24
24
  BASE_PERFECT_BLEND_MODE_FUNCTIONS: () => BASE_PERFECT_BLEND_MODE_FUNCTIONS,
25
25
  BaseBlendMode: () => BaseBlendMode,
26
+ CANVAS_CTX_FAILED: () => CANVAS_CTX_FAILED,
26
27
  HistoryManager: () => HistoryManager,
27
28
  IndexedImage: () => IndexedImage,
28
29
  MaskType: () => MaskType,
30
+ OFFSCREEN_CANVAS_CTX_FAILED: () => OFFSCREEN_CANVAS_CTX_FAILED,
29
31
  PixelAccumulator: () => PixelAccumulator,
32
+ PixelBuffer32: () => PixelBuffer32,
30
33
  PixelData: () => PixelData,
31
34
  PixelEngineConfig: () => PixelEngineConfig,
32
35
  PixelTile: () => PixelTile,
33
36
  PixelWriter: () => PixelWriter,
34
37
  UnsupportedFormatError: () => UnsupportedFormatError,
38
+ applyAlphaMaskToPixelData: () => applyAlphaMaskToPixelData,
39
+ applyBinaryMaskToAlphaMask: () => applyBinaryMaskToAlphaMask,
40
+ applyBinaryMaskToPixelData: () => applyBinaryMaskToPixelData,
35
41
  applyCircleBrushToPixelData: () => applyCircleBrushToPixelData,
36
- applyMaskToPixelData: () => applyMaskToPixelData,
37
42
  applyPatchTiles: () => applyPatchTiles,
38
43
  applyRectBrushToPixelData: () => applyRectBrushToPixelData,
39
44
  base64DecodeArrayBuffer: () => base64DecodeArrayBuffer,
40
45
  base64EncodeArrayBuffer: () => base64EncodeArrayBuffer,
41
46
  blendColorPixelData: () => blendColorPixelData,
47
+ blendColorPixelDataAlphaMask: () => blendColorPixelDataAlphaMask,
48
+ blendColorPixelDataBinaryMask: () => blendColorPixelDataBinaryMask,
42
49
  blendPixelData: () => blendPixelData,
50
+ blendPixelDataAlphaMask: () => blendPixelDataAlphaMask,
51
+ blendPixelDataBinaryMask: () => blendPixelDataBinaryMask,
43
52
  clearPixelData: () => clearPixelData,
44
53
  color32ToCssRGBA: () => color32ToCssRGBA,
45
54
  color32ToHex: () => color32ToHex,
@@ -72,10 +81,13 @@ __export(src_exports, {
72
81
  fileToImageData: () => fileToImageData,
73
82
  fillPixelData: () => fillPixelData,
74
83
  floodFillSelection: () => floodFillSelection,
75
- getCircleBrushBounds: () => getCircleBrushBounds,
84
+ forEachLinePoint: () => forEachLinePoint,
85
+ getCircleBrushOrPencilBounds: () => getCircleBrushOrPencilBounds,
86
+ getCircleBrushOrPencilStrokeBounds: () => getCircleBrushOrPencilStrokeBounds,
76
87
  getImageDataFromClipboard: () => getImageDataFromClipboard,
77
88
  getIndexedImageColorCounts: () => getIndexedImageColorCounts,
78
- getRectBrushBounds: () => getRectBrushBounds,
89
+ getRectBrushOrPencilBounds: () => getRectBrushOrPencilBounds,
90
+ getRectBrushOrPencilStrokeBounds: () => getRectBrushOrPencilStrokeBounds,
79
91
  getSupportedPixelFormats: () => getSupportedPixelFormats,
80
92
  hardLightFast: () => hardLightFast,
81
93
  hardLightPerfect: () => hardLightPerfect,
@@ -107,17 +119,29 @@ __export(src_exports, {
107
119
  makeBlendModeRegistry: () => makeBlendModeRegistry,
108
120
  makeFastBlendModeRegistry: () => makeFastBlendModeRegistry,
109
121
  makeFullPixelMutator: () => makeFullPixelMutator,
122
+ makeImageDataLike: () => makeImageDataLike,
110
123
  makePerfectBlendModeRegistry: () => makePerfectBlendModeRegistry,
111
124
  makePixelCanvas: () => makePixelCanvas,
112
125
  makeReusableCanvas: () => makeReusableCanvas,
113
126
  makeReusableImageData: () => makeReusableImageData,
114
- mergeMasks: () => mergeMasks,
127
+ mergeAlphaMasks: () => mergeAlphaMasks,
128
+ mergeBinaryMasks: () => mergeBinaryMasks,
115
129
  multiplyFast: () => multiplyFast,
116
130
  multiplyPerfect: () => multiplyPerfect,
117
- mutatorApplyMask: () => mutatorApplyMask,
131
+ mutatorApplyAlphaMask: () => mutatorApplyAlphaMask,
132
+ mutatorApplyBinaryMask: () => mutatorApplyBinaryMask,
133
+ mutatorApplyCircleBrush: () => mutatorApplyCircleBrush,
134
+ mutatorApplyCircleBrushStroke: () => mutatorApplyCircleBrushStroke,
135
+ mutatorApplyCirclePencil: () => mutatorApplyCirclePencil,
136
+ mutatorApplyCirclePencilStroke: () => mutatorApplyCirclePencilStroke,
137
+ mutatorApplyRectBrush: () => mutatorApplyRectBrush,
138
+ mutatorApplyRectBrushStroke: () => mutatorApplyRectBrushStroke,
139
+ mutatorApplyRectPencil: () => mutatorApplyRectPencil,
140
+ mutatorApplyRectPencilStroke: () => mutatorApplyRectPencilStroke,
118
141
  mutatorBlendColor: () => mutatorBlendColor,
119
142
  mutatorBlendPixel: () => mutatorBlendPixel,
120
143
  mutatorBlendPixelData: () => mutatorBlendPixelData,
144
+ mutatorClear: () => mutatorClear,
121
145
  mutatorFill: () => mutatorFill,
122
146
  mutatorInvert: () => mutatorInvert,
123
147
  overlayFast: () => overlayFast,
@@ -147,7 +171,10 @@ __export(src_exports, {
147
171
  sourceOverPerfect: () => sourceOverPerfect,
148
172
  subtractFast: () => subtractFast,
149
173
  subtractPerfect: () => subtractPerfect,
174
+ toBlendModeIndexAndName: () => toBlendModeIndexAndName,
150
175
  trimRectBounds: () => trimRectBounds,
176
+ uInt32ArrayToImageData: () => uInt32ArrayToImageData,
177
+ uInt32ArrayToImageDataLike: () => uInt32ArrayToImageDataLike,
151
178
  unpackAlpha: () => unpackAlpha,
152
179
  unpackBlue: () => unpackBlue,
153
180
  unpackColor: () => unpackColor,
@@ -164,617 +191,238 @@ __export(src_exports, {
164
191
  });
165
192
  module.exports = __toCommonJS(src_exports);
166
193
 
167
- // src/BlendModes/blend-modes.ts
168
- var BaseBlendMode = {
169
- overwrite: 0,
170
- sourceOver: 1,
171
- darken: 2,
172
- multiply: 3,
173
- colorBurn: 4,
174
- linearBurn: 5,
175
- darkerColor: 6,
176
- lighten: 7,
177
- screen: 8,
178
- colorDodge: 9,
179
- linearDodge: 10,
180
- lighterColor: 11,
181
- overlay: 12,
182
- softLight: 13,
183
- hardLight: 14,
184
- vividLight: 15,
185
- linearLight: 16,
186
- pinLight: 17,
187
- hardMix: 18,
188
- difference: 19,
189
- exclusion: 20,
190
- subtract: 21,
191
- divide: 22
194
+ // src/_types.ts
195
+ var MaskType = /* @__PURE__ */ ((MaskType2) => {
196
+ MaskType2[MaskType2["ALPHA"] = 0] = "ALPHA";
197
+ MaskType2[MaskType2["BINARY"] = 1] = "BINARY";
198
+ return MaskType2;
199
+ })(MaskType || {});
200
+
201
+ // src/color.ts
202
+ function packColor(r, g, b, a) {
203
+ return (a << 24 | b << 16 | g << 8 | r) >>> 0;
204
+ }
205
+ function packRGBA({
206
+ r,
207
+ g,
208
+ b,
209
+ a
210
+ }) {
211
+ return (a << 24 | b << 16 | g << 8 | r) >>> 0;
212
+ }
213
+ var unpackRed = (packed) => packed >>> 0 & 255;
214
+ var unpackGreen = (packed) => packed >>> 8 & 255;
215
+ var unpackBlue = (packed) => packed >>> 16 & 255;
216
+ var unpackAlpha = (packed) => packed >>> 24 & 255;
217
+ function unpackColor(packed) {
218
+ return {
219
+ r: packed >>> 0 & 255,
220
+ g: packed >>> 8 & 255,
221
+ b: packed >>> 16 & 255,
222
+ a: packed >>> 24 & 255
223
+ };
224
+ }
225
+ var SCRATCH_RGBA = {
226
+ r: 0,
227
+ g: 0,
228
+ b: 0,
229
+ a: 0
192
230
  };
193
- var overwriteBase = (src, _dst) => src;
194
- overwriteBase.isOverwrite = true;
231
+ function unpackColorTo(packed, scratch = SCRATCH_RGBA) {
232
+ scratch.r = packed >>> 0 & 255;
233
+ scratch.g = packed >>> 8 & 255;
234
+ scratch.b = packed >>> 16 & 255;
235
+ scratch.a = packed >>> 24 & 255;
236
+ return scratch;
237
+ }
238
+ function colorDistance(a, b) {
239
+ const dr = (a & 255) - (b & 255);
240
+ const dg = (a >>> 8 & 255) - (b >>> 8 & 255);
241
+ const db = (a >>> 16 & 255) - (b >>> 16 & 255);
242
+ const da = (a >>> 24 & 255) - (b >>> 24 & 255);
243
+ return dr * dr + dg * dg + db * db + da * da;
244
+ }
245
+ function lerpColor32(a, b, t) {
246
+ const r = (a & 255) + t * ((b & 255) - (a & 255));
247
+ const g = (a >>> 8 & 255) + t * ((b >>> 8 & 255) - (a >>> 8 & 255));
248
+ const b_ = (a >>> 16 & 255) + t * ((b >>> 16 & 255) - (a >>> 16 & 255));
249
+ const a_ = (a >>> 24 & 255) + t * ((b >>> 24 & 255) - (a >>> 24 & 255));
250
+ return (a_ << 24 | b_ << 16 | g << 8 | r) >>> 0;
251
+ }
252
+ function lerpColor32Fast(src, dst, w) {
253
+ const invA = 255 - w;
254
+ const rb = (src & 16711935) * w + (dst & 16711935) * invA >>> 8 & 16711935;
255
+ const ga = (src >>> 8 & 16711935) * w + (dst >>> 8 & 16711935) * invA >>> 8 & 16711935;
256
+ return (rb | ga << 8) >>> 0;
257
+ }
258
+ function color32ToHex(color) {
259
+ const r = (color & 255).toString(16).padStart(2, "0");
260
+ const g = (color >>> 8 & 255).toString(16).padStart(2, "0");
261
+ const b = (color >>> 16 & 255).toString(16).padStart(2, "0");
262
+ const a = (color >>> 24 & 255).toString(16).padStart(2, "0");
263
+ return `#${r}${g}${b}${a}`;
264
+ }
265
+ function color32ToCssRGBA(color) {
266
+ const r = color & 255;
267
+ const g = color >>> 8 & 255;
268
+ const b = color >>> 16 & 255;
269
+ const a = color >>> 24 & 255;
270
+ const alpha = Number((a / 255).toFixed(3));
271
+ return `rgba(${r},${g},${b},${alpha})`;
272
+ }
195
273
 
196
- // src/BlendModes/BlendModeRegistry.ts
197
- function makeBlendModeRegistry(blendModes, initialEntries) {
198
- const blendToName = /* @__PURE__ */ new Map();
199
- const blendToIndex = /* @__PURE__ */ new Map();
200
- const indexToName = [];
201
- const indexToBlend = [];
202
- const nameToBlend = {};
203
- const nameToIndex = {};
204
- const add = (name, index, blendFn) => {
205
- if (!Number.isFinite(index)) {
206
- throw new Error(`Index "${index}" is not a number. Attempting to add name: "${name}", index: "${index}"`);
274
+ // src/Internal/resolveClipping.ts
275
+ var makeClippedRect = () => ({
276
+ x: 0,
277
+ y: 0,
278
+ w: 0,
279
+ h: 0,
280
+ inBounds: false
281
+ });
282
+ var makeClippedBlit = () => ({
283
+ x: 0,
284
+ y: 0,
285
+ sx: 0,
286
+ sy: 0,
287
+ w: 0,
288
+ h: 0,
289
+ inBounds: false
290
+ });
291
+ function resolveRectClipping(x, y, w, h, boundaryW, boundaryH, out) {
292
+ if (x < 0) {
293
+ w += x;
294
+ x = 0;
295
+ }
296
+ if (y < 0) {
297
+ h += y;
298
+ y = 0;
299
+ }
300
+ const actualW = Math.min(w, boundaryW - x);
301
+ const actualH = Math.min(h, boundaryH - y);
302
+ if (actualW <= 0 || actualH <= 0) {
303
+ out.inBounds = false;
304
+ return out;
305
+ }
306
+ out.x = x;
307
+ out.y = y;
308
+ out.w = actualW;
309
+ out.h = actualH;
310
+ out.inBounds = true;
311
+ return out;
312
+ }
313
+ function resolveBlitClipping(x, y, sx, sy, w, h, dstW, dstH, srcW, srcH, out) {
314
+ if (sx < 0) {
315
+ x -= sx;
316
+ w += sx;
317
+ sx = 0;
318
+ }
319
+ if (sy < 0) {
320
+ y -= sy;
321
+ h += sy;
322
+ sy = 0;
323
+ }
324
+ w = Math.min(w, srcW - sx);
325
+ h = Math.min(h, srcH - sy);
326
+ if (x < 0) {
327
+ sx -= x;
328
+ w += x;
329
+ x = 0;
330
+ }
331
+ if (y < 0) {
332
+ sy -= y;
333
+ h += y;
334
+ y = 0;
335
+ }
336
+ const actualW = Math.min(w, dstW - x);
337
+ const actualH = Math.min(h, dstH - y);
338
+ if (actualW <= 0 || actualH <= 0) {
339
+ out.inBounds = false;
340
+ return out;
341
+ }
342
+ out.x = x;
343
+ out.y = y;
344
+ out.sx = sx;
345
+ out.sy = sy;
346
+ out.w = actualW;
347
+ out.h = actualH;
348
+ out.inBounds = true;
349
+ return out;
350
+ }
351
+
352
+ // src/ImageData/extractImageDataBuffer.ts
353
+ var SCRATCH_BLIT = makeClippedBlit();
354
+ function extractImageDataBuffer(imageData, _x, _y, _w, _h) {
355
+ const {
356
+ x,
357
+ y,
358
+ w,
359
+ h
360
+ } = typeof _x === "object" ? _x : {
361
+ x: _x,
362
+ y: _y,
363
+ w: _w,
364
+ h: _h
365
+ };
366
+ const {
367
+ width: srcW,
368
+ height: srcH,
369
+ data: src
370
+ } = imageData;
371
+ if (w <= 0 || h <= 0) return new Uint8ClampedArray(0);
372
+ const out = new Uint8ClampedArray(w * h * 4);
373
+ const clip = resolveBlitClipping(0, 0, x, y, w, h, w, h, srcW, srcH, SCRATCH_BLIT);
374
+ if (!clip.inBounds) return out;
375
+ const {
376
+ x: dstX,
377
+ y: dstY,
378
+ sx: srcX,
379
+ sy: srcY,
380
+ w: copyW,
381
+ h: copyH
382
+ } = clip;
383
+ const rowLen = copyW * 4;
384
+ for (let row = 0; row < copyH; row++) {
385
+ const srcStart = ((srcY + row) * srcW + srcX) * 4;
386
+ const dstStart = ((dstY + row) * w + dstX) * 4;
387
+ out.set(src.subarray(srcStart, srcStart + rowLen), dstStart);
388
+ }
389
+ return out;
390
+ }
391
+
392
+ // src/Mask/extractMask.ts
393
+ function extractMask(mask, maskWidth, xOrRect, y, w, h) {
394
+ let finalX;
395
+ let finalY;
396
+ let finalW;
397
+ let finalH;
398
+ if (typeof xOrRect === "object") {
399
+ finalX = xOrRect.x;
400
+ finalY = xOrRect.y;
401
+ finalW = xOrRect.w;
402
+ finalH = xOrRect.h;
403
+ } else {
404
+ finalX = xOrRect;
405
+ finalY = y;
406
+ finalW = w;
407
+ finalH = h;
408
+ }
409
+ const out = new Uint8Array(finalW * finalH);
410
+ const srcH = mask.length / maskWidth;
411
+ for (let row = 0; row < finalH; row++) {
412
+ const currentSrcY = finalY + row;
413
+ if (currentSrcY < 0 || currentSrcY >= srcH) {
414
+ continue;
207
415
  }
208
- if (indexToBlend[index]) {
209
- throw new Error(`Blend Mode index: ${index} is already used. Attempting to add name: "${name}", index: "${index}"`);
416
+ const start = Math.max(0, finalX);
417
+ const end = Math.min(maskWidth, finalX + finalW);
418
+ if (start < end) {
419
+ const srcOffset = currentSrcY * maskWidth + start;
420
+ const dstOffset = row * finalW + (start - finalX);
421
+ const count = end - start;
422
+ out.set(mask.subarray(srcOffset, srcOffset + count), dstOffset);
210
423
  }
211
- indexToName[index] = name;
212
- indexToBlend[index] = blendFn;
213
- blendToIndex.set(blendFn, index);
214
- blendToName.set(blendFn, name);
215
- nameToBlend[name] = blendFn;
216
- nameToIndex[name] = index;
217
- };
218
- for (const [name, index] of Object.entries(blendModes)) {
219
- const blend = initialEntries[index];
220
- add(name, index, blend);
221
424
  }
222
- return {
223
- nameToBlend,
224
- nameToIndex,
225
- blendToIndex,
226
- blendToName,
227
- indexToBlend,
228
- indexToName,
229
- indexType: null,
230
- nameType: null
231
- };
232
- }
233
-
234
- // src/BlendModes/blend-modes-fast.ts
235
- var overwriteFast = overwriteBase;
236
- var sourceOverFast = (src, dst) => {
237
- const sa = src >>> 24 & 255;
238
- if (sa === 255) return src;
239
- if (sa === 0) return dst;
240
- const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
241
- const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
242
- const da = dst >>> 24 & 255;
243
- const invA = 255 - sa;
244
- const r = sr * sa + dr * invA >> 8;
245
- const g = sg * sa + dg * invA >> 8;
246
- const b = sb * sa + db * invA >> 8;
247
- const a = 255 * sa + da * invA >> 8;
248
- return (a << 24 | b << 16 | g << 8 | r) >>> 0;
249
- };
250
- var darkenFast = (src, dst) => {
251
- const sa = src >>> 24 & 255;
252
- if (sa === 0) return dst;
253
- const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
254
- const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
255
- const br = sr < dr ? sr : dr;
256
- const bg = sg < dg ? sg : dg;
257
- const bb = sb < db ? sb : db;
258
- if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
259
- const invA = 255 - sa;
260
- const r = br * sa + dr * invA >> 8;
261
- const g = bg * sa + dg * invA >> 8;
262
- const b = bb * sa + db * invA >> 8;
263
- const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
264
- return (a << 24 | b << 16 | g << 8 | r) >>> 0;
265
- };
266
- var multiplyFast = (src, dst) => {
267
- const sa = src >>> 24 & 255;
268
- if (sa === 0) return dst;
269
- const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
270
- const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
271
- const br = sr * dr >> 8;
272
- const bg = sg * dg >> 8;
273
- const bb = sb * db >> 8;
274
- if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
275
- const invA = 255 - sa;
276
- const da = dst >>> 24 & 255;
277
- const r = br * sa + dr * invA >> 8;
278
- const g = bg * sa + dg * invA >> 8;
279
- const b = bb * sa + db * invA >> 8;
280
- const a = 255 * sa + da * invA >> 8;
281
- return (a << 24 | b << 16 | g << 8 | r) >>> 0;
282
- };
283
- var colorBurnFast = (src, dst) => {
284
- const sa = src >>> 24 & 255;
285
- if (sa === 0) return dst;
286
- const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
287
- const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
288
- const br = dr === 255 ? 255 : sr === 0 ? 0 : Math.max(0, 255 - (255 - dr << 8) / sr | 0);
289
- const bg = dg === 255 ? 255 : sg === 0 ? 0 : Math.max(0, 255 - (255 - dg << 8) / sg | 0);
290
- const bb = db === 255 ? 255 : sb === 0 ? 0 : Math.max(0, 255 - (255 - db << 8) / sb | 0);
291
- if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
292
- const invA = 255 - sa;
293
- const da = dst >>> 24 & 255;
294
- const r = br * sa + dr * invA >> 8;
295
- const g = bg * sa + dg * invA >> 8;
296
- const b = bb * sa + db * invA >> 8;
297
- const a = 255 * sa + da * invA >> 8;
298
- return (a << 24 | b << 16 | g << 8 | r) >>> 0;
299
- };
300
- var linearBurnFast = (src, dst) => {
301
- const sa = src >>> 24 & 255;
302
- if (sa === 0) return dst;
303
- const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
304
- const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
305
- const brU = dr + sr - 255;
306
- const bgU = dg + sg - 255;
307
- const bbU = db + sb - 255;
308
- const br = brU < 0 ? 0 : brU;
309
- const bg = bgU < 0 ? 0 : bgU;
310
- const bb = bbU < 0 ? 0 : bbU;
311
- if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
312
- const invA = 255 - sa;
313
- const r = br * sa + dr * invA >> 8;
314
- const g = bg * sa + dg * invA >> 8;
315
- const b = bb * sa + db * invA >> 8;
316
- const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
317
- return (a << 24 | b << 16 | g << 8 | r) >>> 0;
318
- };
319
- var darkerFast = (src, dst) => {
320
- const sa = src >>> 24 & 255;
321
- if (sa === 0) return dst;
322
- const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
323
- const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
324
- const lumSrc = sr * 77 + sg * 151 + sb * 28;
325
- const lumDst = dr * 77 + dg * 151 + db * 28;
326
- let br, bg, bb;
327
- if (lumSrc < lumDst) {
328
- br = sr;
329
- bg = sg;
330
- bb = sb;
331
- } else {
332
- br = dr;
333
- bg = dg;
334
- bb = db;
335
- }
336
- if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
337
- const invA = 255 - sa;
338
- const r = br * sa + dr * invA >> 8;
339
- const g = bg * sa + dg * invA >> 8;
340
- const b = bb * sa + db * invA >> 8;
341
- const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
342
- return (a << 24 | b << 16 | g << 8 | r) >>> 0;
343
- };
344
- var lightenFast = (src, dst) => {
345
- const sa = src >>> 24 & 255;
346
- if (sa === 0) return dst;
347
- const br = Math.max(src & 255, dst & 255);
348
- const bg = Math.max(src >> 8 & 255, dst >> 8 & 255);
349
- const bb = Math.max(src >> 16 & 255, dst >> 16 & 255);
350
- if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
351
- const dr = dst & 255;
352
- const dg = dst >> 8 & 255;
353
- const db = dst >> 16 & 255;
354
- const invA = 255 - sa;
355
- const r = br * sa + dr * invA >> 8;
356
- const g = bg * sa + dg * invA >> 8;
357
- const b = bb * sa + db * invA >> 8;
358
- const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
359
- return (a << 24 | b << 16 | g << 8 | r) >>> 0;
360
- };
361
- var screenFast = (src, dst) => {
362
- const sa = src >>> 24 & 255;
363
- if (sa === 0) return dst;
364
- const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
365
- const br = 255 - ((255 - (src & 255)) * (255 - dr) >> 8);
366
- const bg = 255 - ((255 - (src >>> 8 & 255)) * (255 - dg) >> 8);
367
- const bb = 255 - ((255 - (src >>> 16 & 255)) * (255 - db) >> 8);
368
- if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
369
- const invA = 255 - sa;
370
- const r = br * sa + dr * invA >> 8;
371
- const g = bg * sa + dg * invA >> 8;
372
- const b = bb * sa + db * invA >> 8;
373
- const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
374
- return (a << 24 | b << 16 | g << 8 | r) >>> 0;
375
- };
376
- var colorDodgeFast = (src, dst) => {
377
- const sa = src >>> 24 & 255;
378
- if (sa === 0) return dst;
379
- const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
380
- const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
381
- const br = sr === 255 ? 255 : Math.min(255, (dr << 8) / (255 - sr) | 0);
382
- const bg = sg === 255 ? 255 : Math.min(255, (dg << 8) / (255 - sg) | 0);
383
- const bb = sb === 255 ? 255 : Math.min(255, (db << 8) / (255 - sb) | 0);
384
- if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
385
- const invA = 255 - sa;
386
- const r = br * sa + dr * invA >> 8;
387
- const g = bg * sa + dg * invA >> 8;
388
- const b = bb * sa + db * invA >> 8;
389
- const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
390
- return (a << 24 | b << 16 | g << 8 | r) >>> 0;
391
- };
392
- var linearDodgeFast = (src, dst) => {
393
- const sa = src >>> 24 & 255;
394
- if (sa === 0) return dst;
395
- const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
396
- const brU = (src & 255) + dr;
397
- const bgU = (src >>> 8 & 255) + dg;
398
- const bbU = (src >>> 16 & 255) + db;
399
- const br = brU > 255 ? 255 : brU;
400
- const bg = bgU > 255 ? 255 : bgU;
401
- const bb = bbU > 255 ? 255 : bbU;
402
- if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
403
- const invA = 255 - sa;
404
- const r = br * sa + dr * invA >> 8;
405
- const g = bg * sa + dg * invA >> 8;
406
- const b = bb * sa + db * invA >> 8;
407
- const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
408
- return (a << 24 | b << 16 | g << 8 | r) >>> 0;
409
- };
410
- var lighterFast = (src, dst) => {
411
- const sa = src >>> 24 & 255;
412
- if (sa === 0) return dst;
413
- const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
414
- const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
415
- const lumSrc = sr * 77 + sg * 151 + sb * 28;
416
- const lumDst = dr * 77 + dg * 151 + db * 28;
417
- let br, bg, bb;
418
- if (lumSrc > lumDst) {
419
- br = sr;
420
- bg = sg;
421
- bb = sb;
422
- } else {
423
- br = dr;
424
- bg = dg;
425
- bb = db;
426
- }
427
- if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
428
- const invA = 255 - sa;
429
- const r = br * sa + dr * invA >> 8;
430
- const g = bg * sa + dg * invA >> 8;
431
- const b = bb * sa + db * invA >> 8;
432
- const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
433
- return (a << 24 | b << 16 | g << 8 | r) >>> 0;
434
- };
435
- var overlayFast = (src, dst) => {
436
- const sa = src >>> 24 & 255;
437
- if (sa === 0) return dst;
438
- const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
439
- const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
440
- const br = dr < 128 ? 2 * sr * dr >> 8 : 255 - (2 * (255 - sr) * (255 - dr) >> 8);
441
- const bg = dg < 128 ? 2 * sg * dg >> 8 : 255 - (2 * (255 - sg) * (255 - dg) >> 8);
442
- const bb = db < 128 ? 2 * sb * db >> 8 : 255 - (2 * (255 - sb) * (255 - db) >> 8);
443
- if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
444
- const invA = 255 - sa;
445
- const r = br * sa + dr * invA >> 8;
446
- const g = bg * sa + dg * invA >> 8;
447
- const b = bb * sa + db * invA >> 8;
448
- const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
449
- return (a << 24 | b << 16 | g << 8 | r) >>> 0;
450
- };
451
- var softLightFast = (src, dst) => {
452
- const sa = src >>> 24 & 255;
453
- if (sa === 0) return dst;
454
- const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
455
- const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
456
- const br = (255 - dr) * (sr * dr >> 8) + dr * (255 - ((255 - sr) * (255 - dr) >> 8)) >> 8;
457
- const bg = (255 - dg) * (sg * dg >> 8) + dg * (255 - ((255 - sg) * (255 - dg) >> 8)) >> 8;
458
- const bb = (255 - db) * (sb * db >> 8) + db * (255 - ((255 - sb) * (255 - db) >> 8)) >> 8;
459
- if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
460
- const invA = 255 - sa;
461
- const r = br * sa + dr * invA >> 8;
462
- const g = bg * sa + dg * invA >> 8;
463
- const b = bb * sa + db * invA >> 8;
464
- const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
465
- return (a << 24 | b << 16 | g << 8 | r) >>> 0;
466
- };
467
- var hardLightFast = (src, dst) => {
468
- const sa = src >>> 24 & 255;
469
- if (sa === 0) return dst;
470
- const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
471
- const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
472
- const br = sr < 128 ? 2 * sr * dr >> 8 : 255 - (2 * (255 - sr) * (255 - dr) >> 8);
473
- const bg = sg < 128 ? 2 * sg * dg >> 8 : 255 - (2 * (255 - sg) * (255 - dg) >> 8);
474
- const bb = sb < 128 ? 2 * sb * db >> 8 : 255 - (2 * (255 - sb) * (255 - db) >> 8);
475
- if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
476
- const invA = 255 - sa;
477
- const r = br * sa + dr * invA >> 8;
478
- const g = bg * sa + dg * invA >> 8;
479
- const b = bb * sa + db * invA >> 8;
480
- const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
481
- return (a << 24 | b << 16 | g << 8 | r) >>> 0;
482
- };
483
- var vividLightFast = (src, dst) => {
484
- const sa = src >>> 24 & 255;
485
- if (sa === 0) return dst;
486
- const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
487
- const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
488
- 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);
489
- 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);
490
- 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);
491
- if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
492
- const invA = 255 - sa;
493
- const r = br * sa + dr * invA >> 8;
494
- const g = bg * sa + dg * invA >> 8;
495
- const b = bb * sa + db * invA >> 8;
496
- const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
497
- return (a << 24 | b << 16 | g << 8 | r) >>> 0;
498
- };
499
- var linearLightFast = (src, dst) => {
500
- const sa = src >>> 24 & 255;
501
- if (sa === 0) return dst;
502
- const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
503
- const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
504
- const brU = dr + 2 * sr - 255;
505
- const bgU = dg + 2 * sg - 255;
506
- const bbU = db + 2 * sb - 255;
507
- const br = brU < 0 ? 0 : brU > 255 ? 255 : brU;
508
- const bg = bgU < 0 ? 0 : bgU > 255 ? 255 : bgU;
509
- const bb = bbU < 0 ? 0 : bbU > 255 ? 255 : bbU;
510
- if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
511
- const invA = 255 - sa;
512
- const r = br * sa + dr * invA >> 8;
513
- const g = bg * sa + dg * invA >> 8;
514
- const b = bb * sa + db * invA >> 8;
515
- const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
516
- return (a << 24 | b << 16 | g << 8 | r) >>> 0;
517
- };
518
- var pinLightFast = (src, dst) => {
519
- const sa = src >>> 24 & 255;
520
- if (sa === 0) return dst;
521
- const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
522
- const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
523
- const br = sr < 128 ? dr < 2 * sr ? dr : 2 * sr : dr > 2 * sr - 256 ? dr : 2 * sr - 256;
524
- const bg = sg < 128 ? dg < 2 * sg ? dg : 2 * sg : dg > 2 * sg - 256 ? dg : 2 * sg - 256;
525
- const bb = sb < 128 ? db < 2 * sb ? db : 2 * sb : db > 2 * sb - 256 ? db : 2 * sb - 256;
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 hardMixFast = (src, dst) => {
535
- const sa = src >>> 24 & 255;
536
- if (sa === 0) return dst;
537
- const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
538
- const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
539
- 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;
540
- 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;
541
- 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;
542
- if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
543
- const invA = 255 - sa;
544
- const r = br * sa + dr * invA >> 8;
545
- const g = bg * sa + dg * invA >> 8;
546
- const b = bb * sa + db * invA >> 8;
547
- const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
548
- return (a << 24 | b << 16 | g << 8 | r) >>> 0;
549
- };
550
- var differenceFast = (src, dst) => {
551
- const sa = src >>> 24 & 255;
552
- if (sa === 0) return dst;
553
- const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
554
- const brD = (src & 255) - dr;
555
- const bgD = (src >>> 8 & 255) - dg;
556
- const bbD = (src >>> 16 & 255) - db;
557
- const br = brD < 0 ? -brD : brD;
558
- const bg = bgD < 0 ? -bgD : bgD;
559
- const bb = bbD < 0 ? -bbD : bbD;
560
- if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
561
- const invA = 255 - sa;
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 + (dst >>> 24 & 255) * invA >> 8;
566
- return (a << 24 | b << 16 | g << 8 | r) >>> 0;
567
- };
568
- var exclusionFast = (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 br = dr + sr - (dr * sr >> 7);
574
- const bg = dg + sg - (dg * sg >> 7);
575
- const bb = db + sb - (db * sb >> 7);
576
- if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
577
- const invA = 255 - sa;
578
- const r = br * sa + dr * invA >> 8;
579
- const g = bg * sa + dg * invA >> 8;
580
- const b = bb * sa + db * invA >> 8;
581
- const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
582
- return (a << 24 | b << 16 | g << 8 | r) >>> 0;
583
- };
584
- var subtractFast = (src, dst) => {
585
- const sa = src >>> 24 & 255;
586
- if (sa === 0) return dst;
587
- const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
588
- const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
589
- const brU = dr - sr;
590
- const bgU = dg - sg;
591
- const bbU = db - sb;
592
- const br = brU < 0 ? 0 : brU;
593
- const bg = bgU < 0 ? 0 : bgU;
594
- const bb = bbU < 0 ? 0 : bbU;
595
- if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
596
- const invA = 255 - sa;
597
- const r = br * sa + dr * invA >> 8;
598
- const g = bg * sa + dg * invA >> 8;
599
- const b = bb * sa + db * invA >> 8;
600
- const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
601
- return (a << 24 | b << 16 | g << 8 | r) >>> 0;
602
- };
603
- var divideFast = (src, dst) => {
604
- const sa = src >>> 24 & 255;
605
- if (sa === 0) return dst;
606
- const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
607
- const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
608
- const br = sr === 0 ? 255 : Math.min(255, (dr << 8) / sr | 0);
609
- const bg = sg === 0 ? 255 : Math.min(255, (dg << 8) / sg | 0);
610
- const bb = sb === 0 ? 255 : Math.min(255, (db << 8) / sb | 0);
611
- if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
612
- const invA = 255 - sa;
613
- const r = br * sa + dr * invA >> 8;
614
- const g = bg * sa + dg * invA >> 8;
615
- const b = bb * sa + db * invA >> 8;
616
- const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
617
- return (a << 24 | b << 16 | g << 8 | r) >>> 0;
618
- };
619
- var BASE_FAST_BLEND_MODE_FUNCTIONS = {
620
- [BaseBlendMode.overwrite]: overwriteFast,
621
- [BaseBlendMode.sourceOver]: sourceOverFast,
622
- [BaseBlendMode.darken]: darkenFast,
623
- [BaseBlendMode.multiply]: multiplyFast,
624
- [BaseBlendMode.colorBurn]: colorBurnFast,
625
- [BaseBlendMode.linearBurn]: linearBurnFast,
626
- [BaseBlendMode.darkerColor]: darkerFast,
627
- [BaseBlendMode.lighten]: lightenFast,
628
- [BaseBlendMode.screen]: screenFast,
629
- [BaseBlendMode.colorDodge]: colorDodgeFast,
630
- [BaseBlendMode.linearDodge]: linearDodgeFast,
631
- [BaseBlendMode.lighterColor]: lighterFast,
632
- [BaseBlendMode.overlay]: overlayFast,
633
- [BaseBlendMode.softLight]: softLightFast,
634
- [BaseBlendMode.hardLight]: hardLightFast,
635
- [BaseBlendMode.vividLight]: vividLightFast,
636
- [BaseBlendMode.linearLight]: linearLightFast,
637
- [BaseBlendMode.pinLight]: pinLightFast,
638
- [BaseBlendMode.hardMix]: hardMixFast,
639
- [BaseBlendMode.difference]: differenceFast,
640
- [BaseBlendMode.exclusion]: exclusionFast,
641
- [BaseBlendMode.subtract]: subtractFast,
642
- [BaseBlendMode.divide]: divideFast
643
- };
644
- function makeFastBlendModeRegistry() {
645
- return makeBlendModeRegistry(BaseBlendMode, BASE_FAST_BLEND_MODE_FUNCTIONS);
646
- }
647
-
648
- // src/_types.ts
649
- var MaskType = /* @__PURE__ */ ((MaskType2) => {
650
- MaskType2[MaskType2["ALPHA"] = 0] = "ALPHA";
651
- MaskType2[MaskType2["BINARY"] = 1] = "BINARY";
652
- return MaskType2;
653
- })(MaskType || {});
654
-
655
- // src/color.ts
656
- function packColor(r, g, b, a) {
657
- return (a << 24 | b << 16 | g << 8 | r) >>> 0;
658
- }
659
- function packRGBA({ r, g, b, a }) {
660
- return (a << 24 | b << 16 | g << 8 | r) >>> 0;
661
- }
662
- var unpackRed = (packed) => packed >>> 0 & 255;
663
- var unpackGreen = (packed) => packed >>> 8 & 255;
664
- var unpackBlue = (packed) => packed >>> 16 & 255;
665
- var unpackAlpha = (packed) => packed >>> 24 & 255;
666
- function unpackColor(packed) {
667
- return {
668
- r: packed >>> 0 & 255,
669
- g: packed >>> 8 & 255,
670
- b: packed >>> 16 & 255,
671
- a: packed >>> 24 & 255
672
- };
673
- }
674
- var SCRATCH_RGBA = { r: 0, g: 0, b: 0, a: 0 };
675
- function unpackColorTo(packed, scratch = SCRATCH_RGBA) {
676
- scratch.r = packed >>> 0 & 255;
677
- scratch.g = packed >>> 8 & 255;
678
- scratch.b = packed >>> 16 & 255;
679
- scratch.a = packed >>> 24 & 255;
680
- return scratch;
681
- }
682
- function colorDistance(a, b) {
683
- const dr = (a & 255) - (b & 255);
684
- const dg = (a >>> 8 & 255) - (b >>> 8 & 255);
685
- const db = (a >>> 16 & 255) - (b >>> 16 & 255);
686
- const da = (a >>> 24 & 255) - (b >>> 24 & 255);
687
- return dr * dr + dg * dg + db * db + da * da;
688
- }
689
- function lerpColor32(a, b, t) {
690
- const r = (a & 255) + t * ((b & 255) - (a & 255));
691
- const g = (a >>> 8 & 255) + t * ((b >>> 8 & 255) - (a >>> 8 & 255));
692
- const b_ = (a >>> 16 & 255) + t * ((b >>> 16 & 255) - (a >>> 16 & 255));
693
- const a_ = (a >>> 24 & 255) + t * ((b >>> 24 & 255) - (a >>> 24 & 255));
694
- return (a_ << 24 | b_ << 16 | g << 8 | r) >>> 0;
695
- }
696
- function lerpColor32Fast(src, dst, w) {
697
- const invA = 255 - w;
698
- const rb = (src & 16711935) * w + (dst & 16711935) * invA >>> 8 & 16711935;
699
- const ga = (src >>> 8 & 16711935) * w + (dst >>> 8 & 16711935) * invA >>> 8 & 16711935;
700
- return (rb | ga << 8) >>> 0;
701
- }
702
- function color32ToHex(color) {
703
- const r = (color & 255).toString(16).padStart(2, "0");
704
- const g = (color >>> 8 & 255).toString(16).padStart(2, "0");
705
- const b = (color >>> 16 & 255).toString(16).padStart(2, "0");
706
- const a = (color >>> 24 & 255).toString(16).padStart(2, "0");
707
- return `#${r}${g}${b}${a}`;
708
- }
709
- function color32ToCssRGBA(color) {
710
- const r = color & 255;
711
- const g = color >>> 8 & 255;
712
- const b = color >>> 16 & 255;
713
- const a = color >>> 24 & 255;
714
- const alpha = Number((a / 255).toFixed(3));
715
- return `rgba(${r},${g},${b},${alpha})`;
716
- }
717
-
718
- // src/ImageData/extractImageDataBuffer.ts
719
- function extractImageDataBuffer(imageData, _x, _y, _w, _h) {
720
- const { x, y, w, h } = typeof _x === "object" ? _x : { x: _x, y: _y, w: _w, h: _h };
721
- const { width: srcW, height: srcH, data: src } = imageData;
722
- if (w <= 0 || h <= 0) return new Uint8ClampedArray(0);
723
- const out = new Uint8ClampedArray(w * h * 4);
724
- const x0 = Math.max(0, x);
725
- const y0 = Math.max(0, y);
726
- const x1 = Math.min(srcW, x + w);
727
- const y1 = Math.min(srcH, y + h);
728
- if (x1 <= x0 || y1 <= y0) return out;
729
- for (let row = 0; row < y1 - y0; row++) {
730
- const srcRow = y0 + row;
731
- const srcStart = (srcRow * srcW + x0) * 4;
732
- const rowLen = (x1 - x0) * 4;
733
- const dstRow = y0 - y + row;
734
- const dstCol = x0 - x;
735
- const dstStart = (dstRow * w + dstCol) * 4;
736
- out.set(src.subarray(srcStart, srcStart + rowLen), dstStart);
737
- }
738
- return out;
739
- }
740
-
741
- // src/Mask/extractMask.ts
742
- function extractMask(mask, maskWidth, xOrRect, y, w, h) {
743
- let finalX;
744
- let finalY;
745
- let finalW;
746
- let finalH;
747
- if (typeof xOrRect === "object") {
748
- finalX = xOrRect.x;
749
- finalY = xOrRect.y;
750
- finalW = xOrRect.w;
751
- finalH = xOrRect.h;
752
- } else {
753
- finalX = xOrRect;
754
- finalY = y;
755
- finalW = w;
756
- finalH = h;
757
- }
758
- const out = new Uint8Array(finalW * finalH);
759
- const srcH = mask.length / maskWidth;
760
- for (let row = 0; row < finalH; row++) {
761
- const currentSrcY = finalY + row;
762
- if (currentSrcY < 0 || currentSrcY >= srcH) {
763
- continue;
764
- }
765
- const start = Math.max(0, finalX);
766
- const end = Math.min(maskWidth, finalX + finalW);
767
- if (start < end) {
768
- const srcOffset = currentSrcY * maskWidth + start;
769
- const dstOffset = row * finalW + (start - finalX);
770
- const count = end - start;
771
- out.set(
772
- mask.subarray(srcOffset, srcOffset + count),
773
- dstOffset
774
- );
775
- }
776
- }
777
- return out;
425
+ return out;
778
426
  }
779
427
 
780
428
  // src/Rect/trimRectBounds.ts
@@ -784,14 +432,8 @@ function trimRectBounds(target, bounds) {
784
432
  const originalW = target.w;
785
433
  const intersectedX = Math.max(target.x, bounds.x);
786
434
  const intersectedY = Math.max(target.y, bounds.y);
787
- const intersectedMaxX = Math.min(
788
- target.x + target.w,
789
- bounds.x + bounds.w
790
- );
791
- const intersectedMaxY = Math.min(
792
- target.y + target.h,
793
- bounds.y + bounds.h
794
- );
435
+ const intersectedMaxX = Math.min(target.x + target.w, bounds.x + bounds.w);
436
+ const intersectedMaxY = Math.min(target.y + target.h, bounds.y + bounds.h);
795
437
  if (intersectedMaxX <= intersectedX || intersectedMaxY <= intersectedY) {
796
438
  target.w = 0;
797
439
  target.h = 0;
@@ -809,14 +451,7 @@ function trimRectBounds(target, bounds) {
809
451
  target.w = intersectedW;
810
452
  target.h = intersectedH;
811
453
  if ("mask" in target && target.mask) {
812
- const currentMask = extractMask(
813
- target.mask,
814
- originalW,
815
- offsetX,
816
- offsetY,
817
- intersectedW,
818
- intersectedH
819
- );
454
+ const currentMask = extractMask(target.mask, originalW, offsetX, offsetY, intersectedW, intersectedH);
820
455
  let minX = intersectedW;
821
456
  let maxX = -1;
822
457
  let minY = intersectedH;
@@ -840,14 +475,7 @@ function trimRectBounds(target, bounds) {
840
475
  const finalW = maxX - minX + 1;
841
476
  const finalH = maxY - minY + 1;
842
477
  if (finalW !== intersectedW || finalH !== intersectedH) {
843
- target.mask = extractMask(
844
- currentMask,
845
- intersectedW,
846
- minX,
847
- minY,
848
- finalW,
849
- finalH
850
- );
478
+ target.mask = extractMask(currentMask, intersectedW, minX, minY, finalW, finalH);
851
479
  target.x += minX;
852
480
  target.y += minY;
853
481
  target.w = finalW;
@@ -870,11 +498,7 @@ function floodFillSelection(img, startX, startY, {
870
498
  data32 = img.data32;
871
499
  imageData = img.imageData;
872
500
  } else {
873
- data32 = new Uint32Array(
874
- img.data.buffer,
875
- img.data.byteOffset,
876
- img.data.byteLength >> 2
877
- );
501
+ data32 = new Uint32Array(img.data.buffer, img.data.byteOffset, img.data.byteLength >> 2);
878
502
  imageData = img;
879
503
  }
880
504
  const {
@@ -985,35 +609,514 @@ function floodFillSelection(img, startX, startY, {
985
609
  finalMask[my * sw + mx] = 1;
986
610
  }
987
611
  }
988
- trimRectBounds(
989
- selectionRect,
990
- { x: 0, y: 0, w: width, h: height }
991
- );
992
- const extracted = extractImageDataBuffer(
993
- imageData,
994
- selectionRect.x,
995
- selectionRect.y,
996
- selectionRect.w,
997
- selectionRect.h
998
- );
612
+ trimRectBounds(selectionRect, {
613
+ x: 0,
614
+ y: 0,
615
+ w: width,
616
+ h: height
617
+ });
618
+ const extracted = extractImageDataBuffer(imageData, selectionRect.x, selectionRect.y, selectionRect.w, selectionRect.h);
619
+ return {
620
+ startX,
621
+ startY,
622
+ selectionRect,
623
+ pixels: extracted
624
+ };
625
+ }
626
+
627
+ // src/BlendModes/blend-modes.ts
628
+ var BaseBlendMode = {
629
+ overwrite: 0,
630
+ sourceOver: 1,
631
+ darken: 2,
632
+ multiply: 3,
633
+ colorBurn: 4,
634
+ linearBurn: 5,
635
+ darkerColor: 6,
636
+ lighten: 7,
637
+ screen: 8,
638
+ colorDodge: 9,
639
+ linearDodge: 10,
640
+ lighterColor: 11,
641
+ overlay: 12,
642
+ softLight: 13,
643
+ hardLight: 14,
644
+ vividLight: 15,
645
+ linearLight: 16,
646
+ pinLight: 17,
647
+ hardMix: 18,
648
+ difference: 19,
649
+ exclusion: 20,
650
+ subtract: 21,
651
+ divide: 22
652
+ };
653
+ var overwriteBase = (src, _dst) => src;
654
+ overwriteBase.isOverwrite = true;
655
+
656
+ // src/BlendModes/BlendModeRegistry.ts
657
+ function makeBlendModeRegistry(blendModes, initialEntries, registryName = "anonymous") {
658
+ const blendToName = /* @__PURE__ */ new Map();
659
+ const blendToIndex = /* @__PURE__ */ new Map();
660
+ const indexToName = [];
661
+ const indexToBlend = [];
662
+ const nameToBlend = {};
663
+ const nameToIndex = {};
664
+ const add = (name, index, blendFn) => {
665
+ if (!Number.isFinite(index)) {
666
+ throw new Error(`Index "${index}" is not a number. Attempting to add name: "${name}", index: "${index}"`);
667
+ }
668
+ if (indexToBlend[index]) {
669
+ throw new Error(`Blend Mode index: ${index} is already used. Attempting to add name: "${name}", index: "${index}"`);
670
+ }
671
+ indexToName[index] = name;
672
+ indexToBlend[index] = blendFn;
673
+ blendToIndex.set(blendFn, index);
674
+ blendToName.set(blendFn, name);
675
+ nameToBlend[name] = blendFn;
676
+ nameToIndex[name] = index;
677
+ };
678
+ for (const [name, index] of Object.entries(blendModes)) {
679
+ const blend = initialEntries[index];
680
+ add(name, index, blend);
681
+ }
999
682
  return {
1000
- startX,
1001
- startY,
1002
- selectionRect,
1003
- pixels: extracted
683
+ registryName,
684
+ nameToBlend,
685
+ nameToIndex,
686
+ blendToIndex,
687
+ blendToName,
688
+ indexToBlend,
689
+ indexToName,
690
+ indexType: null,
691
+ nameType: null
1004
692
  };
1005
693
  }
1006
694
 
695
+ // src/BlendModes/blend-modes-fast.ts
696
+ var overwriteFast = overwriteBase;
697
+ var sourceOverFast = (src, dst) => {
698
+ const sa = src >>> 24 & 255;
699
+ if (sa === 255) return src;
700
+ if (sa === 0) return dst;
701
+ const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
702
+ const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
703
+ const da = dst >>> 24 & 255;
704
+ const invA = 255 - sa;
705
+ const r = sr * sa + dr * invA >> 8;
706
+ const g = sg * sa + dg * invA >> 8;
707
+ const b = sb * sa + db * invA >> 8;
708
+ const a = 255 * sa + da * invA >> 8;
709
+ return (a << 24 | b << 16 | g << 8 | r) >>> 0;
710
+ };
711
+ var darkenFast = (src, dst) => {
712
+ const sa = src >>> 24 & 255;
713
+ if (sa === 0) return dst;
714
+ const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
715
+ const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
716
+ const br = sr < dr ? sr : dr;
717
+ const bg = sg < dg ? sg : dg;
718
+ const bb = sb < db ? sb : db;
719
+ if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
720
+ const invA = 255 - sa;
721
+ const r = br * sa + dr * invA >> 8;
722
+ const g = bg * sa + dg * invA >> 8;
723
+ const b = bb * sa + db * invA >> 8;
724
+ const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
725
+ return (a << 24 | b << 16 | g << 8 | r) >>> 0;
726
+ };
727
+ var multiplyFast = (src, dst) => {
728
+ const sa = src >>> 24 & 255;
729
+ if (sa === 0) return dst;
730
+ const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
731
+ const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
732
+ const br = sr * dr >> 8;
733
+ const bg = sg * dg >> 8;
734
+ const bb = sb * db >> 8;
735
+ if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
736
+ const invA = 255 - sa;
737
+ const da = dst >>> 24 & 255;
738
+ const r = br * sa + dr * invA >> 8;
739
+ const g = bg * sa + dg * invA >> 8;
740
+ const b = bb * sa + db * invA >> 8;
741
+ const a = 255 * sa + da * invA >> 8;
742
+ return (a << 24 | b << 16 | g << 8 | r) >>> 0;
743
+ };
744
+ var colorBurnFast = (src, dst) => {
745
+ const sa = src >>> 24 & 255;
746
+ if (sa === 0) return dst;
747
+ const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
748
+ const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
749
+ const br = dr === 255 ? 255 : sr === 0 ? 0 : Math.max(0, 255 - (255 - dr << 8) / sr | 0);
750
+ const bg = dg === 255 ? 255 : sg === 0 ? 0 : Math.max(0, 255 - (255 - dg << 8) / sg | 0);
751
+ const bb = db === 255 ? 255 : sb === 0 ? 0 : Math.max(0, 255 - (255 - db << 8) / sb | 0);
752
+ if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
753
+ const invA = 255 - sa;
754
+ const da = dst >>> 24 & 255;
755
+ const r = br * sa + dr * invA >> 8;
756
+ const g = bg * sa + dg * invA >> 8;
757
+ const b = bb * sa + db * invA >> 8;
758
+ const a = 255 * sa + da * invA >> 8;
759
+ return (a << 24 | b << 16 | g << 8 | r) >>> 0;
760
+ };
761
+ var linearBurnFast = (src, dst) => {
762
+ const sa = src >>> 24 & 255;
763
+ if (sa === 0) return dst;
764
+ const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
765
+ const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
766
+ const brU = dr + sr - 255;
767
+ const bgU = dg + sg - 255;
768
+ const bbU = db + sb - 255;
769
+ const br = brU < 0 ? 0 : brU;
770
+ const bg = bgU < 0 ? 0 : bgU;
771
+ const bb = bbU < 0 ? 0 : bbU;
772
+ if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
773
+ const invA = 255 - sa;
774
+ const r = br * sa + dr * invA >> 8;
775
+ const g = bg * sa + dg * invA >> 8;
776
+ const b = bb * sa + db * invA >> 8;
777
+ const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
778
+ return (a << 24 | b << 16 | g << 8 | r) >>> 0;
779
+ };
780
+ var darkerFast = (src, dst) => {
781
+ const sa = src >>> 24 & 255;
782
+ if (sa === 0) return dst;
783
+ const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
784
+ const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
785
+ const lumSrc = sr * 77 + sg * 151 + sb * 28;
786
+ const lumDst = dr * 77 + dg * 151 + db * 28;
787
+ let br, bg, bb;
788
+ if (lumSrc < lumDst) {
789
+ br = sr;
790
+ bg = sg;
791
+ bb = sb;
792
+ } else {
793
+ br = dr;
794
+ bg = dg;
795
+ bb = db;
796
+ }
797
+ if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
798
+ const invA = 255 - sa;
799
+ const r = br * sa + dr * invA >> 8;
800
+ const g = bg * sa + dg * invA >> 8;
801
+ const b = bb * sa + db * invA >> 8;
802
+ const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
803
+ return (a << 24 | b << 16 | g << 8 | r) >>> 0;
804
+ };
805
+ var lightenFast = (src, dst) => {
806
+ const sa = src >>> 24 & 255;
807
+ if (sa === 0) return dst;
808
+ const br = Math.max(src & 255, dst & 255);
809
+ const bg = Math.max(src >> 8 & 255, dst >> 8 & 255);
810
+ const bb = Math.max(src >> 16 & 255, dst >> 16 & 255);
811
+ if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
812
+ const dr = dst & 255;
813
+ const dg = dst >> 8 & 255;
814
+ const db = dst >> 16 & 255;
815
+ const invA = 255 - sa;
816
+ const r = br * sa + dr * invA >> 8;
817
+ const g = bg * sa + dg * invA >> 8;
818
+ const b = bb * sa + db * invA >> 8;
819
+ const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
820
+ return (a << 24 | b << 16 | g << 8 | r) >>> 0;
821
+ };
822
+ var screenFast = (src, dst) => {
823
+ const sa = src >>> 24 & 255;
824
+ if (sa === 0) return dst;
825
+ const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
826
+ const br = 255 - ((255 - (src & 255)) * (255 - dr) >> 8);
827
+ const bg = 255 - ((255 - (src >>> 8 & 255)) * (255 - dg) >> 8);
828
+ const bb = 255 - ((255 - (src >>> 16 & 255)) * (255 - db) >> 8);
829
+ if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
830
+ const invA = 255 - sa;
831
+ const r = br * sa + dr * invA >> 8;
832
+ const g = bg * sa + dg * invA >> 8;
833
+ const b = bb * sa + db * invA >> 8;
834
+ const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
835
+ return (a << 24 | b << 16 | g << 8 | r) >>> 0;
836
+ };
837
+ var colorDodgeFast = (src, dst) => {
838
+ const sa = src >>> 24 & 255;
839
+ if (sa === 0) return dst;
840
+ const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
841
+ const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
842
+ const br = sr === 255 ? 255 : Math.min(255, (dr << 8) / (255 - sr) | 0);
843
+ const bg = sg === 255 ? 255 : Math.min(255, (dg << 8) / (255 - sg) | 0);
844
+ const bb = sb === 255 ? 255 : Math.min(255, (db << 8) / (255 - sb) | 0);
845
+ if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
846
+ const invA = 255 - sa;
847
+ const r = br * sa + dr * invA >> 8;
848
+ const g = bg * sa + dg * invA >> 8;
849
+ const b = bb * sa + db * invA >> 8;
850
+ const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
851
+ return (a << 24 | b << 16 | g << 8 | r) >>> 0;
852
+ };
853
+ var linearDodgeFast = (src, dst) => {
854
+ const sa = src >>> 24 & 255;
855
+ if (sa === 0) return dst;
856
+ const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
857
+ const brU = (src & 255) + dr;
858
+ const bgU = (src >>> 8 & 255) + dg;
859
+ const bbU = (src >>> 16 & 255) + db;
860
+ const br = brU > 255 ? 255 : brU;
861
+ const bg = bgU > 255 ? 255 : bgU;
862
+ const bb = bbU > 255 ? 255 : 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 lighterFast = (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 lumSrc = sr * 77 + sg * 151 + sb * 28;
877
+ const lumDst = dr * 77 + dg * 151 + db * 28;
878
+ let br, bg, bb;
879
+ if (lumSrc > lumDst) {
880
+ br = sr;
881
+ bg = sg;
882
+ bb = sb;
883
+ } else {
884
+ br = dr;
885
+ bg = dg;
886
+ bb = db;
887
+ }
888
+ if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
889
+ const invA = 255 - sa;
890
+ const r = br * sa + dr * invA >> 8;
891
+ const g = bg * sa + dg * invA >> 8;
892
+ const b = bb * sa + db * invA >> 8;
893
+ const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
894
+ return (a << 24 | b << 16 | g << 8 | r) >>> 0;
895
+ };
896
+ var overlayFast = (src, dst) => {
897
+ const sa = src >>> 24 & 255;
898
+ if (sa === 0) return dst;
899
+ const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
900
+ const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
901
+ const br = dr < 128 ? 2 * sr * dr >> 8 : 255 - (2 * (255 - sr) * (255 - dr) >> 8);
902
+ const bg = dg < 128 ? 2 * sg * dg >> 8 : 255 - (2 * (255 - sg) * (255 - dg) >> 8);
903
+ const bb = db < 128 ? 2 * sb * db >> 8 : 255 - (2 * (255 - sb) * (255 - db) >> 8);
904
+ if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
905
+ const invA = 255 - sa;
906
+ const r = br * sa + dr * invA >> 8;
907
+ const g = bg * sa + dg * invA >> 8;
908
+ const b = bb * sa + db * invA >> 8;
909
+ const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
910
+ return (a << 24 | b << 16 | g << 8 | r) >>> 0;
911
+ };
912
+ var softLightFast = (src, dst) => {
913
+ const sa = src >>> 24 & 255;
914
+ if (sa === 0) return dst;
915
+ const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
916
+ const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
917
+ const br = (255 - dr) * (sr * dr >> 8) + dr * (255 - ((255 - sr) * (255 - dr) >> 8)) >> 8;
918
+ const bg = (255 - dg) * (sg * dg >> 8) + dg * (255 - ((255 - sg) * (255 - dg) >> 8)) >> 8;
919
+ const bb = (255 - db) * (sb * db >> 8) + db * (255 - ((255 - sb) * (255 - db) >> 8)) >> 8;
920
+ if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
921
+ const invA = 255 - sa;
922
+ const r = br * sa + dr * invA >> 8;
923
+ const g = bg * sa + dg * invA >> 8;
924
+ const b = bb * sa + db * invA >> 8;
925
+ const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
926
+ return (a << 24 | b << 16 | g << 8 | r) >>> 0;
927
+ };
928
+ var hardLightFast = (src, dst) => {
929
+ const sa = src >>> 24 & 255;
930
+ if (sa === 0) return dst;
931
+ const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
932
+ const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
933
+ const br = sr < 128 ? 2 * sr * dr >> 8 : 255 - (2 * (255 - sr) * (255 - dr) >> 8);
934
+ const bg = sg < 128 ? 2 * sg * dg >> 8 : 255 - (2 * (255 - sg) * (255 - dg) >> 8);
935
+ const bb = sb < 128 ? 2 * sb * db >> 8 : 255 - (2 * (255 - sb) * (255 - db) >> 8);
936
+ if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
937
+ const invA = 255 - sa;
938
+ const r = br * sa + dr * invA >> 8;
939
+ const g = bg * sa + dg * invA >> 8;
940
+ const b = bb * sa + db * invA >> 8;
941
+ const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
942
+ return (a << 24 | b << 16 | g << 8 | r) >>> 0;
943
+ };
944
+ var vividLightFast = (src, dst) => {
945
+ const sa = src >>> 24 & 255;
946
+ if (sa === 0) return dst;
947
+ const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
948
+ const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
949
+ 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);
950
+ 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);
951
+ 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);
952
+ if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
953
+ const invA = 255 - sa;
954
+ const r = br * sa + dr * invA >> 8;
955
+ const g = bg * sa + dg * invA >> 8;
956
+ const b = bb * sa + db * invA >> 8;
957
+ const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
958
+ return (a << 24 | b << 16 | g << 8 | r) >>> 0;
959
+ };
960
+ var linearLightFast = (src, dst) => {
961
+ const sa = src >>> 24 & 255;
962
+ if (sa === 0) return dst;
963
+ const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
964
+ const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
965
+ const brU = dr + 2 * sr - 255;
966
+ const bgU = dg + 2 * sg - 255;
967
+ const bbU = db + 2 * sb - 255;
968
+ const br = brU < 0 ? 0 : brU > 255 ? 255 : brU;
969
+ const bg = bgU < 0 ? 0 : bgU > 255 ? 255 : bgU;
970
+ const bb = bbU < 0 ? 0 : bbU > 255 ? 255 : bbU;
971
+ if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
972
+ const invA = 255 - sa;
973
+ const r = br * sa + dr * invA >> 8;
974
+ const g = bg * sa + dg * invA >> 8;
975
+ const b = bb * sa + db * invA >> 8;
976
+ const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
977
+ return (a << 24 | b << 16 | g << 8 | r) >>> 0;
978
+ };
979
+ var pinLightFast = (src, dst) => {
980
+ const sa = src >>> 24 & 255;
981
+ if (sa === 0) return dst;
982
+ const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
983
+ const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
984
+ const br = sr < 128 ? dr < 2 * sr ? dr : 2 * sr : dr > 2 * sr - 256 ? dr : 2 * sr - 256;
985
+ const bg = sg < 128 ? dg < 2 * sg ? dg : 2 * sg : dg > 2 * sg - 256 ? dg : 2 * sg - 256;
986
+ const bb = sb < 128 ? db < 2 * sb ? db : 2 * sb : db > 2 * sb - 256 ? db : 2 * sb - 256;
987
+ if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
988
+ const invA = 255 - sa;
989
+ const r = br * sa + dr * invA >> 8;
990
+ const g = bg * sa + dg * invA >> 8;
991
+ const b = bb * sa + db * invA >> 8;
992
+ const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
993
+ return (a << 24 | b << 16 | g << 8 | r) >>> 0;
994
+ };
995
+ var hardMixFast = (src, dst) => {
996
+ const sa = src >>> 24 & 255;
997
+ if (sa === 0) return dst;
998
+ const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
999
+ const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
1000
+ 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;
1001
+ 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;
1002
+ 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;
1003
+ if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
1004
+ const invA = 255 - sa;
1005
+ const r = br * sa + dr * invA >> 8;
1006
+ const g = bg * sa + dg * invA >> 8;
1007
+ const b = bb * sa + db * invA >> 8;
1008
+ const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
1009
+ return (a << 24 | b << 16 | g << 8 | r) >>> 0;
1010
+ };
1011
+ var differenceFast = (src, dst) => {
1012
+ const sa = src >>> 24 & 255;
1013
+ if (sa === 0) return dst;
1014
+ const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
1015
+ const brD = (src & 255) - dr;
1016
+ const bgD = (src >>> 8 & 255) - dg;
1017
+ const bbD = (src >>> 16 & 255) - db;
1018
+ const br = brD < 0 ? -brD : brD;
1019
+ const bg = bgD < 0 ? -bgD : bgD;
1020
+ const bb = bbD < 0 ? -bbD : bbD;
1021
+ if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
1022
+ const invA = 255 - sa;
1023
+ const r = br * sa + dr * invA >> 8;
1024
+ const g = bg * sa + dg * invA >> 8;
1025
+ const b = bb * sa + db * invA >> 8;
1026
+ const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
1027
+ return (a << 24 | b << 16 | g << 8 | r) >>> 0;
1028
+ };
1029
+ var exclusionFast = (src, dst) => {
1030
+ const sa = src >>> 24 & 255;
1031
+ if (sa === 0) return dst;
1032
+ const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
1033
+ const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
1034
+ const br = dr + sr - (dr * sr >> 7);
1035
+ const bg = dg + sg - (dg * sg >> 7);
1036
+ const bb = db + sb - (db * sb >> 7);
1037
+ if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
1038
+ const invA = 255 - sa;
1039
+ const r = br * sa + dr * invA >> 8;
1040
+ const g = bg * sa + dg * invA >> 8;
1041
+ const b = bb * sa + db * invA >> 8;
1042
+ const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
1043
+ return (a << 24 | b << 16 | g << 8 | r) >>> 0;
1044
+ };
1045
+ var subtractFast = (src, dst) => {
1046
+ const sa = src >>> 24 & 255;
1047
+ if (sa === 0) return dst;
1048
+ const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
1049
+ const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
1050
+ const brU = dr - sr;
1051
+ const bgU = dg - sg;
1052
+ const bbU = db - sb;
1053
+ const br = brU < 0 ? 0 : brU;
1054
+ const bg = bgU < 0 ? 0 : bgU;
1055
+ const bb = bbU < 0 ? 0 : bbU;
1056
+ if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
1057
+ const invA = 255 - sa;
1058
+ const r = br * sa + dr * invA >> 8;
1059
+ const g = bg * sa + dg * invA >> 8;
1060
+ const b = bb * sa + db * invA >> 8;
1061
+ const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
1062
+ return (a << 24 | b << 16 | g << 8 | r) >>> 0;
1063
+ };
1064
+ var divideFast = (src, dst) => {
1065
+ const sa = src >>> 24 & 255;
1066
+ if (sa === 0) return dst;
1067
+ const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
1068
+ const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
1069
+ const br = sr === 0 ? 255 : Math.min(255, (dr << 8) / sr | 0);
1070
+ const bg = sg === 0 ? 255 : Math.min(255, (dg << 8) / sg | 0);
1071
+ const bb = sb === 0 ? 255 : Math.min(255, (db << 8) / sb | 0);
1072
+ if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
1073
+ const invA = 255 - sa;
1074
+ const r = br * sa + dr * invA >> 8;
1075
+ const g = bg * sa + dg * invA >> 8;
1076
+ const b = bb * sa + db * invA >> 8;
1077
+ const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
1078
+ return (a << 24 | b << 16 | g << 8 | r) >>> 0;
1079
+ };
1080
+ var BASE_FAST_BLEND_MODE_FUNCTIONS = {
1081
+ [BaseBlendMode.overwrite]: overwriteFast,
1082
+ [BaseBlendMode.sourceOver]: sourceOverFast,
1083
+ [BaseBlendMode.darken]: darkenFast,
1084
+ [BaseBlendMode.multiply]: multiplyFast,
1085
+ [BaseBlendMode.colorBurn]: colorBurnFast,
1086
+ [BaseBlendMode.linearBurn]: linearBurnFast,
1087
+ [BaseBlendMode.darkerColor]: darkerFast,
1088
+ [BaseBlendMode.lighten]: lightenFast,
1089
+ [BaseBlendMode.screen]: screenFast,
1090
+ [BaseBlendMode.colorDodge]: colorDodgeFast,
1091
+ [BaseBlendMode.linearDodge]: linearDodgeFast,
1092
+ [BaseBlendMode.lighterColor]: lighterFast,
1093
+ [BaseBlendMode.overlay]: overlayFast,
1094
+ [BaseBlendMode.softLight]: softLightFast,
1095
+ [BaseBlendMode.hardLight]: hardLightFast,
1096
+ [BaseBlendMode.vividLight]: vividLightFast,
1097
+ [BaseBlendMode.linearLight]: linearLightFast,
1098
+ [BaseBlendMode.pinLight]: pinLightFast,
1099
+ [BaseBlendMode.hardMix]: hardMixFast,
1100
+ [BaseBlendMode.difference]: differenceFast,
1101
+ [BaseBlendMode.exclusion]: exclusionFast,
1102
+ [BaseBlendMode.subtract]: subtractFast,
1103
+ [BaseBlendMode.divide]: divideFast
1104
+ };
1105
+ function makeFastBlendModeRegistry(name = "fast") {
1106
+ return makeBlendModeRegistry(BaseBlendMode, BASE_FAST_BLEND_MODE_FUNCTIONS, name);
1107
+ }
1108
+
1007
1109
  // src/BlendModes/blend-modes-perfect.ts
1008
1110
  var overwritePerfect = overwriteBase;
1009
1111
  var sourceOverPerfect = (src, dst) => {
1010
1112
  const sa = src >>> 24 & 255;
1011
1113
  if (sa === 255) return src;
1012
1114
  if (sa === 0) return dst;
1013
- const invA = 255 - sa;
1115
+ const da = dst >>> 24 & 255;
1116
+ if (da === 0) return src;
1014
1117
  const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
1015
1118
  const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
1016
- const da = dst >>> 24 & 255;
1119
+ const invA = 255 - sa;
1017
1120
  const tR = sr * sa + dr * invA;
1018
1121
  const r = tR + 1 + (tR >> 8) >> 8;
1019
1122
  const tG = sg * sa + dg * invA;
@@ -1555,9 +1658,53 @@ var BASE_PERFECT_BLEND_MODE_FUNCTIONS = {
1555
1658
  [BaseBlendMode.subtract]: subtractPerfect,
1556
1659
  [BaseBlendMode.divide]: dividePerfect
1557
1660
  };
1558
- function makePerfectBlendModeRegistry() {
1559
- return makeBlendModeRegistry(BaseBlendMode, BASE_PERFECT_BLEND_MODE_FUNCTIONS);
1661
+ function makePerfectBlendModeRegistry(name = "perfect") {
1662
+ return makeBlendModeRegistry(BaseBlendMode, BASE_PERFECT_BLEND_MODE_FUNCTIONS, name);
1663
+ }
1664
+
1665
+ // src/BlendModes/toBlendModeIndexAndName.ts
1666
+ function toBlendModeIndexAndName(input) {
1667
+ if (typeof input === "number") {
1668
+ const name = getKeyByValue(BaseBlendMode, input);
1669
+ if (name === void 0) throw new Error(`Invalid index: ${input}`);
1670
+ return {
1671
+ blendIndex: input,
1672
+ blendName: name
1673
+ };
1674
+ }
1675
+ const trimmed = input.trim();
1676
+ const num = Number(trimmed);
1677
+ const isNumeric = trimmed !== "" && !Number.isNaN(num);
1678
+ if (isNumeric && Number.isInteger(num)) {
1679
+ console.log({
1680
+ trimmed,
1681
+ num,
1682
+ isNumeric,
1683
+ isInt: Number.isInteger(num)
1684
+ });
1685
+ const name = getKeyByValue(BaseBlendMode, num);
1686
+ console.log({
1687
+ name
1688
+ });
1689
+ if (name === void 0) throw new Error(`Invalid index: ${num}`);
1690
+ return {
1691
+ blendIndex: num,
1692
+ blendName: name
1693
+ };
1694
+ }
1695
+ if (trimmed in BaseBlendMode) {
1696
+ return {
1697
+ blendIndex: BaseBlendMode[trimmed],
1698
+ blendName: trimmed
1699
+ };
1700
+ }
1701
+ throw new Error(`Invalid blend mode: ${JSON.stringify(input)}`);
1560
1702
  }
1703
+ var getKeyByValue = (obj, value) => {
1704
+ for (const key in obj) {
1705
+ if (obj[key] === value) return key;
1706
+ }
1707
+ };
1561
1708
 
1562
1709
  // src/Canvas/_constants.ts
1563
1710
  var OFFSCREEN_CANVAS_CTX_FAILED = "Failed to create OffscreenCanvas context";
@@ -1596,7 +1743,10 @@ function makeReusableCanvas() {
1596
1743
  } else {
1597
1744
  ctx.clearRect(0, 0, width, height);
1598
1745
  }
1599
- return { canvas, ctx };
1746
+ return {
1747
+ canvas,
1748
+ ctx
1749
+ };
1600
1750
  }
1601
1751
  get2.reset = () => {
1602
1752
  canvas = null;
@@ -1610,21 +1760,13 @@ async function imgBlobToImageData(blob) {
1610
1760
  let bitmap = null;
1611
1761
  try {
1612
1762
  bitmap = await createImageBitmap(blob);
1613
- const canvas = new OffscreenCanvas(
1614
- bitmap.width,
1615
- bitmap.height
1616
- );
1763
+ const canvas = new OffscreenCanvas(bitmap.width, bitmap.height);
1617
1764
  const ctx = canvas.getContext("2d");
1618
1765
  if (!ctx) {
1619
1766
  throw new Error("Failed to get 2D context");
1620
1767
  }
1621
1768
  ctx.drawImage(bitmap, 0, 0);
1622
- return ctx.getImageData(
1623
- 0,
1624
- 0,
1625
- bitmap.width,
1626
- bitmap.height
1627
- );
1769
+ return ctx.getImageData(0, 0, bitmap.width, bitmap.height);
1628
1770
  } finally {
1629
1771
  bitmap?.close();
1630
1772
  }
@@ -1672,6 +1814,64 @@ async function writeImageDataToClipboard(imageData) {
1672
1814
  return writeImgBlobToClipboard(blob);
1673
1815
  }
1674
1816
 
1817
+ // src/History/HistoryManager.ts
1818
+ var HistoryManager = class {
1819
+ constructor(maxSteps = 50) {
1820
+ this.maxSteps = maxSteps;
1821
+ this.undoStack = [];
1822
+ this.redoStack = [];
1823
+ this.listeners = /* @__PURE__ */ new Set();
1824
+ }
1825
+ undoStack;
1826
+ redoStack;
1827
+ listeners;
1828
+ get canUndo() {
1829
+ return this.undoStack.length > 0;
1830
+ }
1831
+ get canRedo() {
1832
+ return this.redoStack.length > 0;
1833
+ }
1834
+ subscribe(fn) {
1835
+ this.listeners.add(fn);
1836
+ return () => this.listeners.delete(fn);
1837
+ }
1838
+ notify() {
1839
+ this.listeners.forEach((fn) => fn());
1840
+ }
1841
+ commit(action) {
1842
+ this.undoStack.push(action);
1843
+ this.clearRedoStack();
1844
+ if (this.undoStack.length > this.maxSteps) {
1845
+ this.undoStack.shift()?.dispose?.();
1846
+ }
1847
+ this.notify();
1848
+ }
1849
+ undo() {
1850
+ let action = this.undoStack.pop();
1851
+ if (!action) return;
1852
+ this.redoStack.push(action);
1853
+ action.undo();
1854
+ this.notify();
1855
+ }
1856
+ redo() {
1857
+ let action = this.redoStack.pop();
1858
+ if (!action) return;
1859
+ this.undoStack.push(action);
1860
+ action.redo();
1861
+ this.notify();
1862
+ }
1863
+ clearRedoStack() {
1864
+ let length = this.redoStack.length;
1865
+ for (let i = 0; i < length; i++) {
1866
+ let action = this.redoStack[i];
1867
+ if (action) {
1868
+ action.dispose?.();
1869
+ }
1870
+ }
1871
+ this.redoStack.length = 0;
1872
+ }
1873
+ };
1874
+
1675
1875
  // src/History/PixelPatchTiles.ts
1676
1876
  var PixelTile = class {
1677
1877
  constructor(id, tx, ty, tileArea) {
@@ -1725,12 +1925,7 @@ var PixelAccumulator = class {
1725
1925
  tile.ty = ty;
1726
1926
  return tile;
1727
1927
  }
1728
- return new PixelTile(
1729
- id,
1730
- tx,
1731
- ty,
1732
- this.config.tileArea
1733
- );
1928
+ return new PixelTile(id, tx, ty, this.config.tileArea);
1734
1929
  }
1735
1930
  recyclePatch(patch) {
1736
1931
  const before = patch.beforeTiles;
@@ -1761,11 +1956,7 @@ var PixelAccumulator = class {
1761
1956
  let id = ty * columns + tx;
1762
1957
  let tile = this.lookup[id];
1763
1958
  if (!tile) {
1764
- tile = this.getTile(
1765
- id,
1766
- tx,
1767
- ty
1768
- );
1959
+ tile = this.getTile(id, tx, ty);
1769
1960
  this.extractState(tile);
1770
1961
  this.lookup[id] = tile;
1771
1962
  this.beforeTiles.push(tile);
@@ -1791,11 +1982,7 @@ var PixelAccumulator = class {
1791
1982
  let id = ty * columns + tx;
1792
1983
  let tile = this.lookup[id];
1793
1984
  if (!tile) {
1794
- tile = this.getTile(
1795
- id,
1796
- tx,
1797
- ty
1798
- );
1985
+ tile = this.getTile(id, tx, ty);
1799
1986
  this.extractState(tile);
1800
1987
  this.lookup[id] = tile;
1801
1988
  this.beforeTiles.push(tile);
@@ -1834,11 +2021,7 @@ var PixelAccumulator = class {
1834
2021
  for (let i = 0; i < length; i++) {
1835
2022
  let beforeTile = this.beforeTiles[i];
1836
2023
  if (beforeTile) {
1837
- let afterTile = this.getTile(
1838
- beforeTile.id,
1839
- beforeTile.tx,
1840
- beforeTile.ty
1841
- );
2024
+ let afterTile = this.getTile(beforeTile.id, beforeTile.tx, beforeTile.ty);
1842
2025
  this.extractState(afterTile);
1843
2026
  afterTiles.push(afterTile);
1844
2027
  }
@@ -1851,64 +2034,6 @@ var PixelAccumulator = class {
1851
2034
  }
1852
2035
  };
1853
2036
 
1854
- // src/History/HistoryManager.ts
1855
- var HistoryManager = class {
1856
- constructor(maxSteps = 50) {
1857
- this.maxSteps = maxSteps;
1858
- this.undoStack = [];
1859
- this.redoStack = [];
1860
- this.listeners = /* @__PURE__ */ new Set();
1861
- }
1862
- undoStack;
1863
- redoStack;
1864
- listeners;
1865
- get canUndo() {
1866
- return this.undoStack.length > 0;
1867
- }
1868
- get canRedo() {
1869
- return this.redoStack.length > 0;
1870
- }
1871
- subscribe(fn) {
1872
- this.listeners.add(fn);
1873
- return () => this.listeners.delete(fn);
1874
- }
1875
- notify() {
1876
- this.listeners.forEach((fn) => fn());
1877
- }
1878
- commit(action) {
1879
- this.undoStack.push(action);
1880
- this.clearRedoStack();
1881
- if (this.undoStack.length > this.maxSteps) {
1882
- this.undoStack.shift()?.dispose?.();
1883
- }
1884
- this.notify();
1885
- }
1886
- undo() {
1887
- let action = this.undoStack.pop();
1888
- if (!action) return;
1889
- this.redoStack.push(action);
1890
- action.undo();
1891
- this.notify();
1892
- }
1893
- redo() {
1894
- let action = this.redoStack.pop();
1895
- if (!action) return;
1896
- this.undoStack.push(action);
1897
- action.redo();
1898
- this.notify();
1899
- }
1900
- clearRedoStack() {
1901
- let length = this.redoStack.length;
1902
- for (let i = 0; i < length; i++) {
1903
- let action = this.redoStack[i];
1904
- if (action) {
1905
- action.dispose?.();
1906
- }
1907
- }
1908
- this.redoStack.length = 0;
1909
- }
1910
- };
1911
-
1912
2037
  // src/History/PixelEngineConfig.ts
1913
2038
  var PixelEngineConfig = class {
1914
2039
  tileSize;
@@ -1926,51 +2051,210 @@ var PixelEngineConfig = class {
1926
2051
  }
1927
2052
  };
1928
2053
 
1929
- // src/PixelData/applyCircleBrushToPixelData.ts
1930
- function applyCircleBrushToPixelData(target, color, centerX, centerY, brushSize, alpha = 255, fallOff, blendFn = sourceOverPerfect, bounds) {
1931
- const targetWidth = target.width;
1932
- const targetHeight = target.height;
1933
- const b = bounds ?? getCircleBrushBounds(
1934
- centerX,
1935
- centerY,
1936
- brushSize,
1937
- targetWidth,
1938
- targetHeight
1939
- );
1940
- if (b.w <= 0 || b.h <= 0) return;
1941
- const data32 = target.data32;
1942
- const r = brushSize / 2;
1943
- const rSqr = r * r;
1944
- const invR = 1 / r;
1945
- const centerOffset = brushSize % 2 === 0 ? 0.5 : 0;
1946
- const baseColor = color & 16777215;
1947
- const constantSrc = (alpha << 24 | baseColor) >>> 0;
1948
- const endX = b.x + b.w;
1949
- const endY = b.y + b.h;
1950
- const fCenterX = Math.floor(centerX);
1951
- const fCenterY = Math.floor(centerY);
1952
- for (let cy = b.y; cy < endY; cy++) {
1953
- const relY = cy - fCenterY + centerOffset;
1954
- const dySqr = relY * relY;
1955
- const rowOffset = cy * targetWidth;
1956
- for (let cx = b.x; cx < endX; cx++) {
1957
- const relX = cx - fCenterX + centerOffset;
1958
- const dSqr = relX * relX + dySqr;
1959
- if (dSqr <= rSqr) {
1960
- const idx = rowOffset + cx;
1961
- if (fallOff) {
1962
- const strength = fallOff(Math.sqrt(dSqr) * invR);
1963
- const fAlpha = alpha * strength & 255;
1964
- const src = (fAlpha << 24 | baseColor) >>> 0;
1965
- data32[idx] = blendFn(src, data32[idx]);
1966
- } else {
1967
- data32[idx] = blendFn(constantSrc, data32[idx]);
2054
+ // src/PixelData/applyAlphaMaskToPixelData.ts
2055
+ function applyAlphaMaskToPixelData(dst, mask, opts = {}) {
2056
+ const {
2057
+ x: targetX = 0,
2058
+ y: targetY = 0,
2059
+ w: width = dst.width,
2060
+ h: height = dst.height,
2061
+ alpha: globalAlpha = 255,
2062
+ mw,
2063
+ mx = 0,
2064
+ my = 0,
2065
+ invertMask = false
2066
+ } = opts;
2067
+ if (globalAlpha === 0) return;
2068
+ let x = targetX;
2069
+ let y = targetY;
2070
+ let w = width;
2071
+ let h = height;
2072
+ if (x < 0) {
2073
+ w += x;
2074
+ x = 0;
2075
+ }
2076
+ if (y < 0) {
2077
+ h += y;
2078
+ y = 0;
2079
+ }
2080
+ w = Math.min(w, dst.width - x);
2081
+ h = Math.min(h, dst.height - y);
2082
+ if (w <= 0) return;
2083
+ if (h <= 0) return;
2084
+ const mPitch = mw ?? width;
2085
+ if (mPitch <= 0) return;
2086
+ const maskHeight = mask.length / mPitch | 0;
2087
+ const startX = mx + (x - targetX);
2088
+ const startY = my + (y - targetY);
2089
+ const sX0 = Math.max(0, startX);
2090
+ const sY0 = Math.max(0, startY);
2091
+ const sX1 = Math.min(mPitch, startX + w);
2092
+ const sY1 = Math.min(maskHeight, startY + h);
2093
+ const finalW = sX1 - sX0;
2094
+ const finalH = sY1 - sY0;
2095
+ if (finalW <= 0) return;
2096
+ if (finalH <= 0) return;
2097
+ const xShift = sX0 - startX;
2098
+ const yShift = sY0 - startY;
2099
+ const dst32 = dst.data32;
2100
+ const dw = dst.width;
2101
+ const dStride = dw - finalW;
2102
+ const mStride = mPitch - finalW;
2103
+ let dIdx = (y + yShift) * dw + (x + xShift);
2104
+ let mIdx = sY0 * mPitch + sX0;
2105
+ for (let iy = 0; iy < h; iy++) {
2106
+ for (let ix = 0; ix < w; ix++) {
2107
+ const mVal = mask[mIdx];
2108
+ const effectiveM = invertMask ? 255 - mVal : mVal;
2109
+ let weight = 0;
2110
+ if (effectiveM === 0) {
2111
+ weight = 0;
2112
+ } else if (effectiveM === 255) {
2113
+ weight = globalAlpha;
2114
+ } else if (globalAlpha === 255) {
2115
+ weight = effectiveM;
2116
+ } else {
2117
+ weight = effectiveM * globalAlpha + 128 >> 8;
2118
+ }
2119
+ if (weight === 0) {
2120
+ dst32[dIdx] = (dst32[dIdx] & 16777215) >>> 0;
2121
+ } else if (weight !== 255) {
2122
+ const d = dst32[dIdx];
2123
+ const da = d >>> 24;
2124
+ if (da !== 0) {
2125
+ const finalAlpha = da === 255 ? weight : da * weight + 128 >> 8;
2126
+ dst32[dIdx] = (d & 16777215 | finalAlpha << 24) >>> 0;
2127
+ }
2128
+ }
2129
+ dIdx++;
2130
+ mIdx++;
2131
+ }
2132
+ dIdx += dStride;
2133
+ mIdx += mStride;
2134
+ }
2135
+ }
2136
+
2137
+ // src/History/PixelMutator/mutatorApplyAlphaMask.ts
2138
+ var defaults = {
2139
+ applyAlphaMaskToPixelData
2140
+ };
2141
+ var mutatorApplyAlphaMask = ((writer, deps = defaults) => {
2142
+ const {
2143
+ applyAlphaMaskToPixelData: applyAlphaMaskToPixelData2 = defaults.applyAlphaMaskToPixelData
2144
+ } = deps;
2145
+ return {
2146
+ applyAlphaMask: (mask, opts = {}) => {
2147
+ let target = writer.target;
2148
+ const {
2149
+ x = 0,
2150
+ y = 0,
2151
+ w = writer.target.width,
2152
+ h = writer.target.height
2153
+ } = opts;
2154
+ writer.accumulator.storeRegionBeforeState(x, y, w, h);
2155
+ applyAlphaMaskToPixelData2(target, mask, opts);
2156
+ }
2157
+ };
2158
+ });
2159
+
2160
+ // src/PixelData/applyBinaryMaskToPixelData.ts
2161
+ function applyBinaryMaskToPixelData(dst, mask, opts = {}) {
2162
+ const {
2163
+ x: targetX = 0,
2164
+ y: targetY = 0,
2165
+ w: width = dst.width,
2166
+ h: height = dst.height,
2167
+ alpha = 255,
2168
+ mw,
2169
+ mx = 0,
2170
+ my = 0,
2171
+ invertMask = false
2172
+ } = opts;
2173
+ if (alpha === 0) return;
2174
+ let x = targetX;
2175
+ let y = targetY;
2176
+ let w = width;
2177
+ let h = height;
2178
+ if (x < 0) {
2179
+ w += x;
2180
+ x = 0;
2181
+ }
2182
+ if (y < 0) {
2183
+ h += y;
2184
+ y = 0;
2185
+ }
2186
+ w = Math.min(w, dst.width - x);
2187
+ h = Math.min(h, dst.height - y);
2188
+ if (w <= 0) return;
2189
+ if (h <= 0) return;
2190
+ const mPitch = mw ?? width;
2191
+ if (mPitch <= 0) return;
2192
+ const maskHeight = mask.length / mPitch | 0;
2193
+ const startX = mx + (x - targetX);
2194
+ const startY = my + (y - targetY);
2195
+ const sX0 = Math.max(0, startX);
2196
+ const sY0 = Math.max(0, startY);
2197
+ const sX1 = Math.min(mPitch, startX + w);
2198
+ const sY1 = Math.min(maskHeight, startY + h);
2199
+ const finalW = sX1 - sX0;
2200
+ const finalH = sY1 - sY0;
2201
+ if (finalW <= 0) return;
2202
+ if (finalH <= 0) return;
2203
+ const xShift = sX0 - startX;
2204
+ const yShift = sY0 - startY;
2205
+ const dst32 = dst.data32;
2206
+ const dw = dst.width;
2207
+ const dStride = dw - finalW;
2208
+ const mStride = mPitch - finalW;
2209
+ let dIdx = (y + yShift) * dw + (x + xShift);
2210
+ let mIdx = sY0 * mPitch + sX0;
2211
+ for (let iy = 0; iy < h; iy++) {
2212
+ for (let ix = 0; ix < w; ix++) {
2213
+ const mVal = mask[mIdx];
2214
+ const isMaskedOut = invertMask ? mVal !== 0 : mVal === 0;
2215
+ if (isMaskedOut) {
2216
+ dst32[dIdx] = (dst32[dIdx] & 16777215) >>> 0;
2217
+ } else if (alpha !== 255) {
2218
+ const d = dst32[dIdx];
2219
+ const da = d >>> 24;
2220
+ if (da !== 0) {
2221
+ const finalAlpha = da === 255 ? alpha : da * alpha + 128 >> 8;
2222
+ dst32[dIdx] = (d & 16777215 | finalAlpha << 24) >>> 0;
1968
2223
  }
1969
2224
  }
2225
+ dIdx++;
2226
+ mIdx++;
1970
2227
  }
2228
+ dIdx += dStride;
2229
+ mIdx += mStride;
1971
2230
  }
1972
2231
  }
1973
- function getCircleBrushBounds(centerX, centerY, brushSize, targetWidth, targetHeight, out) {
2232
+
2233
+ // src/History/PixelMutator/mutatorApplyBinaryMask.ts
2234
+ var defaults2 = {
2235
+ applyBinaryMaskToPixelData
2236
+ };
2237
+ var mutatorApplyBinaryMask = ((writer, deps = defaults2) => {
2238
+ const {
2239
+ applyBinaryMaskToPixelData: applyBinaryMaskToPixelData2 = defaults2.applyBinaryMaskToPixelData
2240
+ } = deps;
2241
+ return {
2242
+ applyBinaryMask: (mask, opts = {}) => {
2243
+ let target = writer.target;
2244
+ const {
2245
+ x = 0,
2246
+ y = 0,
2247
+ w = writer.target.width,
2248
+ h = writer.target.height
2249
+ } = opts;
2250
+ writer.accumulator.storeRegionBeforeState(x, y, w, h);
2251
+ applyBinaryMaskToPixelData2(target, mask, opts);
2252
+ }
2253
+ };
2254
+ });
2255
+
2256
+ // src/Rect/getCircleBrushOrPencilBounds.ts
2257
+ function getCircleBrushOrPencilBounds(centerX, centerY, brushSize, targetWidth, targetHeight, out) {
1974
2258
  const r = brushSize / 2;
1975
2259
  const minOffset = -Math.ceil(r - 0.5);
1976
2260
  const maxOffset = Math.floor(r - 0.5);
@@ -1984,104 +2268,137 @@ function getCircleBrushBounds(centerX, centerY, brushSize, targetWidth, targetHe
1984
2268
  w: 0,
1985
2269
  h: 0
1986
2270
  };
1987
- const cStartX = targetWidth !== void 0 ? Math.max(0, startX) : startX;
1988
- const cStartY = targetHeight !== void 0 ? Math.max(0, startY) : startY;
1989
- const cEndX = targetWidth !== void 0 ? Math.min(targetWidth, endX) : endX;
1990
- const cEndY = targetHeight !== void 0 ? Math.min(targetHeight, endY) : endY;
2271
+ const cStartX = Math.max(0, startX);
2272
+ const cStartY = Math.max(0, startY);
2273
+ const cEndX = Math.min(targetWidth, endX);
2274
+ const cEndY = Math.min(targetHeight, endY);
2275
+ const w = cEndX - cStartX;
2276
+ const h = cEndY - cStartY;
1991
2277
  res.x = cStartX;
1992
2278
  res.y = cStartY;
1993
- res.w = Math.max(0, cEndX - cStartX);
1994
- res.h = Math.max(0, cEndY - cStartY);
2279
+ res.w = w < 0 ? 0 : w;
2280
+ res.h = h < 0 ? 0 : h;
1995
2281
  return res;
1996
2282
  }
1997
2283
 
1998
- // src/History/PixelWriter.ts
1999
- var PixelWriter = class {
2000
- target;
2001
- historyManager;
2002
- accumulator;
2003
- config;
2004
- mutator;
2005
- constructor(target, mutatorFactory, {
2006
- tileSize = 256,
2007
- maxHistorySteps = 50,
2008
- historyManager = new HistoryManager(maxHistorySteps)
2009
- } = {}) {
2010
- this.target = target;
2011
- this.config = new PixelEngineConfig(tileSize);
2012
- this.historyManager = historyManager;
2013
- this.accumulator = new PixelAccumulator(target, this.config);
2014
- this.mutator = mutatorFactory(this);
2015
- }
2016
- withHistory(cb) {
2017
- cb(this.mutator);
2018
- this.captureHistory();
2019
- }
2020
- captureHistory() {
2021
- const beforeTiles = this.accumulator.beforeTiles;
2022
- if (beforeTiles.length === 0) return;
2023
- const afterTiles = this.accumulator.extractAfterTiles();
2024
- const patch = {
2025
- beforeTiles,
2026
- afterTiles
2027
- };
2028
- const target = this.target;
2029
- const tileSize = this.config.tileSize;
2030
- const accumulator = this.accumulator;
2031
- const action = {
2032
- undo: () => applyPatchTiles(target, patch.beforeTiles, tileSize),
2033
- redo: () => applyPatchTiles(target, patch.afterTiles, tileSize),
2034
- dispose: () => accumulator.recyclePatch(patch)
2035
- };
2036
- this.historyManager.commit(action);
2037
- this.accumulator.reset();
2284
+ // src/PixelData/applyCircleBrushToPixelData.ts
2285
+ function applyCircleBrushToPixelData(target, color, centerX, centerY, brushSize, alpha = 255, fallOff, blendFn = sourceOverPerfect, bounds) {
2286
+ const targetWidth = target.width;
2287
+ const targetHeight = target.height;
2288
+ const b = bounds ?? getCircleBrushOrPencilBounds(centerX, centerY, brushSize, targetWidth, targetHeight);
2289
+ if (b.w <= 0 || b.h <= 0) return;
2290
+ const data32 = target.data32;
2291
+ const r = brushSize / 2;
2292
+ const rSqr = r * r;
2293
+ const invR = 1 / r;
2294
+ const centerOffset = brushSize % 2 === 0 ? 0.5 : 0;
2295
+ const endX = b.x + b.w;
2296
+ const endY = b.y + b.h;
2297
+ const fCenterX = Math.floor(centerX);
2298
+ const fCenterY = Math.floor(centerY);
2299
+ const baseSrcAlpha = color >>> 24;
2300
+ const colorRGB = color & 16777215;
2301
+ const isOpaque = alpha === 255;
2302
+ const isOverwrite = blendFn.isOverwrite;
2303
+ for (let cy = b.y; cy < endY; cy++) {
2304
+ const relY = cy - fCenterY + centerOffset;
2305
+ const dySqr = relY * relY;
2306
+ const rowOffset = cy * targetWidth;
2307
+ for (let cx = b.x; cx < endX; cx++) {
2308
+ const relX = cx - fCenterX + centerOffset;
2309
+ const dSqr = relX * relX + dySqr;
2310
+ if (dSqr <= rSqr) {
2311
+ const idx = rowOffset + cx;
2312
+ let weight = alpha;
2313
+ const strength = fallOff(1 - Math.sqrt(dSqr) * invR);
2314
+ const maskVal = strength * 255 | 0;
2315
+ if (maskVal === 0) continue;
2316
+ if (isOpaque) {
2317
+ weight = maskVal;
2318
+ } else if (maskVal !== 255) {
2319
+ weight = maskVal * alpha + 128 >> 8;
2320
+ }
2321
+ let finalCol = color;
2322
+ if (weight < 255) {
2323
+ const a = baseSrcAlpha * weight + 128 >> 8;
2324
+ if (a === 0 && !isOverwrite) continue;
2325
+ finalCol = (colorRGB | a << 24) >>> 0;
2326
+ }
2327
+ data32[idx] = blendFn(finalCol, data32[idx]);
2328
+ }
2329
+ }
2038
2330
  }
2039
- };
2331
+ }
2040
2332
 
2041
2333
  // src/History/PixelMutator/mutatorApplyCircleBrush.ts
2042
- var boundsOut = { x: 0, y: 0, w: 0, h: 0 };
2043
- function mutatorApplyCircleBrush(writer) {
2334
+ var defaults3 = {
2335
+ applyCircleBrushToPixelData,
2336
+ getCircleBrushOrPencilBounds
2337
+ };
2338
+ var mutatorApplyCircleBrush = ((writer, deps = defaults3) => {
2339
+ const {
2340
+ applyCircleBrushToPixelData: applyCircleBrushToPixelData2 = defaults3.applyCircleBrushToPixelData,
2341
+ getCircleBrushOrPencilBounds: getCircleBrushOrPencilBounds2 = defaults3.getCircleBrushOrPencilBounds
2342
+ } = deps;
2343
+ const boundsOut = {
2344
+ x: 0,
2345
+ y: 0,
2346
+ w: 0,
2347
+ h: 0
2348
+ };
2044
2349
  return {
2045
2350
  applyCircleBrush(color, centerX, centerY, brushSize, alpha = 255, fallOff, blendFn) {
2046
- const circleBounds = getCircleBrushBounds(
2047
- centerX,
2048
- centerY,
2049
- brushSize,
2050
- writer.target.width,
2051
- writer.target.height,
2052
- boundsOut
2053
- );
2054
- const { x, y, w, h } = circleBounds;
2351
+ const bounds = getCircleBrushOrPencilBounds2(centerX, centerY, brushSize, writer.target.width, writer.target.height, boundsOut);
2352
+ const {
2353
+ x,
2354
+ y,
2355
+ w,
2356
+ h
2357
+ } = bounds;
2055
2358
  writer.accumulator.storeRegionBeforeState(x, y, w, h);
2056
- applyCircleBrushToPixelData(
2057
- writer.target,
2058
- color,
2059
- centerX,
2060
- centerY,
2061
- brushSize,
2062
- alpha,
2063
- fallOff,
2064
- blendFn,
2065
- circleBounds
2066
- );
2359
+ applyCircleBrushToPixelData2(writer.target, color, centerX, centerY, brushSize, alpha, fallOff, blendFn, bounds);
2067
2360
  }
2068
2361
  };
2362
+ });
2363
+
2364
+ // src/Algorithm/forEachLinePoint.ts
2365
+ function forEachLinePoint(x0, y0, x1, y1, callback) {
2366
+ const dx = x1 - x0;
2367
+ const dy = y1 - y0;
2368
+ const steps = Math.max(Math.abs(dx), Math.abs(dy));
2369
+ if (steps === 0) {
2370
+ callback(x0, y0);
2371
+ return;
2372
+ }
2373
+ const xInc = dx / steps;
2374
+ const yInc = dy / steps;
2375
+ let curX = x0;
2376
+ let curY = y0;
2377
+ for (let i = 0; i <= steps; i++) {
2378
+ callback(curX, curY);
2379
+ curX += xInc;
2380
+ curY += yInc;
2381
+ }
2069
2382
  }
2070
2383
 
2071
- // src/PixelData/applyMaskToPixelData.ts
2072
- function applyMaskToPixelData(dst, mask, opts = {}) {
2384
+ // src/PixelData/blendColorPixelDataAlphaMask.ts
2385
+ function blendColorPixelDataAlphaMask(dst, color, mask, opts) {
2073
2386
  const {
2074
2387
  x: targetX = 0,
2075
2388
  y: targetY = 0,
2076
2389
  w: width = dst.width,
2077
2390
  h: height = dst.height,
2078
2391
  alpha: globalAlpha = 255,
2079
- maskType = 0 /* ALPHA */,
2080
- mw,
2392
+ blendFn = sourceOverPerfect,
2393
+ mw = width,
2081
2394
  mx = 0,
2082
2395
  my = 0,
2083
2396
  invertMask = false
2084
2397
  } = opts;
2398
+ if (globalAlpha === 0 || !mask) return;
2399
+ const baseSrcAlpha = color >>> 24;
2400
+ const isOverwrite = blendFn.isOverwrite || false;
2401
+ if (baseSrcAlpha === 0 && !isOverwrite) return;
2085
2402
  let x = targetX;
2086
2403
  let y = targetY;
2087
2404
  let w = width;
@@ -2096,57 +2413,49 @@ function applyMaskToPixelData(dst, mask, opts = {}) {
2096
2413
  }
2097
2414
  const actualW = Math.min(w, dst.width - x);
2098
2415
  const actualH = Math.min(h, dst.height - y);
2099
- if (actualW <= 0 || actualH <= 0 || globalAlpha === 0) {
2100
- return;
2101
- }
2416
+ if (actualW <= 0 || actualH <= 0) return;
2417
+ const dx = x - targetX | 0;
2418
+ const dy = y - targetY | 0;
2102
2419
  const dst32 = dst.data32;
2103
2420
  const dw = dst.width;
2104
- const mPitch = mw ?? width;
2105
- const isAlpha = maskType === 0 /* ALPHA */;
2106
- const dx = x - targetX;
2107
- const dy = y - targetY;
2108
- let dIdx = y * dw + x;
2109
- let mIdx = (my + dy) * mPitch + (mx + dx);
2110
- const dStride = dw - actualW;
2111
- const mStride = mPitch - actualW;
2421
+ const mPitch = mw;
2422
+ let dIdx = y * dw + x | 0;
2423
+ let mIdx = (my + dy) * mPitch + (mx + dx) | 0;
2424
+ const dStride = dw - actualW | 0;
2425
+ let mStride = mPitch - actualW | 0;
2426
+ const isOpaque = globalAlpha === 255;
2427
+ const colorRGB = color & 16777215;
2112
2428
  for (let iy = 0; iy < actualH; iy++) {
2113
2429
  for (let ix = 0; ix < actualW; ix++) {
2114
2430
  const mVal = mask[mIdx];
2431
+ const effM = invertMask ? 255 - mVal : mVal;
2432
+ if (effM === 0) {
2433
+ dIdx++;
2434
+ mIdx++;
2435
+ continue;
2436
+ }
2115
2437
  let weight = globalAlpha;
2116
- if (isAlpha) {
2117
- const effectiveM = invertMask ? 255 - mVal : mVal;
2118
- if (effectiveM === 0) {
2119
- dst32[dIdx] = (dst32[dIdx] & 16777215) >>> 0;
2120
- dIdx++;
2121
- mIdx++;
2122
- continue;
2123
- }
2124
- weight = globalAlpha === 255 ? effectiveM : effectiveM * globalAlpha + 128 >> 8;
2125
- } else {
2126
- const isHit = invertMask ? mVal === 0 : mVal === 1;
2127
- if (!isHit) {
2128
- dst32[dIdx] = (dst32[dIdx] & 16777215) >>> 0;
2438
+ if (isOpaque) {
2439
+ weight = effM;
2440
+ } else if (effM !== 255) {
2441
+ weight = effM * globalAlpha + 128 >> 8;
2442
+ }
2443
+ if (weight === 0) {
2444
+ dIdx++;
2445
+ mIdx++;
2446
+ continue;
2447
+ }
2448
+ let finalCol = color;
2449
+ if (weight < 255) {
2450
+ const a = baseSrcAlpha * weight + 128 >> 8;
2451
+ if (a === 0 && !isOverwrite) {
2129
2452
  dIdx++;
2130
2453
  mIdx++;
2131
2454
  continue;
2132
2455
  }
2133
- weight = globalAlpha;
2134
- }
2135
- if (weight === 0) {
2136
- dst32[dIdx] = (dst32[dIdx] & 16777215) >>> 0;
2137
- } else {
2138
- const d = dst32[dIdx];
2139
- const da = d >>> 24;
2140
- let finalAlpha = da;
2141
- if (da === 0) {
2142
- } else if (weight === 255) {
2143
- } else if (da === 255) {
2144
- finalAlpha = weight;
2145
- } else {
2146
- finalAlpha = da * weight + 128 >> 8;
2147
- }
2148
- dst32[dIdx] = (d & 16777215 | finalAlpha << 24) >>> 0;
2456
+ finalCol = (colorRGB | a << 24) >>> 0;
2149
2457
  }
2458
+ dst32[dIdx] = blendFn(finalCol, dst32[dIdx]);
2150
2459
  dIdx++;
2151
2460
  mIdx++;
2152
2461
  }
@@ -2155,159 +2464,595 @@ function applyMaskToPixelData(dst, mask, opts = {}) {
2155
2464
  }
2156
2465
  }
2157
2466
 
2158
- // src/History/PixelMutator/mutatorApplyMask.ts
2159
- function mutatorApplyMask(writer) {
2467
+ // src/Rect/getCircleBrushOrPencilStrokeBounds.ts
2468
+ function getCircleBrushOrPencilStrokeBounds(x0, y0, x1, y1, brushSize, result) {
2469
+ const r = Math.ceil(brushSize / 2);
2470
+ const minX = Math.min(x0, x1) - r;
2471
+ const minY = Math.min(y0, y1) - r;
2472
+ const maxX = Math.max(x0, x1) + r;
2473
+ const maxY = Math.max(x0, y1) + r;
2474
+ result.x = Math.floor(minX);
2475
+ result.y = Math.floor(minY);
2476
+ result.w = Math.ceil(maxX - minX);
2477
+ result.h = Math.ceil(maxY - minY);
2478
+ return result;
2479
+ }
2480
+
2481
+ // src/History/PixelMutator/mutatorApplyCircleBrushStroke.ts
2482
+ var defaults4 = {
2483
+ forEachLinePoint,
2484
+ blendColorPixelDataAlphaMask,
2485
+ getCircleBrushOrPencilBounds,
2486
+ getCircleBrushOrPencilStrokeBounds
2487
+ };
2488
+ var mutatorApplyCircleBrushStroke = ((writer, deps = defaults4) => {
2489
+ const {
2490
+ forEachLinePoint: forEachLinePoint2 = defaults4.forEachLinePoint,
2491
+ blendColorPixelDataAlphaMask: blendColorPixelDataAlphaMask2 = defaults4.blendColorPixelDataAlphaMask,
2492
+ getCircleBrushOrPencilBounds: getCircleBrushOrPencilBounds2 = defaults4.getCircleBrushOrPencilBounds,
2493
+ getCircleBrushOrPencilStrokeBounds: getCircleBrushOrPencilStrokeBounds2 = defaults4.getCircleBrushOrPencilStrokeBounds
2494
+ } = deps;
2495
+ const strokeBoundsOut = {
2496
+ x: 0,
2497
+ y: 0,
2498
+ w: 0,
2499
+ h: 0
2500
+ };
2501
+ const circleBrushBounds = {
2502
+ x: 0,
2503
+ y: 0,
2504
+ w: 0,
2505
+ h: 0
2506
+ };
2507
+ const blendColorPixelOptions = {
2508
+ alpha: 255,
2509
+ blendFn: sourceOverPerfect,
2510
+ x: 0,
2511
+ y: 0,
2512
+ w: 0,
2513
+ h: 0
2514
+ };
2160
2515
  return {
2161
- applyMask: (mask, opts = {}) => {
2162
- let target = writer.target;
2516
+ applyCircleBrushStroke(color, x0, y0, x1, y1, brushSize, alpha = 255, fallOff, blendFn = sourceOverPerfect) {
2163
2517
  const {
2164
- x = 0,
2165
- y = 0,
2166
- w = writer.target.width,
2167
- h = writer.target.height
2168
- } = opts;
2169
- writer.accumulator.storeRegionBeforeState(x, y, w, h);
2170
- applyMaskToPixelData(target, mask, opts);
2518
+ x: bx,
2519
+ y: by,
2520
+ w: bw,
2521
+ h: bh
2522
+ } = getCircleBrushOrPencilStrokeBounds2(x0, y0, x1, y1, brushSize, strokeBoundsOut);
2523
+ if (bw <= 0 || bh <= 0) return;
2524
+ const mask = new Uint8Array(bw * bh);
2525
+ const r = brushSize / 2;
2526
+ const rSqr = r * r;
2527
+ const invR = 1 / r;
2528
+ const centerOffset = brushSize % 2 === 0 ? 0.5 : 0;
2529
+ const targetWidth = writer.target.width;
2530
+ const targetHeight = writer.target.height;
2531
+ forEachLinePoint2(x0, y0, x1, y1, (px, py) => {
2532
+ const {
2533
+ x: cbx,
2534
+ y: cby,
2535
+ w: cbw,
2536
+ h: cbh
2537
+ } = getCircleBrushOrPencilBounds2(px, py, brushSize, targetWidth, targetHeight, circleBrushBounds);
2538
+ writer.accumulator.storeRegionBeforeState(cbx, cby, cbw, cbh);
2539
+ const startX = Math.max(bx, cbx);
2540
+ const startY = Math.max(by, cby);
2541
+ const endX = Math.min(bx + bw, cbx + cbw);
2542
+ const endY = Math.min(by + bh, cby + cbh);
2543
+ const fPx = Math.floor(px);
2544
+ const fPy = Math.floor(py);
2545
+ for (let my = startY; my < endY; my++) {
2546
+ const dy = my - fPy + centerOffset;
2547
+ const dySqr = dy * dy;
2548
+ const maskRowOffset = (my - by) * bw;
2549
+ for (let mx = startX; mx < endX; mx++) {
2550
+ const dx = mx - fPx + centerOffset;
2551
+ const dSqr = dx * dx + dySqr;
2552
+ if (dSqr <= rSqr) {
2553
+ const maskIdx = maskRowOffset + (mx - bx);
2554
+ const dist = Math.sqrt(dSqr) * invR;
2555
+ const intensity = fallOff(1 - dist) * 255 | 0;
2556
+ if (intensity > mask[maskIdx]) {
2557
+ mask[maskIdx] = intensity;
2558
+ }
2559
+ }
2560
+ }
2561
+ }
2562
+ });
2563
+ blendColorPixelOptions.blendFn = blendFn;
2564
+ blendColorPixelOptions.alpha = alpha;
2565
+ blendColorPixelOptions.x = bx;
2566
+ blendColorPixelOptions.y = by;
2567
+ blendColorPixelOptions.w = bw;
2568
+ blendColorPixelOptions.h = bh;
2569
+ blendColorPixelDataAlphaMask2(writer.target, color, mask, blendColorPixelOptions);
2171
2570
  }
2172
2571
  };
2173
- }
2174
-
2175
- // src/ImageData/imageDataToUInt32Array.ts
2176
- function imageDataToUInt32Array(imageData) {
2177
- return new Uint32Array(
2178
- imageData.data.buffer,
2179
- imageData.data.byteOffset,
2180
- // Shift right by 2 is a fast bitwise division by 4.
2181
- imageData.data.byteLength >> 2
2182
- );
2183
- }
2572
+ });
2184
2573
 
2185
- // src/PixelData/PixelData.ts
2186
- var PixelData = class _PixelData {
2187
- data32;
2188
- imageData;
2189
- get width() {
2190
- return this.imageData.width;
2191
- }
2192
- get height() {
2193
- return this.imageData.height;
2574
+ // src/PixelData/blendColorPixelDataBinaryMask.ts
2575
+ function blendColorPixelDataBinaryMask(dst, color, mask, opts = {}) {
2576
+ const {
2577
+ x: targetX = 0,
2578
+ y: targetY = 0,
2579
+ w: width = dst.width,
2580
+ h: height = dst.height,
2581
+ alpha: globalAlpha = 255,
2582
+ blendFn = sourceOverPerfect,
2583
+ mw = width,
2584
+ mx = 0,
2585
+ my = 0,
2586
+ invertMask = false
2587
+ } = opts;
2588
+ if (globalAlpha === 0 || !mask) return;
2589
+ const baseSrcAlpha = color >>> 24;
2590
+ const isOverwrite = blendFn.isOverwrite || false;
2591
+ if (baseSrcAlpha === 0 && !isOverwrite) return;
2592
+ let x = targetX;
2593
+ let y = targetY;
2594
+ let w = width;
2595
+ let h = height;
2596
+ if (x < 0) {
2597
+ w += x;
2598
+ x = 0;
2194
2599
  }
2195
- constructor(imageData) {
2196
- this.data32 = imageDataToUInt32Array(imageData);
2197
- this.imageData = imageData;
2600
+ if (y < 0) {
2601
+ h += y;
2602
+ y = 0;
2198
2603
  }
2199
- set(imageData) {
2200
- this.imageData = imageData;
2201
- this.data32 = imageDataToUInt32Array(imageData);
2604
+ const actualW = Math.min(w, dst.width - x);
2605
+ const actualH = Math.min(h, dst.height - y);
2606
+ if (actualW <= 0 || actualH <= 0) return;
2607
+ let baseColorWithGlobalAlpha = color;
2608
+ if (globalAlpha < 255) {
2609
+ const a = baseSrcAlpha * globalAlpha + 128 >> 8;
2610
+ if (a === 0 && !isOverwrite) return;
2611
+ baseColorWithGlobalAlpha = (color & 16777215 | a << 24) >>> 0;
2202
2612
  }
2203
- /**
2204
- * Creates a deep copy of the PixelData using the environment's ImageData constructor.
2205
- */
2206
- copy() {
2207
- const buffer = new Uint8ClampedArray(this.imageData.data);
2208
- const ImageConstructor = typeof ImageData !== "undefined" ? ImageData : this.imageData.constructor;
2209
- const newImageData = new ImageConstructor(
2210
- buffer,
2211
- this.width,
2212
- this.height
2213
- );
2214
- return new _PixelData(newImageData);
2613
+ const dx = x - targetX | 0;
2614
+ const dy = y - targetY | 0;
2615
+ const dst32 = dst.data32;
2616
+ const dw = dst.width;
2617
+ const mPitch = mw;
2618
+ let dIdx = y * dw + x | 0;
2619
+ let mIdx = (my + dy) * mPitch + (mx + dx) | 0;
2620
+ const dStride = dw - actualW | 0;
2621
+ const mStride = mPitch - actualW | 0;
2622
+ const skipVal = invertMask ? 1 : 0;
2623
+ for (let iy = 0; iy < actualH; iy++) {
2624
+ for (let ix = 0; ix < actualW; ix++) {
2625
+ if (mask[mIdx] === skipVal) {
2626
+ dIdx++;
2627
+ mIdx++;
2628
+ continue;
2629
+ }
2630
+ dst32[dIdx] = blendFn(baseColorWithGlobalAlpha, dst32[dIdx]);
2631
+ dIdx++;
2632
+ mIdx++;
2633
+ }
2634
+ dIdx += dStride;
2635
+ mIdx += mStride;
2215
2636
  }
2637
+ }
2638
+
2639
+ // src/History/PixelMutator/mutatorApplyCirclePencilStroke.ts
2640
+ var defaults5 = {
2641
+ forEachLinePoint,
2642
+ blendColorPixelDataBinaryMask,
2643
+ getCircleBrushOrPencilBounds,
2644
+ getCircleBrushOrPencilStrokeBounds
2216
2645
  };
2646
+ var mutatorApplyCirclePencilStroke = ((writer, deps = defaults5) => {
2647
+ const {
2648
+ forEachLinePoint: forEachLinePoint2 = defaults5.forEachLinePoint,
2649
+ blendColorPixelDataBinaryMask: blendColorPixelDataBinaryMask2 = defaults5.blendColorPixelDataBinaryMask,
2650
+ getCircleBrushOrPencilStrokeBounds: getCircleBrushOrPencilStrokeBounds2 = defaults5.getCircleBrushOrPencilStrokeBounds,
2651
+ getCircleBrushOrPencilBounds: getCircleBrushOrPencilBounds2 = defaults5.getCircleBrushOrPencilBounds
2652
+ } = deps;
2653
+ const strokeBoundsOut = {
2654
+ x: 0,
2655
+ y: 0,
2656
+ w: 0,
2657
+ h: 0
2658
+ };
2659
+ const circlePencilBounds = {
2660
+ x: 0,
2661
+ y: 0,
2662
+ w: 0,
2663
+ h: 0
2664
+ };
2665
+ const blendColorPixelOptions = {
2666
+ alpha: 255,
2667
+ blendFn: sourceOverPerfect,
2668
+ x: 0,
2669
+ y: 0,
2670
+ w: 0,
2671
+ h: 0
2672
+ };
2673
+ return {
2674
+ applyCirclePencilStroke(color, x0, y0, x1, y1, brushSize, alpha = 255, blendFn = sourceOverPerfect) {
2675
+ const {
2676
+ x: bx,
2677
+ y: by,
2678
+ w: bw,
2679
+ h: bh
2680
+ } = getCircleBrushOrPencilStrokeBounds2(x0, y0, x1, y1, brushSize, strokeBoundsOut);
2681
+ if (bw <= 0 || bh <= 0) return;
2682
+ const mask = new Uint8Array(bw * bh);
2683
+ const r = brushSize / 2;
2684
+ const rSqr = r * r;
2685
+ const centerOffset = brushSize % 2 === 0 ? 0.5 : 0;
2686
+ const targetWidth = writer.target.width;
2687
+ const targetHeight = writer.target.height;
2688
+ forEachLinePoint2(x0, y0, x1, y1, (px, py) => {
2689
+ const {
2690
+ x: cbx,
2691
+ y: cby,
2692
+ w: cbw,
2693
+ h: cbh
2694
+ } = getCircleBrushOrPencilBounds2(px, py, brushSize, targetWidth, targetHeight, circlePencilBounds);
2695
+ writer.accumulator.storeRegionBeforeState(cbx, cby, cbw, cbh);
2696
+ const startX = Math.max(bx, cbx);
2697
+ const startY = Math.max(by, cby);
2698
+ const endX = Math.min(bx + bw, cbx + cbw);
2699
+ const endY = Math.min(by + bh, cby + cbh);
2700
+ const fPx = Math.floor(px);
2701
+ const fPy = Math.floor(py);
2702
+ for (let my = startY; my < endY; my++) {
2703
+ const dy = my - fPy + centerOffset;
2704
+ const dySqr = dy * dy;
2705
+ const maskRowOffset = (my - by) * bw;
2706
+ for (let mx = startX; mx < endX; mx++) {
2707
+ const dx = mx - fPx + centerOffset;
2708
+ const dSqr = dx * dx + dySqr;
2709
+ if (dSqr <= rSqr) {
2710
+ const maskIdx = maskRowOffset + (mx - bx);
2711
+ mask[maskIdx] = 1;
2712
+ }
2713
+ }
2714
+ }
2715
+ });
2716
+ blendColorPixelOptions.blendFn = blendFn;
2717
+ blendColorPixelOptions.alpha = alpha;
2718
+ blendColorPixelOptions.x = bx;
2719
+ blendColorPixelOptions.y = by;
2720
+ blendColorPixelOptions.w = bw;
2721
+ blendColorPixelOptions.h = bh;
2722
+ blendColorPixelDataBinaryMask2(writer.target, color, mask, blendColorPixelOptions);
2723
+ }
2724
+ };
2725
+ });
2726
+
2727
+ // src/Rect/getRectBrushOrPencilBounds.ts
2728
+ function getRectBrushOrPencilBounds(centerX, centerY, brushWidth, brushHeight, targetWidth, targetHeight, out) {
2729
+ const startX = Math.floor(centerX - brushWidth / 2);
2730
+ const startY = Math.floor(centerY - brushHeight / 2);
2731
+ const endX = startX + brushWidth;
2732
+ const endY = startY + brushHeight;
2733
+ const res = out ?? {
2734
+ x: 0,
2735
+ y: 0,
2736
+ w: 0,
2737
+ h: 0
2738
+ };
2739
+ const cStartX = Math.max(0, startX);
2740
+ const cStartY = Math.max(0, startY);
2741
+ const cEndX = Math.min(targetWidth, endX);
2742
+ const cEndY = Math.min(targetHeight, endY);
2743
+ const w = cEndX - cStartX;
2744
+ const h = cEndY - cStartY;
2745
+ res.x = cStartX;
2746
+ res.y = cStartY;
2747
+ res.w = w < 0 ? 0 : w;
2748
+ res.h = h < 0 ? 0 : h;
2749
+ return res;
2750
+ }
2217
2751
 
2218
2752
  // src/PixelData/applyRectBrushToPixelData.ts
2219
2753
  function applyRectBrushToPixelData(target, color, centerX, centerY, brushWidth, brushHeight, alpha = 255, fallOff, blendFn = sourceOverPerfect, bounds) {
2220
2754
  const targetWidth = target.width;
2221
2755
  const targetHeight = target.height;
2222
- const b = bounds ?? getRectBrushBounds(
2223
- centerX,
2224
- centerY,
2225
- brushWidth,
2226
- brushHeight,
2227
- targetWidth,
2228
- targetHeight
2229
- );
2756
+ const b = bounds ?? getRectBrushOrPencilBounds(centerX, centerY, brushWidth, brushHeight, targetWidth, targetHeight);
2230
2757
  if (b.w <= 0 || b.h <= 0) return;
2231
2758
  const data32 = target.data32;
2232
2759
  const baseColor = color & 16777215;
2233
- const constantSrc = (alpha << 24 | baseColor) >>> 0;
2760
+ const baseSrcAlpha = color >>> 24;
2761
+ const isOpaque = alpha === 255;
2234
2762
  const invHalfW = 1 / (brushWidth / 2);
2235
2763
  const invHalfH = 1 / (brushHeight / 2);
2764
+ const centerOffsetX = brushWidth % 2 === 0 ? 0.5 : 0;
2765
+ const centerOffsetY = brushHeight % 2 === 0 ? 0.5 : 0;
2766
+ const fCenterX = Math.floor(centerX);
2767
+ const fCenterY = Math.floor(centerY);
2236
2768
  const endX = b.x + b.w;
2237
2769
  const endY = b.y + b.h;
2770
+ const isOverwrite = blendFn.isOverwrite;
2238
2771
  for (let py = b.y; py < endY; py++) {
2239
2772
  const rowOffset = py * targetWidth;
2240
- const dy = fallOff ? Math.abs(py + 0.5 - centerY) * invHalfH : 0;
2773
+ const dy = Math.abs(py - fCenterY + centerOffsetY) * invHalfH;
2241
2774
  for (let px = b.x; px < endX; px++) {
2242
2775
  const idx = rowOffset + px;
2243
- if (fallOff) {
2244
- const dx = Math.abs(px + 0.5 - centerX) * invHalfW;
2245
- const dist = dx > dy ? dx : dy;
2246
- const strength = fallOff(dist);
2247
- const fAlpha = alpha * strength | 0;
2248
- const src = (fAlpha << 24 | baseColor) >>> 0;
2249
- data32[idx] = blendFn(src, data32[idx]);
2250
- } else {
2251
- data32[idx] = blendFn(constantSrc, data32[idx]);
2776
+ const dx = Math.abs(px - fCenterX + centerOffsetX) * invHalfW;
2777
+ const dist = dx > dy ? dx : dy;
2778
+ const strength = fallOff(dist);
2779
+ const maskVal = strength * 255 | 0;
2780
+ if (maskVal <= 0) continue;
2781
+ let weight = alpha;
2782
+ if (isOpaque) {
2783
+ weight = maskVal;
2784
+ } else if (maskVal !== 255) {
2785
+ weight = maskVal * alpha + 128 >> 8;
2786
+ }
2787
+ let finalCol = color;
2788
+ if (weight < 255) {
2789
+ const a = baseSrcAlpha * weight + 128 >> 8;
2790
+ if (a === 0 && !isOverwrite) continue;
2791
+ finalCol = (a << 24 | baseColor) >>> 0;
2252
2792
  }
2793
+ data32[idx] = blendFn(finalCol, data32[idx]);
2253
2794
  }
2254
2795
  }
2255
2796
  }
2256
- function getRectBrushBounds(centerX, centerY, brushWidth, brushHeight, targetWidth, targetHeight, out) {
2257
- const startX = Math.floor(centerX - brushWidth / 2);
2258
- const startY = Math.floor(centerY - brushHeight / 2);
2259
- const endX = startX + brushWidth;
2260
- const endY = startY + brushHeight;
2261
- const res = out ?? {
2797
+
2798
+ // src/History/PixelMutator/mutatorApplyRectBrush.ts
2799
+ var defaults6 = {
2800
+ applyRectBrushToPixelData,
2801
+ getRectBrushOrPencilBounds
2802
+ };
2803
+ var mutatorApplyRectBrush = ((writer, deps = defaults6) => {
2804
+ const {
2805
+ applyRectBrushToPixelData: applyRectBrushToPixelData2 = defaults6.applyRectBrushToPixelData,
2806
+ getRectBrushOrPencilBounds: getRectBrushOrPencilBounds2 = defaults6.getRectBrushOrPencilBounds
2807
+ } = deps;
2808
+ const boundsOut = {
2809
+ x: 0,
2810
+ y: 0,
2811
+ w: 0,
2812
+ h: 0
2813
+ };
2814
+ return {
2815
+ applyRectBrush(color, centerX, centerY, brushWidth, brushHeight, alpha = 255, fallOff, blendFn) {
2816
+ const bounds = getRectBrushOrPencilBounds2(centerX, centerY, brushWidth, brushHeight, writer.target.width, writer.target.height, boundsOut);
2817
+ const {
2818
+ x,
2819
+ y,
2820
+ w,
2821
+ h
2822
+ } = bounds;
2823
+ writer.accumulator.storeRegionBeforeState(x, y, w, h);
2824
+ applyRectBrushToPixelData2(writer.target, color, centerX, centerY, brushWidth, brushHeight, alpha, fallOff, blendFn, bounds);
2825
+ }
2826
+ };
2827
+ });
2828
+
2829
+ // src/Rect/getRectBrushOrPencilStrokeBounds.ts
2830
+ function getRectBrushOrPencilStrokeBounds(x0, y0, x1, y1, brushWidth, brushHeight, result) {
2831
+ const halfW = brushWidth / 2;
2832
+ const halfH = brushHeight / 2;
2833
+ const minX = Math.min(x0, x1) - halfW;
2834
+ const minY = Math.min(y0, y1) - halfH;
2835
+ const maxX = Math.max(x0, x1) + halfW;
2836
+ const maxY = Math.max(y0, y1) + halfH;
2837
+ result.x = Math.floor(minX);
2838
+ result.y = Math.floor(minY);
2839
+ result.w = Math.ceil(maxX - minX);
2840
+ result.h = Math.ceil(maxY - minY);
2841
+ return result;
2842
+ }
2843
+
2844
+ // src/History/PixelMutator/mutatorApplyRectBrushStroke.ts
2845
+ var defaults7 = {
2846
+ forEachLinePoint,
2847
+ blendColorPixelDataAlphaMask,
2848
+ getRectBrushOrPencilBounds,
2849
+ getRectBrushOrPencilStrokeBounds
2850
+ };
2851
+ var mutatorApplyRectBrushStroke = ((writer, deps = defaults7) => {
2852
+ const {
2853
+ forEachLinePoint: forEachLinePoint2 = defaults7.forEachLinePoint,
2854
+ blendColorPixelDataAlphaMask: blendColorPixelDataAlphaMask2 = defaults7.blendColorPixelDataAlphaMask,
2855
+ getRectBrushOrPencilBounds: getRectBrushOrPencilBounds2 = defaults7.getRectBrushOrPencilBounds,
2856
+ getRectBrushOrPencilStrokeBounds: getRectBrushOrPencilStrokeBounds2 = defaults7.getRectBrushOrPencilStrokeBounds
2857
+ } = deps;
2858
+ const strokeBoundsOut = {
2859
+ x: 0,
2860
+ y: 0,
2861
+ w: 0,
2862
+ h: 0
2863
+ };
2864
+ const rectBrushBounds = {
2865
+ x: 0,
2866
+ y: 0,
2867
+ w: 0,
2868
+ h: 0
2869
+ };
2870
+ const blendColorPixelOptions = {
2871
+ alpha: 255,
2872
+ blendFn: sourceOverPerfect,
2873
+ x: 0,
2874
+ y: 0,
2875
+ w: 0,
2876
+ h: 0
2877
+ };
2878
+ return {
2879
+ applyRectBrushStroke(color, x0, y0, x1, y1, brushWidth, brushHeight, alpha = 255, fallOff, blendFn = sourceOverPerfect) {
2880
+ const {
2881
+ x: bx,
2882
+ y: by,
2883
+ w: bw,
2884
+ h: bh
2885
+ } = getRectBrushOrPencilStrokeBounds2(x0, y0, x1, y1, brushWidth, brushHeight, strokeBoundsOut);
2886
+ if (bw <= 0 || bh <= 0) return;
2887
+ const mask = new Uint8Array(bw * bh);
2888
+ const halfW = brushWidth / 2;
2889
+ const halfH = brushHeight / 2;
2890
+ const invHalfW = 1 / halfW;
2891
+ const invHalfH = 1 / halfH;
2892
+ const centerOffsetX = brushWidth % 2 === 0 ? 0.5 : 0;
2893
+ const centerOffsetY = brushHeight % 2 === 0 ? 0.5 : 0;
2894
+ const targetWidth = writer.target.width;
2895
+ const targetHeight = writer.target.height;
2896
+ forEachLinePoint2(x0, y0, x1, y1, (px, py) => {
2897
+ const {
2898
+ x: rbx,
2899
+ y: rby,
2900
+ w: rbw,
2901
+ h: rbh
2902
+ } = getRectBrushOrPencilBounds2(px, py, brushWidth, brushHeight, targetWidth, targetHeight, rectBrushBounds);
2903
+ writer.accumulator.storeRegionBeforeState(rbx, rby, rbw, rbh);
2904
+ const startX = Math.max(bx, rbx);
2905
+ const startY = Math.max(by, rby);
2906
+ const endX = Math.min(bx + bw, rbx + rbw);
2907
+ const endY = Math.min(by + bh, rby + rbh);
2908
+ const fPx = Math.floor(px);
2909
+ const fPy = Math.floor(py);
2910
+ for (let my = startY; my < endY; my++) {
2911
+ const dy = Math.abs(my - fPy + centerOffsetY) * invHalfH;
2912
+ const maskRowOffset = (my - by) * bw;
2913
+ for (let mx = startX; mx < endX; mx++) {
2914
+ const dx = Math.abs(mx - fPx + centerOffsetX) * invHalfW;
2915
+ const maskIdx = maskRowOffset + (mx - bx);
2916
+ const dist = dx > dy ? dx : dy;
2917
+ const strength = fallOff(dist);
2918
+ if (strength > 0) {
2919
+ const intensity = strength * 255 | 0;
2920
+ if (intensity > mask[maskIdx]) {
2921
+ mask[maskIdx] = intensity;
2922
+ }
2923
+ }
2924
+ }
2925
+ }
2926
+ });
2927
+ blendColorPixelOptions.blendFn = blendFn;
2928
+ blendColorPixelOptions.alpha = alpha;
2929
+ blendColorPixelOptions.x = bx;
2930
+ blendColorPixelOptions.y = by;
2931
+ blendColorPixelOptions.w = bw;
2932
+ blendColorPixelOptions.h = bh;
2933
+ blendColorPixelDataAlphaMask2(writer.target, color, mask, blendColorPixelOptions);
2934
+ }
2935
+ };
2936
+ });
2937
+
2938
+ // src/History/PixelMutator/mutatorApplyRectPencil.ts
2939
+ var defaults8 = {
2940
+ applyRectBrushToPixelData,
2941
+ getRectBrushOrPencilBounds,
2942
+ fallOff: () => 1
2943
+ };
2944
+ var mutatorApplyRectPencil = ((writer, deps = defaults8) => {
2945
+ const {
2946
+ applyRectBrushToPixelData: applyRectBrushToPixelData2 = defaults8.applyRectBrushToPixelData,
2947
+ getRectBrushOrPencilBounds: getRectBrushOrPencilBounds2 = defaults8.getRectBrushOrPencilBounds,
2948
+ fallOff = defaults8.fallOff
2949
+ } = deps;
2950
+ const boundsOut = {
2262
2951
  x: 0,
2263
2952
  y: 0,
2264
2953
  w: 0,
2265
2954
  h: 0
2266
2955
  };
2267
- const cStartX = targetWidth !== void 0 ? Math.max(0, startX) : startX;
2268
- const cStartY = targetHeight !== void 0 ? Math.max(0, startY) : startY;
2269
- const cEndX = targetWidth !== void 0 ? Math.min(targetWidth, endX) : endX;
2270
- const cEndY = targetHeight !== void 0 ? Math.min(targetHeight, endY) : endY;
2271
- const w = cEndX - cStartX;
2272
- const h = cEndY - cStartY;
2273
- res.x = cStartX;
2274
- res.y = cStartY;
2275
- res.w = w < 0 ? 0 : w;
2276
- res.h = h < 0 ? 0 : h;
2277
- return res;
2278
- }
2279
-
2280
- // src/History/PixelMutator/mutatorApplyRectBrush.ts
2281
- var boundsOut2 = { x: 0, y: 0, w: 0, h: 0 };
2282
- function mutatorApplyRectBrush(writer) {
2283
2956
  return {
2284
- applyRectBrush(color, centerX, centerY, brushWidth, brushHeight, alpha = 255, fallOff, blendFn) {
2285
- const bounds = getRectBrushBounds(
2286
- centerX,
2287
- centerY,
2288
- brushWidth,
2289
- brushHeight,
2290
- writer.target.width,
2291
- writer.target.height,
2292
- boundsOut2
2293
- );
2294
- const { x, y, w, h } = bounds;
2957
+ applyRectPencil(color, centerX, centerY, brushWidth, brushHeight, alpha = 255, blendFn) {
2958
+ const bounds = getRectBrushOrPencilBounds2(centerX, centerY, brushWidth, brushHeight, writer.target.width, writer.target.height, boundsOut);
2959
+ const {
2960
+ x,
2961
+ y,
2962
+ w,
2963
+ h
2964
+ } = bounds;
2295
2965
  writer.accumulator.storeRegionBeforeState(x, y, w, h);
2296
- applyRectBrushToPixelData(
2297
- writer.target,
2298
- color,
2299
- centerX,
2300
- centerY,
2301
- brushWidth,
2302
- brushHeight,
2303
- alpha,
2304
- fallOff,
2305
- blendFn,
2306
- bounds
2307
- );
2966
+ applyRectBrushToPixelData2(writer.target, color, centerX, centerY, brushWidth, brushHeight, alpha, fallOff, blendFn, bounds);
2308
2967
  }
2309
2968
  };
2310
- }
2969
+ });
2970
+
2971
+ // src/History/PixelMutator/mutatorApplyRectPencilStroke.ts
2972
+ var defaults9 = {
2973
+ forEachLinePoint,
2974
+ getRectBrushOrPencilBounds,
2975
+ getRectBrushOrPencilStrokeBounds,
2976
+ blendColorPixelDataBinaryMask
2977
+ };
2978
+ var mutatorApplyRectPencilStroke = ((writer, deps = defaults9) => {
2979
+ const {
2980
+ forEachLinePoint: forEachLinePoint2 = defaults9.forEachLinePoint,
2981
+ blendColorPixelDataBinaryMask: blendColorPixelDataBinaryMask2 = defaults9.blendColorPixelDataBinaryMask,
2982
+ getRectBrushOrPencilBounds: getRectBrushOrPencilBounds2 = defaults9.getRectBrushOrPencilBounds,
2983
+ getRectBrushOrPencilStrokeBounds: getRectBrushOrPencilStrokeBounds2 = defaults9.getRectBrushOrPencilStrokeBounds
2984
+ } = deps;
2985
+ const strokeBoundsOut = {
2986
+ x: 0,
2987
+ y: 0,
2988
+ w: 0,
2989
+ h: 0
2990
+ };
2991
+ const rectPencilBounds = {
2992
+ x: 0,
2993
+ y: 0,
2994
+ w: 0,
2995
+ h: 0
2996
+ };
2997
+ const blendColorPixelOptions = {
2998
+ alpha: 255,
2999
+ blendFn: sourceOverPerfect,
3000
+ x: 0,
3001
+ y: 0,
3002
+ w: 0,
3003
+ h: 0
3004
+ };
3005
+ return {
3006
+ applyRectPencilStroke(color, x0, y0, x1, y1, brushWidth, brushHeight, alpha = 255, blendFn = sourceOverPerfect) {
3007
+ const {
3008
+ x: bx,
3009
+ y: by,
3010
+ w: bw,
3011
+ h: bh
3012
+ } = getRectBrushOrPencilStrokeBounds2(x0, y0, x1, y1, brushWidth, brushHeight, strokeBoundsOut);
3013
+ if (bw <= 0 || bh <= 0) return;
3014
+ const mask = new Uint8Array(bw * bh);
3015
+ const halfW = brushWidth / 2;
3016
+ const halfH = brushHeight / 2;
3017
+ const centerOffset = brushWidth % 2 === 0 ? 0.5 : 0;
3018
+ const targetWidth = writer.target.width;
3019
+ const targetHeight = writer.target.height;
3020
+ forEachLinePoint2(x0, y0, x1, y1, (px, py) => {
3021
+ const {
3022
+ x: rbx,
3023
+ y: rby,
3024
+ w: rbw,
3025
+ h: rbh
3026
+ } = getRectBrushOrPencilBounds2(px, py, brushWidth, brushHeight, targetWidth, targetHeight, rectPencilBounds);
3027
+ writer.accumulator.storeRegionBeforeState(rbx, rby, rbw, rbh);
3028
+ const startX = Math.max(bx, rbx);
3029
+ const startY = Math.max(by, rby);
3030
+ const endX = Math.min(bx + bw, rbx + rbw);
3031
+ const endY = Math.min(by + bh, rby + rbh);
3032
+ const fPx = Math.floor(px);
3033
+ const fPy = Math.floor(py);
3034
+ for (let my = startY; my < endY; my++) {
3035
+ const dy = Math.abs(my - fPy + centerOffset);
3036
+ const maskRowOffset = (my - by) * bw;
3037
+ for (let mx = startX; mx < endX; mx++) {
3038
+ const dx = Math.abs(mx - fPx + centerOffset);
3039
+ const maskIdx = maskRowOffset + (mx - bx);
3040
+ if (dx <= halfW && dy <= halfH) {
3041
+ mask[maskIdx] = 1;
3042
+ }
3043
+ }
3044
+ }
3045
+ });
3046
+ blendColorPixelOptions.blendFn = blendFn;
3047
+ blendColorPixelOptions.alpha = alpha;
3048
+ blendColorPixelOptions.x = bx;
3049
+ blendColorPixelOptions.y = by;
3050
+ blendColorPixelOptions.w = bw;
3051
+ blendColorPixelOptions.h = bh;
3052
+ blendColorPixelDataBinaryMask2(writer.target, color, mask, blendColorPixelOptions);
3053
+ }
3054
+ };
3055
+ });
2311
3056
 
2312
3057
  // src/PixelData/blendColorPixelData.ts
2313
3058
  function blendColorPixelData(dst, color, opts = {}) {
@@ -2317,22 +3062,13 @@ function blendColorPixelData(dst, color, opts = {}) {
2317
3062
  w: width = dst.width,
2318
3063
  h: height = dst.height,
2319
3064
  alpha: globalAlpha = 255,
2320
- blendFn = sourceOverFast,
2321
- mask,
2322
- maskType = 0 /* ALPHA */,
2323
- mw,
2324
- mx = 0,
2325
- my = 0,
2326
- invertMask = false
3065
+ blendFn = sourceOverPerfect
2327
3066
  } = opts;
2328
3067
  if (globalAlpha === 0) return;
2329
3068
  const baseSrcAlpha = color >>> 24;
2330
- const isOverwrite = blendFn.isOverwrite;
3069
+ const isOverwrite = blendFn.isOverwrite || false;
2331
3070
  if (baseSrcAlpha === 0 && !isOverwrite) return;
2332
- let x = targetX;
2333
- let y = targetY;
2334
- let w = width;
2335
- let h = height;
3071
+ let x = targetX, y = targetY, w = width, h = height;
2336
3072
  if (x < 0) {
2337
3073
  w += x;
2338
3074
  x = 0;
@@ -2344,76 +3080,33 @@ function blendColorPixelData(dst, color, opts = {}) {
2344
3080
  const actualW = Math.min(w, dst.width - x);
2345
3081
  const actualH = Math.min(h, dst.height - y);
2346
3082
  if (actualW <= 0 || actualH <= 0) return;
3083
+ let finalSrcColor = color;
3084
+ if (globalAlpha < 255) {
3085
+ const a = baseSrcAlpha * globalAlpha + 128 >> 8;
3086
+ if (a === 0 && !isOverwrite) return;
3087
+ finalSrcColor = (color & 16777215 | a << 24) >>> 0;
3088
+ }
2347
3089
  const dst32 = dst.data32;
2348
3090
  const dw = dst.width;
2349
- const mPitch = mw ?? width;
2350
- const isAlphaMask = maskType === 0 /* ALPHA */;
2351
- const dx = x - targetX;
2352
- const dy = y - targetY;
2353
- let dIdx = y * dw + x;
2354
- let mIdx = (my + dy) * mPitch + (mx + dx);
2355
- const dStride = dw - actualW;
2356
- const mStride = mPitch - actualW;
3091
+ let dIdx = y * dw + x | 0;
3092
+ const dStride = dw - actualW | 0;
2357
3093
  for (let iy = 0; iy < actualH; iy++) {
2358
3094
  for (let ix = 0; ix < actualW; ix++) {
2359
- let weight = globalAlpha;
2360
- if (mask) {
2361
- const mVal = mask[mIdx];
2362
- if (isAlphaMask) {
2363
- const effectiveM = invertMask ? 255 - mVal : mVal;
2364
- if (effectiveM === 0) {
2365
- dIdx++;
2366
- mIdx++;
2367
- continue;
2368
- }
2369
- if (globalAlpha === 255) {
2370
- weight = effectiveM;
2371
- } else if (effectiveM === 255) {
2372
- weight = globalAlpha;
2373
- } else {
2374
- weight = effectiveM * globalAlpha + 128 >> 8;
2375
- }
2376
- } else {
2377
- const isHit = invertMask ? mVal === 0 : mVal === 1;
2378
- if (!isHit) {
2379
- dIdx++;
2380
- mIdx++;
2381
- continue;
2382
- }
2383
- weight = globalAlpha;
2384
- }
2385
- if (weight === 0) {
2386
- dIdx++;
2387
- mIdx++;
2388
- continue;
2389
- }
2390
- }
2391
- let currentSrcColor = color;
2392
- if (weight < 255) {
2393
- let currentSrcAlpha = baseSrcAlpha;
2394
- if (baseSrcAlpha === 255) {
2395
- currentSrcAlpha = weight;
2396
- } else {
2397
- currentSrcAlpha = baseSrcAlpha * weight + 128 >> 8;
2398
- }
2399
- if (!isOverwrite && currentSrcAlpha === 0) {
2400
- dIdx++;
2401
- mIdx++;
2402
- continue;
2403
- }
2404
- currentSrcColor = (color & 16777215 | currentSrcAlpha << 24) >>> 0;
2405
- }
2406
- dst32[dIdx] = blendFn(currentSrcColor, dst32[dIdx]);
3095
+ dst32[dIdx] = blendFn(finalSrcColor, dst32[dIdx]);
2407
3096
  dIdx++;
2408
- mIdx++;
2409
3097
  }
2410
3098
  dIdx += dStride;
2411
- mIdx += mStride;
2412
3099
  }
2413
3100
  }
2414
3101
 
2415
3102
  // src/History/PixelMutator/mutatorBlendColor.ts
2416
- function mutatorBlendColor(writer) {
3103
+ var defaults10 = {
3104
+ blendColorPixelData
3105
+ };
3106
+ var mutatorBlendColor = ((writer, deps = defaults10) => {
3107
+ const {
3108
+ blendColorPixelData: blendColorPixelData2 = defaults10.blendColorPixelData
3109
+ } = deps;
2417
3110
  return {
2418
3111
  blendColor(color, opts = {}) {
2419
3112
  const {
@@ -2423,10 +3116,10 @@ function mutatorBlendColor(writer) {
2423
3116
  h = writer.target.height
2424
3117
  } = opts;
2425
3118
  writer.accumulator.storeRegionBeforeState(x, y, w, h);
2426
- blendColorPixelData(writer.target, color, opts);
3119
+ blendColorPixelData2(writer.target, color, opts);
2427
3120
  }
2428
3121
  };
2429
- }
3122
+ });
2430
3123
 
2431
3124
  // src/History/PixelMutator/mutatorBlendPixel.ts
2432
3125
  function mutatorBlendPixel(writer) {
@@ -2460,13 +3153,7 @@ function blendPixelData(dst, src, opts) {
2460
3153
  w: width = src.width,
2461
3154
  h: height = src.height,
2462
3155
  alpha: globalAlpha = 255,
2463
- blendFn = sourceOverFast,
2464
- mask,
2465
- maskType = 0 /* ALPHA */,
2466
- mw,
2467
- mx = 0,
2468
- my = 0,
2469
- invertMask = false
3156
+ blendFn = sourceOverPerfect
2470
3157
  } = opts;
2471
3158
  if (globalAlpha === 0) return;
2472
3159
  let x = targetX;
@@ -2504,91 +3191,48 @@ function blendPixelData(dst, src, opts) {
2504
3191
  const src32 = src.data32;
2505
3192
  const dw = dst.width;
2506
3193
  const sw = src.width;
2507
- const mPitch = mw ?? width;
2508
- const isAlphaMask = maskType === 0 /* ALPHA */;
2509
- const dx = x - targetX;
2510
- const dy = y - targetY;
2511
- let dIdx = y * dw + x;
2512
- let sIdx = sy * sw + sx;
2513
- let mIdx = (my + dy) * mPitch + (mx + dx);
2514
- const dStride = dw - actualW;
2515
- const sStride = sw - actualW;
2516
- const mStride = mPitch - actualW;
3194
+ let dIdx = y * dw + x | 0;
3195
+ let sIdx = sy * sw + sx | 0;
3196
+ const dStride = dw - actualW | 0;
3197
+ const sStride = sw - actualW | 0;
3198
+ const isOpaque = globalAlpha === 255;
2517
3199
  const isOverwrite = blendFn.isOverwrite;
2518
3200
  for (let iy = 0; iy < actualH; iy++) {
2519
3201
  for (let ix = 0; ix < actualW; ix++) {
2520
- const baseSrcColor = src32[sIdx];
2521
- const baseSrcAlpha = baseSrcColor >>> 24;
2522
- if (baseSrcAlpha === 0 && !isOverwrite) {
3202
+ const srcCol = src32[sIdx];
3203
+ const srcAlpha = srcCol >>> 24;
3204
+ if (srcAlpha === 0 && !isOverwrite) {
2523
3205
  dIdx++;
2524
3206
  sIdx++;
2525
- mIdx++;
2526
3207
  continue;
2527
3208
  }
2528
- let weight = globalAlpha;
2529
- if (mask) {
2530
- const mVal = mask[mIdx];
2531
- if (isAlphaMask) {
2532
- const effectiveM = invertMask ? 255 - mVal : mVal;
2533
- if (effectiveM === 0) {
2534
- dIdx++;
2535
- sIdx++;
2536
- mIdx++;
2537
- continue;
2538
- }
2539
- if (globalAlpha === 255) {
2540
- weight = effectiveM;
2541
- } else if (effectiveM === 255) {
2542
- weight = globalAlpha;
2543
- } else {
2544
- weight = effectiveM * globalAlpha + 128 >> 8;
2545
- }
2546
- } else {
2547
- const isHit = invertMask ? mVal === 0 : mVal === 1;
2548
- if (!isHit) {
2549
- dIdx++;
2550
- sIdx++;
2551
- mIdx++;
2552
- continue;
2553
- }
2554
- weight = globalAlpha;
2555
- }
2556
- if (weight === 0) {
2557
- dIdx++;
2558
- sIdx++;
2559
- mIdx++;
2560
- continue;
2561
- }
2562
- }
2563
- let currentSrcColor = baseSrcColor;
2564
- if (weight < 255) {
2565
- let currentSrcAlpha = baseSrcAlpha;
2566
- if (baseSrcAlpha === 255) {
2567
- currentSrcAlpha = weight;
2568
- } else {
2569
- currentSrcAlpha = baseSrcAlpha * weight + 128 >> 8;
2570
- }
2571
- if (!isOverwrite && currentSrcAlpha === 0) {
3209
+ let finalCol = srcCol;
3210
+ if (!isOpaque) {
3211
+ const a = srcAlpha * globalAlpha + 128 >> 8;
3212
+ if (a === 0 && !isOverwrite) {
2572
3213
  dIdx++;
2573
3214
  sIdx++;
2574
- mIdx++;
2575
3215
  continue;
2576
3216
  }
2577
- currentSrcColor = (baseSrcColor & 16777215 | currentSrcAlpha << 24) >>> 0;
3217
+ finalCol = (srcCol & 16777215 | a << 24) >>> 0;
2578
3218
  }
2579
- dst32[dIdx] = blendFn(currentSrcColor, dst32[dIdx]);
3219
+ dst32[dIdx] = blendFn(finalCol, dst32[dIdx]);
2580
3220
  dIdx++;
2581
3221
  sIdx++;
2582
- mIdx++;
2583
3222
  }
2584
3223
  dIdx += dStride;
2585
3224
  sIdx += sStride;
2586
- mIdx += mStride;
2587
3225
  }
2588
3226
  }
2589
3227
 
2590
3228
  // src/History/PixelMutator/mutatorBlendPixelData.ts
2591
- function mutatorBlendPixelData(writer) {
3229
+ var defaults11 = {
3230
+ blendPixelData
3231
+ };
3232
+ var mutatorBlendPixelData = ((writer, deps = defaults11) => {
3233
+ const {
3234
+ blendPixelData: blendPixelData2 = defaults11.blendPixelData
3235
+ } = deps;
2592
3236
  return {
2593
3237
  blendPixelData(src, opts) {
2594
3238
  const {
@@ -2598,12 +3242,13 @@ function mutatorBlendPixelData(writer) {
2598
3242
  h = src.height
2599
3243
  } = opts;
2600
3244
  writer.accumulator.storeRegionBeforeState(x, y, w, h);
2601
- blendPixelData(writer.target, src, opts);
3245
+ blendPixelData2(writer.target, src, opts);
2602
3246
  }
2603
3247
  };
2604
- }
3248
+ });
2605
3249
 
2606
3250
  // src/PixelData/fillPixelData.ts
3251
+ var SCRATCH_RECT = makeClippedRect();
2607
3252
  function fillPixelData(dst, color, _x, _y, _w, _h) {
2608
3253
  let x;
2609
3254
  let y;
@@ -2625,34 +3270,57 @@ function fillPixelData(dst, color, _x, _y, _w, _h) {
2625
3270
  w = dst.width;
2626
3271
  h = dst.height;
2627
3272
  }
2628
- if (x < 0) {
2629
- w += x;
2630
- x = 0;
2631
- }
2632
- if (y < 0) {
2633
- h += y;
2634
- y = 0;
2635
- }
2636
- const actualW = Math.min(w, dst.width - x);
2637
- const actualH = Math.min(h, dst.height - y);
2638
- if (actualW <= 0 || actualH <= 0) {
2639
- return;
2640
- }
3273
+ const clip = resolveRectClipping(x, y, w, h, dst.width, dst.height, SCRATCH_RECT);
3274
+ if (!clip.inBounds) return;
3275
+ const {
3276
+ x: finalX,
3277
+ y: finalY,
3278
+ w: actualW,
3279
+ h: actualH
3280
+ } = clip;
2641
3281
  const dst32 = dst.data32;
2642
3282
  const dw = dst.width;
2643
- if (actualW === dw && actualH === dst.height && x === 0 && y === 0) {
3283
+ if (actualW === dw && actualH === dst.height && finalX === 0 && finalY === 0) {
2644
3284
  dst32.fill(color);
2645
3285
  return;
2646
3286
  }
2647
3287
  for (let iy = 0; iy < actualH; iy++) {
2648
- const start = (y + iy) * dw + x;
3288
+ const start = (finalY + iy) * dw + finalX;
2649
3289
  const end = start + actualW;
2650
3290
  dst32.fill(color, start, end);
2651
3291
  }
2652
3292
  }
2653
3293
 
2654
- // src/History/PixelMutator/mutatorFillPixelData.ts
2655
- function mutatorFill(writer) {
3294
+ // src/History/PixelMutator/mutatorClear.ts
3295
+ var defaults12 = {
3296
+ fillPixelData
3297
+ };
3298
+ var mutatorClear = ((writer, deps = defaults12) => {
3299
+ const {
3300
+ fillPixelData: fillPixelData2 = defaults12.fillPixelData
3301
+ } = deps;
3302
+ return {
3303
+ clear(rect = {}) {
3304
+ const {
3305
+ x = 0,
3306
+ y = 0,
3307
+ w = writer.target.width,
3308
+ h = writer.target.height
3309
+ } = rect;
3310
+ writer.accumulator.storeRegionBeforeState(x, y, w, h);
3311
+ fillPixelData2(writer.target, 0, x, y, w, h);
3312
+ }
3313
+ };
3314
+ });
3315
+
3316
+ // src/History/PixelMutator/mutatorFill.ts
3317
+ var defaults13 = {
3318
+ fillPixelData
3319
+ };
3320
+ var mutatorFill = ((writer, deps = defaults13) => {
3321
+ const {
3322
+ fillPixelData: fillPixelData2 = defaults13.fillPixelData
3323
+ } = deps;
2656
3324
  return {
2657
3325
  fill(color, rect = {}) {
2658
3326
  const {
@@ -2662,12 +3330,13 @@ function mutatorFill(writer) {
2662
3330
  h = writer.target.height
2663
3331
  } = rect;
2664
3332
  writer.accumulator.storeRegionBeforeState(x, y, w, h);
2665
- fillPixelData(writer.target, color, x, y, w, h);
3333
+ fillPixelData2(writer.target, color, x, y, w, h);
2666
3334
  }
2667
3335
  };
2668
- }
3336
+ });
2669
3337
 
2670
3338
  // src/PixelData/invertPixelData.ts
3339
+ var SCRATCH_RECT2 = makeClippedRect();
2671
3340
  function invertPixelData(pixelData, opts = {}) {
2672
3341
  const dst = pixelData;
2673
3342
  const {
@@ -2681,21 +3350,14 @@ function invertPixelData(pixelData, opts = {}) {
2681
3350
  my = 0,
2682
3351
  invertMask = false
2683
3352
  } = opts;
2684
- let x = targetX;
2685
- let y = targetY;
2686
- let w = width;
2687
- let h = height;
2688
- if (x < 0) {
2689
- w += x;
2690
- x = 0;
2691
- }
2692
- if (y < 0) {
2693
- h += y;
2694
- y = 0;
2695
- }
2696
- const actualW = Math.min(w, dst.width - x);
2697
- const actualH = Math.min(h, dst.height - y);
2698
- if (actualW <= 0 || actualH <= 0) return;
3353
+ const clip = resolveRectClipping(targetX, targetY, width, height, dst.width, dst.height, SCRATCH_RECT2);
3354
+ if (!clip.inBounds) return;
3355
+ const {
3356
+ x,
3357
+ y,
3358
+ w: actualW,
3359
+ h: actualH
3360
+ } = clip;
2699
3361
  const dst32 = dst.data32;
2700
3362
  const dw = dst.width;
2701
3363
  const mPitch = mw ?? width;
@@ -2731,54 +3393,137 @@ function invertPixelData(pixelData, opts = {}) {
2731
3393
  }
2732
3394
 
2733
3395
  // src/History/PixelMutator/mutatorInvert.ts
2734
- function mutatorInvert(writer) {
3396
+ var defaults14 = {
3397
+ invertPixelData
3398
+ };
3399
+ var mutatorInvert = ((writer, deps = defaults14) => {
3400
+ const {
3401
+ invertPixelData: invertPixelData2 = defaults14.invertPixelData
3402
+ } = deps;
3403
+ return {
3404
+ invert(opts = {}) {
3405
+ const {
3406
+ x = 0,
3407
+ y = 0,
3408
+ w = writer.target.width,
3409
+ h = writer.target.height
3410
+ } = opts;
3411
+ writer.accumulator.storeRegionBeforeState(x, y, w, h);
3412
+ invertPixelData2(writer.target, opts);
3413
+ }
3414
+ };
3415
+ });
3416
+
3417
+ // src/History/PixelMutator.ts
3418
+ function makeFullPixelMutator(writer) {
3419
+ return {
3420
+ ...mutatorApplyAlphaMask(writer),
3421
+ ...mutatorApplyBinaryMask(writer),
3422
+ ...mutatorBlendPixelData(writer),
3423
+ ...mutatorBlendColor(writer),
3424
+ ...mutatorBlendPixel(writer),
3425
+ ...mutatorFill(writer),
3426
+ ...mutatorInvert(writer),
3427
+ ...mutatorApplyCircleBrush(writer),
3428
+ ...mutatorApplyCircleBrushStroke(writer),
3429
+ ...mutatorApplyCirclePencilStroke(writer),
3430
+ ...mutatorApplyRectBrush(writer),
3431
+ ...mutatorApplyRectBrushStroke(writer),
3432
+ ...mutatorApplyRectPencil(writer),
3433
+ ...mutatorApplyRectPencilStroke(writer),
3434
+ ...mutatorClear(writer)
3435
+ };
3436
+ }
3437
+
3438
+ // src/History/PixelWriter.ts
3439
+ var PixelWriter = class {
3440
+ target;
3441
+ historyManager;
3442
+ accumulator;
3443
+ config;
3444
+ mutator;
3445
+ constructor(target, mutatorFactory, {
3446
+ tileSize = 256,
3447
+ maxHistorySteps = 50,
3448
+ historyManager = new HistoryManager(maxHistorySteps)
3449
+ } = {}) {
3450
+ this.target = target;
3451
+ this.config = new PixelEngineConfig(tileSize);
3452
+ this.historyManager = historyManager;
3453
+ this.accumulator = new PixelAccumulator(target, this.config);
3454
+ this.mutator = mutatorFactory(this);
3455
+ }
3456
+ withHistory(cb) {
3457
+ cb(this.mutator);
3458
+ this.captureHistory();
3459
+ }
3460
+ captureHistory() {
3461
+ const beforeTiles = this.accumulator.beforeTiles;
3462
+ if (beforeTiles.length === 0) return;
3463
+ const afterTiles = this.accumulator.extractAfterTiles();
3464
+ const patch = {
3465
+ beforeTiles,
3466
+ afterTiles
3467
+ };
3468
+ const target = this.target;
3469
+ const tileSize = this.config.tileSize;
3470
+ const accumulator = this.accumulator;
3471
+ const action = {
3472
+ undo: () => applyPatchTiles(target, patch.beforeTiles, tileSize),
3473
+ redo: () => applyPatchTiles(target, patch.afterTiles, tileSize),
3474
+ dispose: () => accumulator.recyclePatch(patch)
3475
+ };
3476
+ this.historyManager.commit(action);
3477
+ this.accumulator.reset();
3478
+ }
3479
+ };
3480
+
3481
+ // src/History/PixelMutator/mutatorApplyCirclePencil.ts
3482
+ var defaults15 = {
3483
+ applyCircleBrushToPixelData,
3484
+ getCircleBrushOrPencilBounds,
3485
+ fallOff: () => 1
3486
+ };
3487
+ var mutatorApplyCirclePencil = ((writer, deps = defaults15) => {
3488
+ const {
3489
+ applyCircleBrushToPixelData: applyCircleBrushToPixelData2 = defaults15.applyCircleBrushToPixelData,
3490
+ getCircleBrushOrPencilBounds: getCircleBrushOrPencilBounds2 = defaults15.getCircleBrushOrPencilBounds,
3491
+ fallOff = defaults15.fallOff
3492
+ } = deps;
3493
+ const boundsOut = {
3494
+ x: 0,
3495
+ y: 0,
3496
+ w: 0,
3497
+ h: 0
3498
+ };
2735
3499
  return {
2736
- invert(opts = {}) {
3500
+ applyCirclePencil(color, centerX, centerY, brushSize, alpha = 255, blendFn) {
3501
+ const bounds = getCircleBrushOrPencilBounds2(centerX, centerY, brushSize, writer.target.width, writer.target.height, boundsOut);
2737
3502
  const {
2738
- x = 0,
2739
- y = 0,
2740
- w = writer.target.width,
2741
- h = writer.target.height
2742
- } = opts;
3503
+ x,
3504
+ y,
3505
+ w,
3506
+ h
3507
+ } = bounds;
2743
3508
  writer.accumulator.storeRegionBeforeState(x, y, w, h);
2744
- invertPixelData(writer.target, opts);
2745
- }
2746
- };
2747
- }
2748
-
2749
- // src/History/PixelMutator.ts
2750
- function makeFullPixelMutator(writer) {
2751
- return {
2752
- ...mutatorApplyMask(writer),
2753
- ...mutatorBlendPixelData(writer),
2754
- ...mutatorBlendColor(writer),
2755
- ...mutatorBlendPixel(writer),
2756
- ...mutatorFill(writer),
2757
- ...mutatorInvert(writer),
2758
- ...mutatorApplyCircleBrush(writer),
2759
- ...mutatorApplyRectBrush(writer)
2760
- };
2761
- }
2762
-
2763
- // src/ImageData/ReusableImageData.ts
2764
- function makeReusableImageData() {
2765
- let imageData = null;
2766
- return function getReusableImageData(width, height) {
2767
- const hasInstance = !!imageData;
2768
- const widthMatches = hasInstance && imageData.width === width;
2769
- const heightMatches = hasInstance && imageData.height === height;
2770
- if (!widthMatches || !heightMatches) {
2771
- imageData = new ImageData(width, height);
3509
+ applyCircleBrushToPixelData2(writer.target, color, centerX, centerY, brushSize, alpha, fallOff, blendFn, bounds);
2772
3510
  }
2773
- return imageData;
2774
3511
  };
2775
- }
3512
+ });
2776
3513
 
2777
3514
  // src/ImageData/copyImageData.ts
2778
- function copyImageData({ data, width, height }) {
3515
+ function copyImageData({
3516
+ data,
3517
+ width,
3518
+ height
3519
+ }) {
2779
3520
  return new ImageData(data.slice(), width, height);
2780
3521
  }
2781
- function copyImageDataLike({ data, width, height }) {
3522
+ function copyImageDataLike({
3523
+ data,
3524
+ width,
3525
+ height
3526
+ }) {
2782
3527
  return {
2783
3528
  data: data.slice(),
2784
3529
  width,
@@ -2786,20 +3531,15 @@ function copyImageDataLike({ data, width, height }) {
2786
3531
  };
2787
3532
  }
2788
3533
 
2789
- // src/PixelData/pixelDataToAlphaMask.ts
2790
- function pixelDataToAlphaMask(pixelData) {
2791
- const {
2792
- data32,
3534
+ // src/ImageData/ImageDataLike.ts
3535
+ function makeImageDataLike(width, height, data) {
3536
+ const size = width * height * 4;
3537
+ const buffer = data ? new Uint8ClampedArray(data.buffer, data.byteOffset, size) : new Uint8ClampedArray(size);
3538
+ return {
2793
3539
  width,
2794
- height
2795
- } = pixelData;
2796
- const len = data32.length;
2797
- const mask = new Uint8Array(width * height);
2798
- for (let i = 0; i < len; i++) {
2799
- const val = data32[i];
2800
- mask[i] = val >>> 24 & 255;
2801
- }
2802
- return mask;
3540
+ height,
3541
+ data: buffer
3542
+ };
2803
3543
  }
2804
3544
 
2805
3545
  // src/ImageData/imageDataToAlphaMask.ts
@@ -2809,11 +3549,7 @@ function imageDataToAlphaMask(imageData) {
2809
3549
  height,
2810
3550
  data
2811
3551
  } = imageData;
2812
- const data32 = new Uint32Array(
2813
- data.buffer,
2814
- data.byteOffset,
2815
- data.byteLength >> 2
2816
- );
3552
+ const data32 = new Uint32Array(data.buffer, data.byteOffset, data.byteLength >> 2);
2817
3553
  const len = data32.length;
2818
3554
  const mask = new Uint8Array(width * height);
2819
3555
  for (let i = 0; i < len; i++) {
@@ -2826,12 +3562,25 @@ function imageDataToAlphaMask(imageData) {
2826
3562
  // src/ImageData/imageDataToDataUrl.ts
2827
3563
  var get = makeReusableCanvas();
2828
3564
  function imageDataToDataUrl(imageData) {
2829
- const { canvas, ctx } = get(imageData.width, imageData.height);
3565
+ const {
3566
+ canvas,
3567
+ ctx
3568
+ } = get(imageData.width, imageData.height);
2830
3569
  ctx.putImageData(imageData, 0, 0);
2831
3570
  return canvas.toDataURL();
2832
3571
  }
2833
3572
  imageDataToDataUrl.reset = get.reset;
2834
3573
 
3574
+ // src/ImageData/imageDataToUInt32Array.ts
3575
+ function imageDataToUInt32Array(imageData) {
3576
+ return new Uint32Array(
3577
+ imageData.data.buffer,
3578
+ imageData.data.byteOffset,
3579
+ // Shift right by 2 is a fast bitwise division by 4.
3580
+ imageData.data.byteLength >> 2
3581
+ );
3582
+ }
3583
+
2835
3584
  // src/ImageData/invertImageData.ts
2836
3585
  function invertImageData(imageData) {
2837
3586
  const data = imageData.data;
@@ -2874,7 +3623,11 @@ function resample32(srcData32, srcW, srcH, factor) {
2874
3623
  // src/ImageData/resampleImageData.ts
2875
3624
  function resampleImageData(source, factor) {
2876
3625
  const src32 = new Uint32Array(source.data.buffer);
2877
- const { data, width, height } = resample32(src32, source.width, source.height, factor);
3626
+ const {
3627
+ data,
3628
+ width,
3629
+ height
3630
+ } = resample32(src32, source.width, source.height, factor);
2878
3631
  const uint8ClampedArray = new Uint8ClampedArray(data.buffer);
2879
3632
  return new ImageData(uint8ClampedArray, width, height);
2880
3633
  }
@@ -2903,14 +3656,25 @@ function resizeImageData(current, newWidth, newHeight, offsetX = 0, offsetY = 0)
2903
3656
  const srcX = x0 - offsetX;
2904
3657
  const dstStart = (dstY * newWidth + x0) * 4;
2905
3658
  const srcStart = (srcY * oldW + srcX) * 4;
2906
- newData.set(
2907
- oldData.subarray(srcStart, srcStart + rowLen),
2908
- dstStart
2909
- );
3659
+ newData.set(oldData.subarray(srcStart, srcStart + rowLen), dstStart);
2910
3660
  }
2911
3661
  return result;
2912
3662
  }
2913
3663
 
3664
+ // src/ImageData/ReusableImageData.ts
3665
+ function makeReusableImageData() {
3666
+ let imageData = null;
3667
+ return function getReusableImageData(width, height) {
3668
+ const hasInstance = !!imageData;
3669
+ const widthMatches = hasInstance && imageData.width === width;
3670
+ const heightMatches = hasInstance && imageData.height === height;
3671
+ if (!widthMatches || !heightMatches) {
3672
+ imageData = new ImageData(width, height);
3673
+ }
3674
+ return imageData;
3675
+ };
3676
+ }
3677
+
2914
3678
  // src/ImageData/serialization.ts
2915
3679
  function base64EncodeArrayBuffer(buffer) {
2916
3680
  const uint8 = new Uint8Array(buffer);
@@ -2953,32 +3717,53 @@ function deserializeNullableImageData(serialized) {
2953
3717
  return deserializeImageData(serialized);
2954
3718
  }
2955
3719
 
3720
+ // src/ImageData/uInt32ArrayToImageData.ts
3721
+ function uInt32ArrayToImageData(data, width, height) {
3722
+ const buffer = data.buffer;
3723
+ const byteOffset = data.byteOffset;
3724
+ const byteLength = data.byteLength;
3725
+ const clampedArray = new Uint8ClampedArray(buffer, byteOffset, byteLength);
3726
+ return new ImageData(clampedArray, width, height);
3727
+ }
3728
+ function uInt32ArrayToImageDataLike(data, width, height) {
3729
+ const buffer = data.buffer;
3730
+ const byteOffset = data.byteOffset;
3731
+ const byteLength = data.byteLength;
3732
+ const clampedArray = new Uint8ClampedArray(buffer, byteOffset, byteLength);
3733
+ return {
3734
+ width,
3735
+ height,
3736
+ data: clampedArray
3737
+ };
3738
+ }
3739
+
2956
3740
  // src/ImageData/writeImageData.ts
3741
+ var SCRATCH_BLIT2 = makeClippedBlit();
2957
3742
  function writeImageData(target, source, x, y, sx = 0, sy = 0, sw = source.width, sh = source.height, mask = null, maskType = 1 /* BINARY */) {
2958
3743
  const dstW = target.width;
2959
3744
  const dstH = target.height;
2960
3745
  const dstData = target.data;
2961
3746
  const srcW = source.width;
2962
3747
  const srcData = source.data;
2963
- const x0 = Math.max(0, x);
2964
- const y0 = Math.max(0, y);
2965
- const x1 = Math.min(dstW, x + sw);
2966
- const y1 = Math.min(dstH, y + sh);
2967
- if (x1 <= x0 || y1 <= y0) {
2968
- return;
2969
- }
3748
+ const clip = resolveBlitClipping(x, y, sx, sy, sw, sh, dstW, dstH, srcW, source.height, SCRATCH_BLIT2);
3749
+ if (!clip.inBounds) return;
3750
+ const {
3751
+ x: dstX,
3752
+ y: dstY,
3753
+ sx: srcX,
3754
+ sy: srcY,
3755
+ w: copyW,
3756
+ h: copyH
3757
+ } = clip;
2970
3758
  const useMask = !!mask;
2971
- const rowCount = y1 - y0;
2972
- const rowLenPixels = x1 - x0;
2973
- for (let row = 0; row < rowCount; row++) {
2974
- const dstY = y0 + row;
2975
- const srcY = sy + (dstY - y);
2976
- const srcXBase = sx + (x0 - x);
2977
- const dstStart = (dstY * dstW + x0) * 4;
2978
- const srcStart = (srcY * srcW + srcXBase) * 4;
3759
+ for (let row = 0; row < copyH; row++) {
3760
+ const currentDstY = dstY + row;
3761
+ const currentSrcY = srcY + row;
3762
+ const dstStart = (currentDstY * dstW + dstX) * 4;
3763
+ const srcStart = (currentSrcY * srcW + srcX) * 4;
2979
3764
  if (useMask && mask) {
2980
- for (let ix = 0; ix < rowLenPixels; ix++) {
2981
- const mi = srcY * srcW + (srcXBase + ix);
3765
+ for (let ix = 0; ix < copyW; ix++) {
3766
+ const mi = currentSrcY * srcW + (srcX + ix);
2982
3767
  const alpha = mask[mi];
2983
3768
  if (alpha === 0) {
2984
3769
  continue;
@@ -3000,7 +3785,7 @@ function writeImageData(target, source, x, y, sx = 0, sy = 0, sw = source.width,
3000
3785
  }
3001
3786
  }
3002
3787
  } else {
3003
- const byteLen = rowLenPixels * 4;
3788
+ const byteLen = copyW * 4;
3004
3789
  const sub = srcData.subarray(srcStart, srcStart + byteLen);
3005
3790
  dstData.set(sub, dstStart);
3006
3791
  }
@@ -3008,24 +3793,52 @@ function writeImageData(target, source, x, y, sx = 0, sy = 0, sw = source.width,
3008
3793
  }
3009
3794
 
3010
3795
  // src/ImageData/writeImageDataBuffer.ts
3796
+ var SCRATCH_BLIT3 = makeClippedBlit();
3011
3797
  function writeImageDataBuffer(imageData, data, _x, _y, _w, _h) {
3012
- const { x, y, w, h } = typeof _x === "object" ? _x : { x: _x, y: _y, w: _w, h: _h };
3013
- const { width: dstW, height: dstH, data: dst } = imageData;
3014
- const x0 = Math.max(0, x);
3015
- const y0 = Math.max(0, y);
3016
- const x1 = Math.min(dstW, x + w);
3017
- const y1 = Math.min(dstH, y + h);
3018
- if (x1 <= x0 || y1 <= y0) return;
3019
- const rowLen = (x1 - x0) * 4;
3020
- const srcCol = x0 - x;
3021
- const srcYOffset = y0 - y;
3022
- const actualH = y1 - y0;
3023
- for (let row = 0; row < actualH; row++) {
3024
- const dstStart = ((y0 + row) * dstW + x0) * 4;
3025
- const srcRow = srcYOffset + row;
3026
- const o = (srcRow * w + srcCol) * 4;
3027
- dst.set(data.subarray(o, o + rowLen), dstStart);
3798
+ const {
3799
+ x,
3800
+ y,
3801
+ w,
3802
+ h
3803
+ } = typeof _x === "object" ? _x : {
3804
+ x: _x,
3805
+ y: _y,
3806
+ w: _w,
3807
+ h: _h
3808
+ };
3809
+ const {
3810
+ width: dstW,
3811
+ height: dstH,
3812
+ data: dst
3813
+ } = imageData;
3814
+ const clip = resolveBlitClipping(x, y, 0, 0, w, h, dstW, dstH, w, h, SCRATCH_BLIT3);
3815
+ if (!clip.inBounds) return;
3816
+ const {
3817
+ x: dstX,
3818
+ y: dstY,
3819
+ sx: srcX,
3820
+ sy: srcY,
3821
+ w: copyW,
3822
+ h: copyH
3823
+ } = clip;
3824
+ const rowLen = copyW * 4;
3825
+ for (let row = 0; row < copyH; row++) {
3826
+ const dstStart = ((dstY + row) * dstW + dstX) * 4;
3827
+ const srcStart = ((srcY + row) * w + srcX) * 4;
3828
+ dst.set(data.subarray(srcStart, srcStart + rowLen), dstStart);
3829
+ }
3830
+ }
3831
+
3832
+ // src/IndexedImage/getIndexedImageColorCounts.ts
3833
+ function getIndexedImageColorCounts(indexedImage) {
3834
+ const data = indexedImage.data;
3835
+ const palette = indexedImage.palette;
3836
+ const frequencies = new Int32Array(palette.length);
3837
+ for (let i = 0; i < data.length; i++) {
3838
+ const colorIndex = data[i];
3839
+ frequencies[colorIndex]++;
3028
3840
  }
3841
+ return frequencies;
3029
3842
  }
3030
3843
 
3031
3844
  // src/IndexedImage/IndexedImage.ts
@@ -3091,13 +3904,7 @@ var IndexedImage = class _IndexedImage {
3091
3904
  indexedData[i] = id;
3092
3905
  }
3093
3906
  const palette = Uint32Array.from(colorMap.keys());
3094
- return new _IndexedImage(
3095
- width,
3096
- height,
3097
- indexedData,
3098
- palette,
3099
- transparentPalletIndex
3100
- );
3907
+ return new _IndexedImage(width, height, indexedData, palette, transparentPalletIndex);
3101
3908
  }
3102
3909
  /**
3103
3910
  * Retrieves the 32-bit packed color value at the given coordinates.
@@ -3112,21 +3919,13 @@ var IndexedImage = class _IndexedImage {
3112
3919
  }
3113
3920
  };
3114
3921
 
3115
- // src/IndexedImage/getIndexedImageColorCounts.ts
3116
- function getIndexedImageColorCounts(indexedImage) {
3117
- const data = indexedImage.data;
3118
- const palette = indexedImage.palette;
3119
- const frequencies = new Int32Array(palette.length);
3120
- for (let i = 0; i < data.length; i++) {
3121
- const colorIndex = data[i];
3122
- frequencies[colorIndex]++;
3123
- }
3124
- return frequencies;
3125
- }
3126
-
3127
3922
  // src/IndexedImage/indexedImageToAverageColor.ts
3128
3923
  function indexedImageToAverageColor(indexedImage, includeTransparent = false) {
3129
- const { data, palette, transparentPalletIndex } = indexedImage;
3924
+ const {
3925
+ data,
3926
+ palette,
3927
+ transparentPalletIndex
3928
+ } = indexedImage;
3130
3929
  const counts = new Uint32Array(palette.length);
3131
3930
  for (let i = 0; i < data.length; i++) {
3132
3931
  const id = data[i];
@@ -3166,26 +3965,14 @@ function indexedImageToAverageColor(indexedImage, includeTransparent = false) {
3166
3965
  return packColor(r, g, b, a);
3167
3966
  }
3168
3967
 
3169
- // src/IndexedImage/resampleIndexedImage.ts
3170
- function resampleIndexedImage(source, factor) {
3171
- const { data, width, height } = resample32(
3172
- source.data,
3173
- source.width,
3174
- source.height,
3175
- factor
3176
- );
3177
- return new IndexedImage(
3968
+ // src/IndexedImage/indexedImageToImageData.ts
3969
+ function indexedImageToImageData(indexedImage) {
3970
+ const {
3178
3971
  width,
3179
3972
  height,
3180
3973
  data,
3181
- source.palette,
3182
- source.transparentPalletIndex
3183
- );
3184
- }
3185
-
3186
- // src/IndexedImage/indexedImageToImageData.ts
3187
- function indexedImageToImageData(indexedImage) {
3188
- const { width, height, data, palette } = indexedImage;
3974
+ palette
3975
+ } = indexedImage;
3189
3976
  const result = new ImageData(width, height);
3190
3977
  const data32 = new Uint32Array(result.data.buffer);
3191
3978
  for (let i = 0; i < data.length; i++) {
@@ -3196,6 +3983,16 @@ function indexedImageToImageData(indexedImage) {
3196
3983
  return result;
3197
3984
  }
3198
3985
 
3986
+ // src/IndexedImage/resampleIndexedImage.ts
3987
+ function resampleIndexedImage(source, factor) {
3988
+ const {
3989
+ data,
3990
+ width,
3991
+ height
3992
+ } = resample32(source.data, source.width, source.height, factor);
3993
+ return new IndexedImage(width, height, data, source.palette, source.transparentPalletIndex);
3994
+ }
3995
+
3199
3996
  // src/Input/fileInputChangeToImageData.ts
3200
3997
  async function fileInputChangeToImageData(event) {
3201
3998
  const target = event.target;
@@ -3219,23 +4016,11 @@ async function fileToImageData(file) {
3219
4016
  let bitmap = null;
3220
4017
  try {
3221
4018
  bitmap = await createImageBitmap(file);
3222
- const canvas = new OffscreenCanvas(
3223
- bitmap.width,
3224
- bitmap.height
3225
- );
4019
+ const canvas = new OffscreenCanvas(bitmap.width, bitmap.height);
3226
4020
  const ctx = canvas.getContext("2d");
3227
4021
  if (!ctx) throw new Error(OFFSCREEN_CANVAS_CTX_FAILED);
3228
- ctx.drawImage(
3229
- bitmap,
3230
- 0,
3231
- 0
3232
- );
3233
- return ctx.getImageData(
3234
- 0,
3235
- 0,
3236
- bitmap.width,
3237
- bitmap.height
3238
- );
4022
+ ctx.drawImage(bitmap, 0, 0);
4023
+ return ctx.getImageData(0, 0, bitmap.width, bitmap.height);
3239
4024
  } finally {
3240
4025
  bitmap?.close();
3241
4026
  }
@@ -3243,125 +4028,495 @@ async function fileToImageData(file) {
3243
4028
 
3244
4029
  // src/Input/getSupportedRasterFormats.ts
3245
4030
  var formatsPromise = null;
3246
- var defaultRasterMimes = [
3247
- "image/png",
3248
- "image/jpeg",
3249
- "image/webp",
3250
- "image/avif",
3251
- "image/gif",
3252
- "image/bmp"
3253
- ];
4031
+ var defaultRasterMimes = ["image/png", "image/jpeg", "image/webp", "image/avif", "image/gif", "image/bmp"];
3254
4032
  async function getSupportedPixelFormats(rasterMimes = defaultRasterMimes) {
3255
4033
  if (formatsPromise) {
3256
4034
  return formatsPromise;
3257
4035
  }
3258
- const probeCanvas = async () => {
3259
- const canvas = new OffscreenCanvas(1, 1);
3260
- const results = await Promise.all(
3261
- rasterMimes.map(async (mime) => {
3262
- try {
3263
- const blob = await canvas.convertToBlob({
3264
- type: mime
3265
- });
3266
- return blob.type === mime ? mime : null;
3267
- } catch {
3268
- return null;
3269
- }
3270
- })
3271
- );
3272
- return results.filter((type) => {
3273
- return type !== null;
3274
- });
3275
- };
3276
- formatsPromise = probeCanvas().catch((error) => {
3277
- formatsPromise = null;
3278
- throw error;
3279
- });
3280
- return formatsPromise;
3281
- }
3282
-
3283
- // src/Mask/copyMask.ts
3284
- function copyMask(src) {
3285
- return src.slice();
4036
+ const probeCanvas = async () => {
4037
+ const canvas = new OffscreenCanvas(1, 1);
4038
+ const results = await Promise.all(rasterMimes.map(async (mime) => {
4039
+ try {
4040
+ const blob = await canvas.convertToBlob({
4041
+ type: mime
4042
+ });
4043
+ return blob.type === mime ? mime : null;
4044
+ } catch {
4045
+ return null;
4046
+ }
4047
+ }));
4048
+ return results.filter((type) => {
4049
+ return type !== null;
4050
+ });
4051
+ };
4052
+ formatsPromise = probeCanvas().catch((error) => {
4053
+ formatsPromise = null;
4054
+ throw error;
4055
+ });
4056
+ return formatsPromise;
4057
+ }
4058
+
4059
+ // src/Mask/applyBinaryMaskToAlphaMask.ts
4060
+ function applyBinaryMaskToAlphaMask(alphaMaskDst, dstWidth, binaryMaskSrc, srcWidth, opts = {}) {
4061
+ const {
4062
+ x: targetX = 0,
4063
+ y: targetY = 0,
4064
+ w: reqWidth = 0,
4065
+ h: reqHeight = 0,
4066
+ mx = 0,
4067
+ my = 0,
4068
+ invertMask = false
4069
+ } = opts;
4070
+ if (dstWidth <= 0) return;
4071
+ if (binaryMaskSrc.length === 0) return;
4072
+ if (srcWidth <= 0) return;
4073
+ const dstHeight = alphaMaskDst.length / dstWidth | 0;
4074
+ const srcHeight = binaryMaskSrc.length / srcWidth | 0;
4075
+ if (dstHeight <= 0) return;
4076
+ if (srcHeight <= 0) return;
4077
+ const dstX0 = Math.max(0, targetX);
4078
+ const dstY0 = Math.max(0, targetY);
4079
+ const dstX1 = reqWidth > 0 ? Math.min(dstWidth, targetX + reqWidth) : dstWidth;
4080
+ const dstY1 = reqHeight > 0 ? Math.min(dstHeight, targetY + reqHeight) : dstHeight;
4081
+ if (dstX0 >= dstX1) return;
4082
+ if (dstY0 >= dstY1) return;
4083
+ const srcX0 = mx + (dstX0 - targetX);
4084
+ const srcY0 = my + (dstY0 - targetY);
4085
+ if (srcX0 >= srcWidth) return;
4086
+ if (srcY0 >= srcHeight) return;
4087
+ if (srcX0 + (dstX1 - dstX0) <= 0) return;
4088
+ if (srcY0 + (dstY1 - dstY0) <= 0) return;
4089
+ const iterW = Math.min(dstX1 - dstX0, srcWidth - srcX0);
4090
+ const iterH = Math.min(dstY1 - dstY0, srcHeight - srcY0);
4091
+ let dstIdx = dstY0 * dstWidth + dstX0;
4092
+ let srcIdx = srcY0 * srcWidth + srcX0;
4093
+ if (invertMask) {
4094
+ for (let row = 0; row < iterH; row++) {
4095
+ const dstEnd = dstIdx + iterW;
4096
+ let d = dstIdx;
4097
+ let s = srcIdx;
4098
+ while (d < dstEnd) {
4099
+ if (binaryMaskSrc[s] !== 0) {
4100
+ alphaMaskDst[d] = 0;
4101
+ }
4102
+ d++;
4103
+ s++;
4104
+ }
4105
+ dstIdx += dstWidth;
4106
+ srcIdx += srcWidth;
4107
+ }
4108
+ } else {
4109
+ for (let row = 0; row < iterH; row++) {
4110
+ const dstEnd = dstIdx + iterW;
4111
+ let d = dstIdx;
4112
+ let s = srcIdx;
4113
+ while (d < dstEnd) {
4114
+ if (binaryMaskSrc[s] === 0) {
4115
+ alphaMaskDst[d] = 0;
4116
+ }
4117
+ d++;
4118
+ s++;
4119
+ }
4120
+ dstIdx += dstWidth;
4121
+ srcIdx += srcWidth;
4122
+ }
4123
+ }
4124
+ }
4125
+
4126
+ // src/Mask/copyMask.ts
4127
+ function copyMask(src) {
4128
+ return src.slice();
4129
+ }
4130
+
4131
+ // src/Mask/invertMask.ts
4132
+ function invertBinaryMask(dst) {
4133
+ const len = dst.length;
4134
+ for (let i = 0; i < len; i++) {
4135
+ dst[i] = dst[i] === 0 ? 1 : 0;
4136
+ }
4137
+ }
4138
+ function invertAlphaMask(dst) {
4139
+ const len = dst.length;
4140
+ for (let i = 0; i < len; i++) {
4141
+ dst[i] = 255 - dst[i];
4142
+ }
4143
+ }
4144
+
4145
+ // src/Mask/mergeAlphaMasks.ts
4146
+ function mergeAlphaMasks(dst, dstWidth, src, srcWidth, opts) {
4147
+ const {
4148
+ x: targetX = 0,
4149
+ y: targetY = 0,
4150
+ w: width = 0,
4151
+ h: height = 0,
4152
+ alpha: globalAlpha = 255,
4153
+ mx = 0,
4154
+ my = 0,
4155
+ invertMask = false
4156
+ } = opts;
4157
+ const dstHeight = dst.length / dstWidth | 0;
4158
+ const srcHeight = src.length / srcWidth | 0;
4159
+ if (width <= 0) return;
4160
+ if (height <= 0) return;
4161
+ if (globalAlpha === 0) return;
4162
+ const startX = Math.max(0, -targetX, -mx);
4163
+ const startY = Math.max(0, -targetY, -my);
4164
+ const endX = Math.min(width, dstWidth - targetX, srcWidth - mx);
4165
+ const endY = Math.min(height, dstHeight - targetY, srcHeight - my);
4166
+ if (startX >= endX) return;
4167
+ if (startY >= endY) return;
4168
+ for (let iy = startY; iy < endY; iy++) {
4169
+ const dy = targetY + iy;
4170
+ const sy = my + iy;
4171
+ let dIdx = dy * dstWidth + targetX + startX;
4172
+ let sIdx = sy * srcWidth + mx + startX;
4173
+ for (let ix = startX; ix < endX; ix++) {
4174
+ const rawM = src[sIdx];
4175
+ const effectiveM = invertMask ? 255 - rawM : rawM;
4176
+ let weight = 0;
4177
+ if (effectiveM === 0) {
4178
+ weight = 0;
4179
+ } else if (effectiveM === 255) {
4180
+ weight = globalAlpha;
4181
+ } else if (globalAlpha === 255) {
4182
+ weight = effectiveM;
4183
+ } else {
4184
+ weight = effectiveM * globalAlpha + 128 >> 8;
4185
+ }
4186
+ if (weight !== 255) {
4187
+ if (weight === 0) {
4188
+ dst[dIdx] = 0;
4189
+ } else {
4190
+ const da = dst[dIdx];
4191
+ if (da === 255) {
4192
+ dst[dIdx] = weight;
4193
+ } else if (da !== 0) {
4194
+ dst[dIdx] = da * weight + 128 >> 8;
4195
+ }
4196
+ }
4197
+ }
4198
+ sIdx++;
4199
+ dIdx++;
4200
+ }
4201
+ }
4202
+ }
4203
+
4204
+ // src/Mask/mergeBinaryMasks.ts
4205
+ function mergeBinaryMasks(dst, dstWidth, src, srcWidth, opts) {
4206
+ const {
4207
+ x: targetX = 0,
4208
+ y: targetY = 0,
4209
+ w: width = 0,
4210
+ h: height = 0,
4211
+ mx = 0,
4212
+ my = 0,
4213
+ invertMask = false
4214
+ } = opts;
4215
+ if (dstWidth <= 0) return;
4216
+ if (srcWidth <= 0) return;
4217
+ const dstHeight = dst.length / dstWidth | 0;
4218
+ const srcHeight = src.length / srcWidth | 0;
4219
+ let x = targetX;
4220
+ let y = targetY;
4221
+ let w = width;
4222
+ let h = height;
4223
+ if (x < 0) {
4224
+ w += x;
4225
+ x = 0;
4226
+ }
4227
+ if (y < 0) {
4228
+ h += y;
4229
+ y = 0;
4230
+ }
4231
+ w = Math.min(w, dstWidth - x);
4232
+ h = Math.min(h, dstHeight - y);
4233
+ if (w <= 0) return;
4234
+ if (h <= 0) return;
4235
+ const startX = mx + (x - targetX);
4236
+ const startY = my + (y - targetY);
4237
+ const sX0 = Math.max(0, startX);
4238
+ const sY0 = Math.max(0, startY);
4239
+ const sX1 = Math.min(srcWidth, startX + w);
4240
+ const sY1 = Math.min(srcHeight, startY + h);
4241
+ const finalW = sX1 - sX0;
4242
+ const finalH = sY1 - sY0;
4243
+ if (finalW <= 0) return;
4244
+ if (finalH <= 0) return;
4245
+ const xShift = sX0 - startX;
4246
+ const yShift = sY0 - startY;
4247
+ const dStride = dstWidth - finalW;
4248
+ const sStride = srcWidth - finalW;
4249
+ let dIdx = (y + yShift) * dstWidth + (x + xShift);
4250
+ let sIdx = sY0 * srcWidth + sX0;
4251
+ for (let iy = 0; iy < finalH; iy++) {
4252
+ for (let ix = 0; ix < finalW; ix++) {
4253
+ const mVal = src[sIdx];
4254
+ const isMaskedOut = invertMask ? mVal !== 0 : mVal === 0;
4255
+ if (isMaskedOut) {
4256
+ dst[dIdx] = 0;
4257
+ }
4258
+ dIdx++;
4259
+ sIdx++;
4260
+ }
4261
+ dIdx += dStride;
4262
+ sIdx += sStride;
4263
+ }
3286
4264
  }
3287
4265
 
3288
- // src/Mask/invertMask.ts
3289
- function invertBinaryMask(dst) {
3290
- const len = dst.length;
3291
- for (let i = 0; i < len; i++) {
3292
- dst[i] = dst[i] === 0 ? 1 : 0;
4266
+ // src/PixelData/PixelData.ts
4267
+ var PixelData = class _PixelData {
4268
+ data32;
4269
+ imageData;
4270
+ width;
4271
+ height;
4272
+ constructor(imageData) {
4273
+ this.data32 = imageDataToUInt32Array(imageData);
4274
+ this.imageData = imageData;
4275
+ this.width = imageData.width;
4276
+ this.height = imageData.height;
3293
4277
  }
3294
- }
3295
- function invertAlphaMask(dst) {
3296
- const len = dst.length;
3297
- for (let i = 0; i < len; i++) {
3298
- dst[i] = 255 - dst[i];
4278
+ set(imageData) {
4279
+ ;
4280
+ this.imageData = imageData;
4281
+ this.data32 = imageDataToUInt32Array(imageData);
4282
+ this.width = imageData.width;
4283
+ this.height = imageData.height;
3299
4284
  }
3300
- }
4285
+ // should only be used for debug and testing
4286
+ copy() {
4287
+ const data = this.imageData.data;
4288
+ const buffer = new Uint8ClampedArray(data);
4289
+ const Ctor = this.imageData.constructor;
4290
+ const isCtorValid = typeof Ctor === "function";
4291
+ let newImageData;
4292
+ if (isCtorValid && Ctor !== Object) {
4293
+ const ImageConstructor = Ctor;
4294
+ newImageData = new ImageConstructor(buffer, this.width, this.height);
4295
+ } else {
4296
+ newImageData = {
4297
+ width: this.width,
4298
+ height: this.height,
4299
+ data: buffer
4300
+ };
4301
+ }
4302
+ return new _PixelData(newImageData);
4303
+ }
4304
+ };
3301
4305
 
3302
- // src/Mask/mergeMasks.ts
3303
- function mergeMasks(dst, dstWidth, src, opts) {
4306
+ // src/PixelData/blendPixelDataAlphaMask.ts
4307
+ function blendPixelDataAlphaMask(dst, src, alphaMask, opts = {}) {
3304
4308
  const {
3305
4309
  x: targetX = 0,
3306
4310
  y: targetY = 0,
3307
- w: width = 0,
3308
- h: height = 0,
4311
+ sx: sourceX = 0,
4312
+ sy: sourceY = 0,
4313
+ w: width = src.width,
4314
+ h: height = src.height,
3309
4315
  alpha: globalAlpha = 255,
3310
- maskType = 0 /* ALPHA */,
3311
- mw,
4316
+ blendFn = sourceOverPerfect,
4317
+ mw = src.width,
3312
4318
  mx = 0,
3313
4319
  my = 0,
3314
4320
  invertMask = false
3315
4321
  } = opts;
3316
- if (width <= 0 || height <= 0 || globalAlpha === 0) {
3317
- return;
4322
+ if (globalAlpha === 0) return;
4323
+ let x = targetX;
4324
+ let y = targetY;
4325
+ let sx = sourceX;
4326
+ let sy = sourceY;
4327
+ let w = width;
4328
+ let h = height;
4329
+ if (sx < 0) {
4330
+ x -= sx;
4331
+ w += sx;
4332
+ sx = 0;
3318
4333
  }
3319
- const sPitch = mw ?? width;
3320
- const isAlpha = maskType === 0 /* ALPHA */;
3321
- for (let iy = 0; iy < height; iy++) {
3322
- const dy = targetY + iy;
3323
- const sy = my + iy;
3324
- if (dy < 0 || sy < 0) {
3325
- continue;
3326
- }
3327
- for (let ix = 0; ix < width; ix++) {
3328
- const dx = targetX + ix;
3329
- const sx = mx + ix;
3330
- if (dx < 0 || dx >= dstWidth || sx < 0 || sx >= sPitch) {
4334
+ if (sy < 0) {
4335
+ y -= sy;
4336
+ h += sy;
4337
+ sy = 0;
4338
+ }
4339
+ w = Math.min(w, src.width - sx);
4340
+ h = Math.min(h, src.height - sy);
4341
+ if (x < 0) {
4342
+ sx -= x;
4343
+ w += x;
4344
+ x = 0;
4345
+ }
4346
+ if (y < 0) {
4347
+ sy -= y;
4348
+ h += y;
4349
+ y = 0;
4350
+ }
4351
+ const actualW = Math.min(w, dst.width - x);
4352
+ const actualH = Math.min(h, dst.height - y);
4353
+ if (actualW <= 0 || actualH <= 0) return;
4354
+ const dw = dst.width;
4355
+ const sw = src.width;
4356
+ const mPitch = mw;
4357
+ const dx = x - targetX | 0;
4358
+ const dy = y - targetY | 0;
4359
+ const dst32 = dst.data32;
4360
+ const src32 = src.data32;
4361
+ let dIdx = y * dw + x | 0;
4362
+ let sIdx = sy * sw + sx | 0;
4363
+ let mIdx = (my + dy) * mPitch + (mx + dx) | 0;
4364
+ const dStride = dw - actualW | 0;
4365
+ const sStride = sw - actualW | 0;
4366
+ const mStride = mPitch - actualW | 0;
4367
+ const isOpaque = globalAlpha === 255;
4368
+ const isOverwrite = blendFn.isOverwrite || false;
4369
+ for (let iy = 0; iy < actualH; iy++) {
4370
+ for (let ix = 0; ix < actualW; ix++) {
4371
+ const mVal = alphaMask[mIdx];
4372
+ const effM = invertMask ? 255 - mVal : mVal;
4373
+ if (effM === 0) {
4374
+ dIdx++;
4375
+ sIdx++;
4376
+ mIdx++;
4377
+ continue;
4378
+ }
4379
+ const srcCol = src32[sIdx];
4380
+ const srcAlpha = srcCol >>> 24;
4381
+ if (srcAlpha === 0 && !isOverwrite) {
4382
+ dIdx++;
4383
+ sIdx++;
4384
+ mIdx++;
3331
4385
  continue;
3332
4386
  }
3333
- const dIdx = dy * dstWidth + dx;
3334
- const sIdx = sy * sPitch + sx;
3335
- const mVal = src[sIdx];
3336
4387
  let weight = globalAlpha;
3337
- if (isAlpha) {
3338
- const effectiveM = invertMask ? 255 - mVal : mVal;
3339
- if (effectiveM === 0) {
3340
- dst[dIdx] = 0;
3341
- continue;
3342
- }
3343
- weight = globalAlpha === 255 ? effectiveM : effectiveM * globalAlpha + 128 >> 8;
3344
- } else {
3345
- const isHit = invertMask ? mVal === 0 : mVal === 1;
3346
- if (!isHit) {
3347
- dst[dIdx] = 0;
4388
+ if (isOpaque) {
4389
+ weight = effM;
4390
+ } else if (effM !== 255) {
4391
+ weight = effM * globalAlpha + 128 >> 8;
4392
+ }
4393
+ if (weight === 0) {
4394
+ dIdx++;
4395
+ sIdx++;
4396
+ mIdx++;
4397
+ continue;
4398
+ }
4399
+ let finalCol = srcCol;
4400
+ if (weight < 255) {
4401
+ const a = srcAlpha * weight + 128 >> 8;
4402
+ if (a === 0 && !isOverwrite) {
4403
+ dIdx++;
4404
+ sIdx++;
4405
+ mIdx++;
3348
4406
  continue;
3349
4407
  }
3350
- weight = globalAlpha;
4408
+ finalCol = (srcCol & 16777215 | a << 24) >>> 0;
3351
4409
  }
3352
- if (weight === 0) {
3353
- dst[dIdx] = 0;
4410
+ dst32[dIdx] = blendFn(finalCol, dst32[dIdx]);
4411
+ dIdx++;
4412
+ sIdx++;
4413
+ mIdx++;
4414
+ }
4415
+ dIdx += dStride;
4416
+ sIdx += sStride;
4417
+ mIdx += mStride;
4418
+ }
4419
+ }
4420
+
4421
+ // src/PixelData/blendPixelDataBinaryMask.ts
4422
+ function blendPixelDataBinaryMask(dst, src, binaryMask, opts) {
4423
+ const {
4424
+ x: targetX = 0,
4425
+ y: targetY = 0,
4426
+ sx: sourceX = 0,
4427
+ sy: sourceY = 0,
4428
+ w: width = src.width,
4429
+ h: height = src.height,
4430
+ alpha: globalAlpha = 255,
4431
+ blendFn = sourceOverPerfect,
4432
+ mw = src.width,
4433
+ mx = 0,
4434
+ my = 0,
4435
+ invertMask = false
4436
+ } = opts;
4437
+ if (globalAlpha === 0) return;
4438
+ let x = targetX;
4439
+ let y = targetY;
4440
+ let sx = sourceX;
4441
+ let sy = sourceY;
4442
+ let w = width;
4443
+ let h = height;
4444
+ if (sx < 0) {
4445
+ x -= sx;
4446
+ w += sx;
4447
+ sx = 0;
4448
+ }
4449
+ if (sy < 0) {
4450
+ y -= sy;
4451
+ h += sy;
4452
+ sy = 0;
4453
+ }
4454
+ w = Math.min(w, src.width - sx);
4455
+ h = Math.min(h, src.height - sy);
4456
+ if (x < 0) {
4457
+ sx -= x;
4458
+ w += x;
4459
+ x = 0;
4460
+ }
4461
+ if (y < 0) {
4462
+ sy -= y;
4463
+ h += y;
4464
+ y = 0;
4465
+ }
4466
+ const actualW = Math.min(w, dst.width - x);
4467
+ const actualH = Math.min(h, dst.height - y);
4468
+ if (actualW <= 0 || actualH <= 0) return;
4469
+ const dx = x - targetX | 0;
4470
+ const dy = y - targetY | 0;
4471
+ const dst32 = dst.data32;
4472
+ const src32 = src.data32;
4473
+ const dw = dst.width;
4474
+ const sw = src.width;
4475
+ const mPitch = mw;
4476
+ let dIdx = y * dw + x | 0;
4477
+ let sIdx = sy * sw + sx | 0;
4478
+ let mIdx = (my + dy) * mPitch + (mx + dx) | 0;
4479
+ const dStride = dw - actualW | 0;
4480
+ const sStride = sw - actualW | 0;
4481
+ const mStride = mPitch - actualW | 0;
4482
+ const skipVal = invertMask ? 1 : 0;
4483
+ const isOpaque = globalAlpha === 255;
4484
+ const isOverwrite = blendFn.isOverwrite || false;
4485
+ for (let iy = 0; iy < actualH; iy++) {
4486
+ for (let ix = 0; ix < actualW; ix++) {
4487
+ if (binaryMask[mIdx] === skipVal) {
4488
+ dIdx++;
4489
+ sIdx++;
4490
+ mIdx++;
3354
4491
  continue;
3355
4492
  }
3356
- const da = dst[dIdx];
3357
- if (da === 0) {
3358
- } else if (weight === 255) {
3359
- } else if (da === 255) {
3360
- dst[dIdx] = weight;
3361
- } else {
3362
- dst[dIdx] = da * weight + 128 >> 8;
4493
+ const srcCol = src32[sIdx];
4494
+ const srcAlpha = srcCol >>> 24;
4495
+ if (srcAlpha === 0 && !isOverwrite) {
4496
+ dIdx++;
4497
+ sIdx++;
4498
+ mIdx++;
4499
+ continue;
4500
+ }
4501
+ let finalCol = srcCol;
4502
+ if (!isOpaque) {
4503
+ const a = srcAlpha * globalAlpha + 128 >> 8;
4504
+ if (a === 0 && !isOverwrite) {
4505
+ dIdx++;
4506
+ sIdx++;
4507
+ mIdx++;
4508
+ continue;
4509
+ }
4510
+ finalCol = (srcCol & 16777215 | a << 24) >>> 0;
3363
4511
  }
4512
+ dst32[dIdx] = blendFn(finalCol, dst32[dIdx]);
4513
+ dIdx++;
4514
+ sIdx++;
4515
+ mIdx++;
3364
4516
  }
4517
+ dIdx += dStride;
4518
+ sIdx += sStride;
4519
+ mIdx += mStride;
3365
4520
  }
3366
4521
  }
3367
4522
 
@@ -3371,32 +4526,40 @@ function clearPixelData(dst, rect) {
3371
4526
  }
3372
4527
 
3373
4528
  // src/PixelData/extractPixelDataBuffer.ts
4529
+ var SCRATCH_BLIT4 = makeClippedBlit();
3374
4530
  function extractPixelDataBuffer(source, _x, _y, _w, _h) {
3375
- const { x, y, w, h } = typeof _x === "object" ? _x : { x: _x, y: _y, w: _w, h: _h };
4531
+ const {
4532
+ x,
4533
+ y,
4534
+ w,
4535
+ h
4536
+ } = typeof _x === "object" ? _x : {
4537
+ x: _x,
4538
+ y: _y,
4539
+ w: _w,
4540
+ h: _h
4541
+ };
3376
4542
  const srcW = source.width;
3377
4543
  const srcH = source.height;
3378
4544
  const srcData = source.data32;
3379
4545
  if (w <= 0 || h <= 0) {
3380
4546
  return new Uint32Array(0);
3381
4547
  }
3382
- const dstImageData = new ImageData(w, h);
3383
- const dstData = new Uint32Array(dstImageData.data.buffer);
3384
- const x0 = Math.max(0, x);
3385
- const y0 = Math.max(0, y);
3386
- const x1 = Math.min(srcW, x + w);
3387
- const y1 = Math.min(srcH, y + h);
3388
- if (x1 <= x0 || y1 <= y0) {
3389
- return dstData;
3390
- }
3391
- const copyWidth = x1 - x0;
3392
- const copyHeight = y1 - y0;
3393
- for (let row = 0; row < copyHeight; row++) {
3394
- const srcRow = y0 + row;
3395
- const srcStart = srcRow * srcW + x0;
3396
- const dstRow = y0 - y + row;
3397
- const dstCol = x0 - x;
3398
- const dstStart = dstRow * w + dstCol;
3399
- const chunk = srcData.subarray(srcStart, srcStart + copyWidth);
4548
+ const dstData = new Uint32Array(w * h);
4549
+ const clip = resolveBlitClipping(0, 0, x, y, w, h, w, h, srcW, srcH, SCRATCH_BLIT4);
4550
+ if (!clip.inBounds) return dstData;
4551
+ const {
4552
+ x: dstX,
4553
+ y: dstY,
4554
+ sx: srcX,
4555
+ sy: srcY,
4556
+ w: copyW,
4557
+ h: copyH
4558
+ } = clip;
4559
+ for (let row = 0; row < copyH; row++) {
4560
+ const srcStart = (srcY + row) * srcW + srcX;
4561
+ const dstStart = (dstY + row) * w + dstX;
4562
+ const chunk = srcData.subarray(srcStart, srcStart + copyW);
3400
4563
  dstData.set(chunk, dstStart);
3401
4564
  }
3402
4565
  return dstData;
@@ -3404,13 +4567,59 @@ function extractPixelDataBuffer(source, _x, _y, _w, _h) {
3404
4567
 
3405
4568
  // src/PixelData/extractPixelData.ts
3406
4569
  function extractPixelData(source, _x, _y, _w, _h) {
3407
- const { x, y, w, h } = typeof _x === "object" ? _x : { x: _x, y: _y, w: _w, h: _h };
4570
+ const {
4571
+ x,
4572
+ y,
4573
+ w,
4574
+ h
4575
+ } = typeof _x === "object" ? _x : {
4576
+ x: _x,
4577
+ y: _y,
4578
+ w: _w,
4579
+ h: _h
4580
+ };
3408
4581
  const result = new PixelData(new ImageData(w, h));
3409
4582
  const buffer = extractPixelDataBuffer(source, x, y, w, h);
3410
4583
  result.data32.set(buffer);
3411
4584
  return result;
3412
4585
  }
3413
4586
 
4587
+ // src/PixelData/PixelBuffer32.ts
4588
+ var PixelBuffer32 = class _PixelBuffer32 {
4589
+ constructor(width, height, data32) {
4590
+ this.width = width;
4591
+ this.height = height;
4592
+ this.data32 = data32 ?? new Uint32Array(width * height);
4593
+ }
4594
+ data32;
4595
+ set(width, height, data32) {
4596
+ ;
4597
+ this.data32 = data32 ?? new Uint32Array(width * height);
4598
+ this.width = width;
4599
+ this.height = height;
4600
+ }
4601
+ copy() {
4602
+ const newData32 = new Uint32Array(this.data32);
4603
+ return new _PixelBuffer32(this.width, this.height, newData32);
4604
+ }
4605
+ };
4606
+
4607
+ // src/PixelData/pixelDataToAlphaMask.ts
4608
+ function pixelDataToAlphaMask(pixelData) {
4609
+ const {
4610
+ data32,
4611
+ width,
4612
+ height
4613
+ } = pixelData;
4614
+ const len = data32.length;
4615
+ const mask = new Uint8Array(width * height);
4616
+ for (let i = 0; i < len; i++) {
4617
+ const val = data32[i];
4618
+ mask[i] = val >>> 24 & 255;
4619
+ }
4620
+ return mask;
4621
+ }
4622
+
3414
4623
  // src/PixelData/reflectPixelData.ts
3415
4624
  function reflectPixelDataHorizontal(pixelData) {
3416
4625
  const width = pixelData.width;
@@ -3448,12 +4657,12 @@ function reflectPixelDataVertical(pixelData) {
3448
4657
 
3449
4658
  // src/PixelData/resamplePixelData.ts
3450
4659
  function resamplePixelData(pixelData, factor) {
3451
- const { data, width, height } = resample32(pixelData.data32, pixelData.width, pixelData.height, factor);
3452
- return new PixelData(new ImageData(
3453
- new Uint8ClampedArray(data.buffer),
4660
+ const {
4661
+ data,
3454
4662
  width,
3455
4663
  height
3456
- ));
4664
+ } = resample32(pixelData.data32, pixelData.width, pixelData.height, factor);
4665
+ return new PixelData(new ImageData(new Uint8ClampedArray(data.buffer), width, height));
3457
4666
  }
3458
4667
 
3459
4668
  // src/PixelData/rotatePixelData.ts
@@ -3477,11 +4686,7 @@ function rotatePixelData(pixelData) {
3477
4686
  newData32[newIdx] = data[oldIdx];
3478
4687
  }
3479
4688
  }
3480
- const newImageData = new ImageData(
3481
- new Uint8ClampedArray(newData32.buffer),
3482
- newWidth,
3483
- newHeight
3484
- );
4689
+ const newImageData = new ImageData(new Uint8ClampedArray(newData32.buffer), newWidth, newHeight);
3485
4690
  pixelData.set(newImageData);
3486
4691
  }
3487
4692
  function rotateSquareInPlace(pixelData) {
@@ -3503,8 +4708,14 @@ function rotateSquareInPlace(pixelData) {
3503
4708
  }
3504
4709
 
3505
4710
  // src/PixelData/writePixelDataBuffer.ts
4711
+ var SCRATCH_BLIT5 = makeClippedBlit();
3506
4712
  function writePixelDataBuffer(target, data, _x, _y, _w, _h) {
3507
- const { x, y, w, h } = typeof _x === "object" ? _x : {
4713
+ const {
4714
+ x,
4715
+ y,
4716
+ w,
4717
+ h
4718
+ } = typeof _x === "object" ? _x : {
3508
4719
  x: _x,
3509
4720
  y: _y,
3510
4721
  w: _w,
@@ -3513,22 +4724,20 @@ function writePixelDataBuffer(target, data, _x, _y, _w, _h) {
3513
4724
  const dstW = target.width;
3514
4725
  const dstH = target.height;
3515
4726
  const dstData = target.data32;
3516
- const x0 = Math.max(0, x);
3517
- const y0 = Math.max(0, y);
3518
- const x1 = Math.min(dstW, x + w);
3519
- const y1 = Math.min(dstH, y + h);
3520
- if (x1 <= x0 || y1 <= y0) {
3521
- return;
3522
- }
3523
- const rowLen = x1 - x0;
3524
- const srcCol = x0 - x;
3525
- const srcYOffset = y0 - y;
3526
- const actualH = y1 - y0;
3527
- for (let row = 0; row < actualH; row++) {
3528
- const dstStart = (y0 + row) * dstW + x0;
3529
- const srcRow = srcYOffset + row;
3530
- const srcStart = srcRow * w + srcCol;
3531
- dstData.set(data.subarray(srcStart, srcStart + rowLen), dstStart);
4727
+ const clip = resolveBlitClipping(x, y, 0, 0, w, h, dstW, dstH, w, h, SCRATCH_BLIT5);
4728
+ if (!clip.inBounds) return;
4729
+ const {
4730
+ x: dstX,
4731
+ y: dstY,
4732
+ sx: srcX,
4733
+ sy: srcY,
4734
+ w: copyW,
4735
+ h: copyH
4736
+ } = clip;
4737
+ for (let row = 0; row < copyH; row++) {
4738
+ const dstStart = (dstY + row) * dstW + dstX;
4739
+ const srcStart = (srcY + row) * w + srcX;
4740
+ dstData.set(data.subarray(srcStart, srcStart + copyW), dstStart);
3532
4741
  }
3533
4742
  }
3534
4743
  // Annotate the CommonJS export names for ESM import in node:
@@ -3536,23 +4745,32 @@ function writePixelDataBuffer(target, data, _x, _y, _w, _h) {
3536
4745
  BASE_FAST_BLEND_MODE_FUNCTIONS,
3537
4746
  BASE_PERFECT_BLEND_MODE_FUNCTIONS,
3538
4747
  BaseBlendMode,
4748
+ CANVAS_CTX_FAILED,
3539
4749
  HistoryManager,
3540
4750
  IndexedImage,
3541
4751
  MaskType,
4752
+ OFFSCREEN_CANVAS_CTX_FAILED,
3542
4753
  PixelAccumulator,
4754
+ PixelBuffer32,
3543
4755
  PixelData,
3544
4756
  PixelEngineConfig,
3545
4757
  PixelTile,
3546
4758
  PixelWriter,
3547
4759
  UnsupportedFormatError,
4760
+ applyAlphaMaskToPixelData,
4761
+ applyBinaryMaskToAlphaMask,
4762
+ applyBinaryMaskToPixelData,
3548
4763
  applyCircleBrushToPixelData,
3549
- applyMaskToPixelData,
3550
4764
  applyPatchTiles,
3551
4765
  applyRectBrushToPixelData,
3552
4766
  base64DecodeArrayBuffer,
3553
4767
  base64EncodeArrayBuffer,
3554
4768
  blendColorPixelData,
4769
+ blendColorPixelDataAlphaMask,
4770
+ blendColorPixelDataBinaryMask,
3555
4771
  blendPixelData,
4772
+ blendPixelDataAlphaMask,
4773
+ blendPixelDataBinaryMask,
3556
4774
  clearPixelData,
3557
4775
  color32ToCssRGBA,
3558
4776
  color32ToHex,
@@ -3585,10 +4803,13 @@ function writePixelDataBuffer(target, data, _x, _y, _w, _h) {
3585
4803
  fileToImageData,
3586
4804
  fillPixelData,
3587
4805
  floodFillSelection,
3588
- getCircleBrushBounds,
4806
+ forEachLinePoint,
4807
+ getCircleBrushOrPencilBounds,
4808
+ getCircleBrushOrPencilStrokeBounds,
3589
4809
  getImageDataFromClipboard,
3590
4810
  getIndexedImageColorCounts,
3591
- getRectBrushBounds,
4811
+ getRectBrushOrPencilBounds,
4812
+ getRectBrushOrPencilStrokeBounds,
3592
4813
  getSupportedPixelFormats,
3593
4814
  hardLightFast,
3594
4815
  hardLightPerfect,
@@ -3620,17 +4841,29 @@ function writePixelDataBuffer(target, data, _x, _y, _w, _h) {
3620
4841
  makeBlendModeRegistry,
3621
4842
  makeFastBlendModeRegistry,
3622
4843
  makeFullPixelMutator,
4844
+ makeImageDataLike,
3623
4845
  makePerfectBlendModeRegistry,
3624
4846
  makePixelCanvas,
3625
4847
  makeReusableCanvas,
3626
4848
  makeReusableImageData,
3627
- mergeMasks,
4849
+ mergeAlphaMasks,
4850
+ mergeBinaryMasks,
3628
4851
  multiplyFast,
3629
4852
  multiplyPerfect,
3630
- mutatorApplyMask,
4853
+ mutatorApplyAlphaMask,
4854
+ mutatorApplyBinaryMask,
4855
+ mutatorApplyCircleBrush,
4856
+ mutatorApplyCircleBrushStroke,
4857
+ mutatorApplyCirclePencil,
4858
+ mutatorApplyCirclePencilStroke,
4859
+ mutatorApplyRectBrush,
4860
+ mutatorApplyRectBrushStroke,
4861
+ mutatorApplyRectPencil,
4862
+ mutatorApplyRectPencilStroke,
3631
4863
  mutatorBlendColor,
3632
4864
  mutatorBlendPixel,
3633
4865
  mutatorBlendPixelData,
4866
+ mutatorClear,
3634
4867
  mutatorFill,
3635
4868
  mutatorInvert,
3636
4869
  overlayFast,
@@ -3660,7 +4893,10 @@ function writePixelDataBuffer(target, data, _x, _y, _w, _h) {
3660
4893
  sourceOverPerfect,
3661
4894
  subtractFast,
3662
4895
  subtractPerfect,
4896
+ toBlendModeIndexAndName,
3663
4897
  trimRectBounds,
4898
+ uInt32ArrayToImageData,
4899
+ uInt32ArrayToImageDataLike,
3664
4900
  unpackAlpha,
3665
4901
  unpackBlue,
3666
4902
  unpackColor,