pixel-data-js 0.25.2 → 0.26.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/dist/index.prod.cjs +1696 -1526
  2. package/dist/index.prod.cjs.map +1 -1
  3. package/dist/index.prod.d.ts +414 -311
  4. package/dist/index.prod.js +1676 -1519
  5. package/dist/index.prod.js.map +1 -1
  6. package/package.json +8 -9
  7. package/src/Algorithm/floodFillSelection.ts +49 -80
  8. package/src/Canvas/PixelCanvas.ts +1 -1
  9. package/src/Canvas/ReusableCanvas.ts +1 -1
  10. package/src/History/HistoryAction.ts +6 -5
  11. package/src/History/PixelMutator/mutatorApplyAlphaMask.ts +8 -10
  12. package/src/History/PixelMutator/mutatorApplyBinaryMask.ts +8 -10
  13. package/src/History/PixelMutator/mutatorApplyMask.ts +39 -0
  14. package/src/History/PixelMutator/{mutatorBlendPixelDataAlphaMask.ts → mutatorBlendAlphaMask.ts} +9 -9
  15. package/src/History/PixelMutator/{mutatorBlendPixelDataBinaryMask.ts → mutatorBlendBinaryMask.ts} +9 -9
  16. package/src/History/PixelMutator/mutatorBlendColor.ts +8 -9
  17. package/src/History/PixelMutator/mutatorBlendColorPaintAlphaMask.ts +51 -0
  18. package/src/History/PixelMutator/mutatorBlendColorPaintBinaryMask.ts +51 -0
  19. package/src/History/PixelMutator/{mutatorBlendPaintMask.ts → mutatorBlendColorPaintMask.ts} +3 -3
  20. package/src/History/PixelMutator/mutatorBlendMask.ts +43 -0
  21. package/src/History/PixelMutator/mutatorBlendPaintRect.ts +55 -0
  22. package/src/History/PixelMutator/mutatorBlendPixel.ts +2 -2
  23. package/src/History/PixelMutator/mutatorBlendPixelData.ts +8 -9
  24. package/src/History/PixelMutator/mutatorClear.ts +13 -11
  25. package/src/History/PixelMutator/mutatorFill.ts +2 -2
  26. package/src/History/PixelMutator/mutatorFillBinaryMask.ts +3 -4
  27. package/src/History/PixelMutator/mutatorInvert.ts +8 -9
  28. package/src/History/PixelMutator.ts +20 -4
  29. package/src/History/PixelWriter.ts +11 -13
  30. package/src/Input/fileToImageData.ts +1 -1
  31. package/src/Internal/helpers.ts +4 -1
  32. package/src/Mask/applyBinaryMaskToAlphaMask.ts +8 -10
  33. package/src/Paint/PaintBuffer.ts +3 -3
  34. package/src/Paint/PaintBufferCanvasRenderer.ts +1 -1
  35. package/src/Paint/makePaintMask.ts +5 -5
  36. package/src/Paint/makeRectFalloffPaintAlphaMask.ts +3 -3
  37. package/src/PixelData/applyAlphaMaskToPixelData.ts +14 -16
  38. package/src/PixelData/applyBinaryMaskToPixelData.ts +14 -16
  39. package/src/PixelData/applyMaskToPixelData.ts +22 -0
  40. package/src/PixelData/blendColorPixelData.ts +12 -15
  41. package/src/PixelData/blendColorPixelDataAlphaMask.ts +16 -16
  42. package/src/PixelData/blendColorPixelDataBinaryMask.ts +16 -16
  43. package/src/PixelData/blendColorPixelDataMask.ts +16 -0
  44. package/src/PixelData/blendColorPixelDataPaintAlphaMask.ts +30 -0
  45. package/src/PixelData/blendColorPixelDataPaintBinaryMask.ts +30 -0
  46. package/src/PixelData/blendColorPixelDataPaintMask.ts +35 -0
  47. package/src/PixelData/blendPixelData.ts +14 -16
  48. package/src/PixelData/blendPixelDataAlphaMask.ts +17 -19
  49. package/src/PixelData/blendPixelDataBinaryMask.ts +17 -19
  50. package/src/PixelData/blendPixelDataMask.ts +16 -0
  51. package/src/PixelData/blendPixelDataPaintBuffer.ts +1 -1
  52. package/src/PixelData/{clearPixelData.ts → clearPixelDataFast.ts} +2 -2
  53. package/src/PixelData/fillPixelDataBinaryMask.ts +8 -22
  54. package/src/PixelData/fillPixelDataFast.ts +2 -2
  55. package/src/PixelData/invertPixelData.ts +13 -16
  56. package/src/_types.ts +8 -7
  57. package/src/index.ts +41 -31
  58. package/dist/index.dev.cjs +0 -5419
  59. package/dist/index.dev.cjs.map +0 -1
  60. package/dist/index.dev.js +0 -5208
  61. package/dist/index.dev.js.map +0 -1
  62. package/src/Canvas/_constants.ts +0 -2
  63. package/src/PixelData/PixelBuffer32.ts +0 -28
@@ -24,14 +24,11 @@ __export(src_exports, {
24
24
  BASE_PERFECT_BLEND_MODE_FUNCTIONS: () => BASE_PERFECT_BLEND_MODE_FUNCTIONS,
25
25
  BaseBlendMode: () => BaseBlendMode,
26
26
  CANVAS_COMPOSITE_MAP: () => CANVAS_COMPOSITE_MAP,
27
- CANVAS_CTX_FAILED: () => CANVAS_CTX_FAILED,
28
27
  HistoryManager: () => HistoryManager,
29
28
  IndexedImage: () => IndexedImage,
30
29
  MaskType: () => MaskType,
31
- OFFSCREEN_CANVAS_CTX_FAILED: () => OFFSCREEN_CANVAS_CTX_FAILED,
32
30
  PaintBuffer: () => PaintBuffer,
33
31
  PixelAccumulator: () => PixelAccumulator,
34
- PixelBuffer32: () => PixelBuffer32,
35
32
  PixelData: () => PixelData,
36
33
  PixelEngineConfig: () => PixelEngineConfig,
37
34
  PixelTile: () => PixelTile,
@@ -41,18 +38,24 @@ __export(src_exports, {
41
38
  applyAlphaMaskToPixelData: () => applyAlphaMaskToPixelData,
42
39
  applyBinaryMaskToAlphaMask: () => applyBinaryMaskToAlphaMask,
43
40
  applyBinaryMaskToPixelData: () => applyBinaryMaskToPixelData,
41
+ applyMaskToPixelData: () => applyMaskToPixelData,
44
42
  applyPatchTiles: () => applyPatchTiles,
45
43
  base64DecodeArrayBuffer: () => base64DecodeArrayBuffer,
46
44
  base64EncodeArrayBuffer: () => base64EncodeArrayBuffer,
47
45
  blendColorPixelData: () => blendColorPixelData,
48
46
  blendColorPixelDataAlphaMask: () => blendColorPixelDataAlphaMask,
49
47
  blendColorPixelDataBinaryMask: () => blendColorPixelDataBinaryMask,
48
+ blendColorPixelDataMask: () => blendColorPixelDataMask,
49
+ blendColorPixelDataPaintAlphaMask: () => blendColorPixelDataPaintAlphaMask,
50
+ blendColorPixelDataPaintBinaryMask: () => blendColorPixelDataPaintBinaryMask,
51
+ blendColorPixelDataPaintMask: () => blendColorPixelDataPaintMask,
50
52
  blendPixel: () => blendPixel,
51
53
  blendPixelData: () => blendPixelData,
52
54
  blendPixelDataAlphaMask: () => blendPixelDataAlphaMask,
53
55
  blendPixelDataBinaryMask: () => blendPixelDataBinaryMask,
56
+ blendPixelDataMask: () => blendPixelDataMask,
54
57
  blendPixelDataPaintBuffer: () => blendPixelDataPaintBuffer,
55
- clearPixelData: () => clearPixelData,
58
+ clearPixelDataFast: () => clearPixelDataFast,
56
59
  color32ToCssRGBA: () => color32ToCssRGBA,
57
60
  color32ToHex: () => color32ToHex,
58
61
  colorBurnFast: () => colorBurnFast,
@@ -125,6 +128,8 @@ __export(src_exports, {
125
128
  makeCanvasFrameRenderer: () => makeCanvasFrameRenderer,
126
129
  makeCirclePaintAlphaMask: () => makeCirclePaintAlphaMask,
127
130
  makeCirclePaintBinaryMask: () => makeCirclePaintBinaryMask,
131
+ makeClippedBlit: () => makeClippedBlit,
132
+ makeClippedRect: () => makeClippedRect,
128
133
  makeFastBlendModeRegistry: () => makeFastBlendModeRegistry,
129
134
  makeFullPixelMutator: () => makeFullPixelMutator,
130
135
  makeHistoryAction: () => makeHistoryAction,
@@ -146,12 +151,17 @@ __export(src_exports, {
146
151
  multiplyPerfect: () => multiplyPerfect,
147
152
  mutatorApplyAlphaMask: () => mutatorApplyAlphaMask,
148
153
  mutatorApplyBinaryMask: () => mutatorApplyBinaryMask,
154
+ mutatorApplyMask: () => mutatorApplyMask,
155
+ mutatorBlendAlphaMask: () => mutatorBlendAlphaMask,
156
+ mutatorBlendBinaryMask: () => mutatorBlendBinaryMask,
149
157
  mutatorBlendColor: () => mutatorBlendColor,
150
- mutatorBlendPaintMask: () => mutatorBlendPaintMask,
158
+ mutatorBlendColorPaintAlphaMask: () => mutatorBlendColorPaintAlphaMask,
159
+ mutatorBlendColorPaintBinaryMask: () => mutatorBlendColorPaintBinaryMask,
160
+ mutatorBlendColorPaintMask: () => mutatorBlendColorPaintMask,
161
+ mutatorBlendMask: () => mutatorBlendMask,
162
+ mutatorBlendPaintRect: () => mutatorBlendPaintRect,
151
163
  mutatorBlendPixel: () => mutatorBlendPixel,
152
164
  mutatorBlendPixelData: () => mutatorBlendPixelData,
153
- mutatorBlendPixelDataAlphaMask: () => mutatorBlendPixelDataAlphaMask,
154
- mutatorBlendPixelDataBinaryMask: () => mutatorBlendPixelDataBinaryMask,
155
165
  mutatorClear: () => mutatorClear,
156
166
  mutatorFill: () => mutatorFill,
157
167
  mutatorFillBinaryMask: () => mutatorFillBinaryMask,
@@ -169,10 +179,13 @@ __export(src_exports, {
169
179
  pixelDataToAlphaMask: () => pixelDataToAlphaMask,
170
180
  reflectPixelDataHorizontal: () => reflectPixelDataHorizontal,
171
181
  reflectPixelDataVertical: () => reflectPixelDataVertical,
182
+ resample32: () => resample32,
172
183
  resampleImageData: () => resampleImageData,
173
184
  resampleIndexedImage: () => resampleIndexedImage,
174
185
  resamplePixelData: () => resamplePixelData,
175
186
  resizeImageData: () => resizeImageData,
187
+ resolveBlitClipping: () => resolveBlitClipping,
188
+ resolveRectClipping: () => resolveRectClipping,
176
189
  rotatePixelData: () => rotatePixelData,
177
190
  screenFast: () => screenFast,
178
191
  screenPerfect: () => screenPerfect,
@@ -507,37 +520,22 @@ function trimMaskRectBounds(target, bounds) {
507
520
  }
508
521
 
509
522
  // src/Algorithm/floodFillSelection.ts
510
- function floodFillSelection(img, startX, startY, {
511
- contiguous = true,
512
- tolerance = 0,
513
- bounds
514
- } = {}) {
515
- let imageData;
516
- let data32;
517
- if ("data32" in img) {
518
- data32 = img.data32;
519
- imageData = img.imageData;
520
- } else {
521
- data32 = new Uint32Array(img.data.buffer, img.data.byteOffset, img.data.byteLength >> 2);
522
- imageData = img;
523
- }
524
- const {
525
- width,
526
- height
527
- } = img;
528
- const limit = bounds || {
529
- x: 0,
530
- y: 0,
531
- w: width,
532
- h: height
533
- };
534
- const xMin = Math.max(0, limit.x);
535
- const xMax = Math.min(width - 1, limit.x + limit.w - 1);
536
- const yMin = Math.max(0, limit.y);
537
- const yMax = Math.min(height - 1, limit.y + limit.h - 1);
523
+ function floodFillSelection(target, startX, startY, contiguous = true, tolerance = 0, bounds, out) {
524
+ const data32 = target.data32;
525
+ const width = target.width;
526
+ const height = target.height;
527
+ const lx = bounds?.x ?? 0;
528
+ const ly = bounds?.y ?? 0;
529
+ const lw = bounds?.w ?? width;
530
+ const lh = bounds?.h ?? height;
531
+ const xMin = Math.max(0, lx);
532
+ const xMax = Math.min(width - 1, lx + lw - 1);
533
+ const yMin = Math.max(0, ly);
534
+ const yMax = Math.min(height - 1, ly + lh - 1);
538
535
  if (startX < xMin || startX > xMax || startY < yMin || startY > yMax) {
539
536
  return null;
540
537
  }
538
+ out = out ?? {};
541
539
  const baseColor = data32[startY * width + startX];
542
540
  let matchCount = 0;
543
541
  const matchX = new Uint16Array(width * height);
@@ -608,42 +606,33 @@ function floodFillSelection(img, startX, startY, {
608
606
  }
609
607
  }
610
608
  }
611
- if (matchCount === 0) {
612
- return null;
613
- }
609
+ if (matchCount === 0) return null;
614
610
  const w = maxX - minX + 1;
615
611
  const h = maxY - minY + 1;
616
- const selectionRect = {
617
- x: minX,
618
- y: minY,
619
- w,
620
- h,
621
- data: new Uint8Array(w * h),
622
- type: 1 /* BINARY */
623
- };
624
- const sw = selectionRect.w;
625
- const sh = selectionRect.h;
626
- const finalMask = selectionRect.data;
612
+ out.startX = startX;
613
+ out.startY = startY;
614
+ out.x = minX;
615
+ out.y = minY;
616
+ out.w = w;
617
+ out.h = h;
618
+ out.data = new Uint8Array(w * h);
619
+ out.type = 1 /* BINARY */;
620
+ const finalMask = out.data;
627
621
  for (let i = 0; i < matchCount; i++) {
628
- const mx = matchX[i] - selectionRect.x;
629
- const my = matchY[i] - selectionRect.y;
630
- if (mx >= 0 && mx < sw && my >= 0 && my < sh) {
631
- finalMask[my * sw + mx] = 1;
622
+ const mx = matchX[i] - minX;
623
+ const my = matchY[i] - minY;
624
+ if (mx >= 0 && mx < w && my >= 0 && my < h) {
625
+ finalMask[my * w + mx] = 1;
632
626
  }
633
627
  }
634
- trimMaskRectBounds(selectionRect, {
628
+ trimMaskRectBounds(out, {
635
629
  x: 0,
636
630
  y: 0,
637
631
  w: width,
638
632
  h: height
639
633
  });
640
- const extracted = extractImageDataBuffer(imageData, selectionRect.x, selectionRect.y, selectionRect.w, selectionRect.h);
641
- return {
642
- startX,
643
- startY,
644
- selectionRect,
645
- pixels: extracted
646
- };
634
+ out.pixels = extractImageDataBuffer(target.imageData, out.x, out.y, out.w, out.h);
635
+ return out;
647
636
  }
648
637
 
649
638
  // src/Algorithm/forEachLinePoint.ts
@@ -666,35 +655,6 @@ function forEachLinePoint(x0, y0, x1, y1, callback) {
666
655
  }
667
656
  }
668
657
 
669
- // src/BlendModes/blend-modes.ts
670
- var BaseBlendMode = {
671
- overwrite: 0,
672
- sourceOver: 1,
673
- darken: 2,
674
- multiply: 3,
675
- colorBurn: 4,
676
- linearBurn: 5,
677
- darkerColor: 6,
678
- lighten: 7,
679
- screen: 8,
680
- colorDodge: 9,
681
- linearDodge: 10,
682
- lighterColor: 11,
683
- overlay: 12,
684
- softLight: 13,
685
- hardLight: 14,
686
- vividLight: 15,
687
- linearLight: 16,
688
- pinLight: 17,
689
- hardMix: 18,
690
- difference: 19,
691
- exclusion: 20,
692
- subtract: 21,
693
- divide: 22
694
- };
695
- var overwriteBase = (src, _dst) => src;
696
- overwriteBase.isOverwrite = true;
697
-
698
658
  // src/BlendModes/BlendModeRegistry.ts
699
659
  function makeBlendModeRegistry(blendModes, initialEntries, registryName = "anonymous") {
700
660
  const blendToName = /* @__PURE__ */ new Map();
@@ -734,6 +694,35 @@ function makeBlendModeRegistry(blendModes, initialEntries, registryName = "anony
734
694
  };
735
695
  }
736
696
 
697
+ // src/BlendModes/blend-modes.ts
698
+ var BaseBlendMode = {
699
+ overwrite: 0,
700
+ sourceOver: 1,
701
+ darken: 2,
702
+ multiply: 3,
703
+ colorBurn: 4,
704
+ linearBurn: 5,
705
+ darkerColor: 6,
706
+ lighten: 7,
707
+ screen: 8,
708
+ colorDodge: 9,
709
+ linearDodge: 10,
710
+ lighterColor: 11,
711
+ overlay: 12,
712
+ softLight: 13,
713
+ hardLight: 14,
714
+ vividLight: 15,
715
+ linearLight: 16,
716
+ pinLight: 17,
717
+ hardMix: 18,
718
+ difference: 19,
719
+ exclusion: 20,
720
+ subtract: 21,
721
+ divide: 22
722
+ };
723
+ var overwriteBase = (src, _dst) => src;
724
+ overwriteBase.isOverwrite = true;
725
+
737
726
  // src/BlendModes/blend-modes-fast.ts
738
727
  var overwriteFast = overwriteBase;
739
728
  var sourceOverFast = (src, dst) => {
@@ -1739,28 +1728,10 @@ var getKeyByValue = (obj, value) => {
1739
1728
  }
1740
1729
  };
1741
1730
 
1742
- // src/Canvas/_constants.ts
1731
+ // support/error-strings.ts
1743
1732
  var OFFSCREEN_CANVAS_CTX_FAILED = "Failed to create OffscreenCanvas context";
1744
1733
  var CANVAS_CTX_FAILED = "Failed to create Canvas context";
1745
1734
 
1746
- // src/Canvas/canvas-blend-modes.ts
1747
- var CANVAS_COMPOSITE_MAP = {
1748
- [BaseBlendMode.overwrite]: "copy",
1749
- [BaseBlendMode.sourceOver]: "source-over",
1750
- [BaseBlendMode.darken]: "darken",
1751
- [BaseBlendMode.multiply]: "multiply",
1752
- [BaseBlendMode.colorBurn]: "color-burn",
1753
- [BaseBlendMode.lighten]: "lighten",
1754
- [BaseBlendMode.screen]: "screen",
1755
- [BaseBlendMode.colorDodge]: "color-dodge",
1756
- [BaseBlendMode.linearDodge]: "lighter",
1757
- [BaseBlendMode.overlay]: "overlay",
1758
- [BaseBlendMode.softLight]: "soft-light",
1759
- [BaseBlendMode.hardLight]: "hard-light",
1760
- [BaseBlendMode.difference]: "difference",
1761
- [BaseBlendMode.exclusion]: "exclusion"
1762
- };
1763
-
1764
1735
  // src/Canvas/ReusableCanvas.ts
1765
1736
  function makeReusableCanvas() {
1766
1737
  return makeReusableCanvasMeta((w, h) => {
@@ -1859,6 +1830,24 @@ function makePixelCanvas(canvas) {
1859
1830
  };
1860
1831
  }
1861
1832
 
1833
+ // src/Canvas/canvas-blend-modes.ts
1834
+ var CANVAS_COMPOSITE_MAP = {
1835
+ [BaseBlendMode.overwrite]: "copy",
1836
+ [BaseBlendMode.sourceOver]: "source-over",
1837
+ [BaseBlendMode.darken]: "darken",
1838
+ [BaseBlendMode.multiply]: "multiply",
1839
+ [BaseBlendMode.colorBurn]: "color-burn",
1840
+ [BaseBlendMode.lighten]: "lighten",
1841
+ [BaseBlendMode.screen]: "screen",
1842
+ [BaseBlendMode.colorDodge]: "color-dodge",
1843
+ [BaseBlendMode.linearDodge]: "lighter",
1844
+ [BaseBlendMode.overlay]: "overlay",
1845
+ [BaseBlendMode.softLight]: "soft-light",
1846
+ [BaseBlendMode.hardLight]: "hard-light",
1847
+ [BaseBlendMode.difference]: "difference",
1848
+ [BaseBlendMode.exclusion]: "exclusion"
1849
+ };
1850
+
1862
1851
  // src/ImageData/imgBlobToImageData.ts
1863
1852
  async function imgBlobToImageData(blob) {
1864
1853
  let bitmap = null;
@@ -1943,10 +1932,9 @@ function applyPatchTiles(target, tiles, tileSize) {
1943
1932
  }
1944
1933
 
1945
1934
  // src/History/HistoryAction.ts
1946
- function makeHistoryAction(writer, patch, after, afterUndo, afterRedo, applyPatchTilesFn = applyPatchTiles) {
1947
- const target = writer.config.target;
1948
- const tileSize = writer.config.tileSize;
1949
- const accumulator = writer.accumulator;
1935
+ function makeHistoryAction(config, accumulator, patch, after, afterUndo, afterRedo, applyPatchTilesFn = applyPatchTiles) {
1936
+ const target = config.target;
1937
+ const tileSize = config.tileSize;
1950
1938
  return {
1951
1939
  undo: () => {
1952
1940
  applyPatchTilesFn(target, patch.beforeTiles, tileSize);
@@ -2216,20 +2204,17 @@ var PixelEngineConfig = class {
2216
2204
  }
2217
2205
  };
2218
2206
 
2219
- // src/PixelData/blendColorPixelData.ts
2220
- function blendColorPixelData(dst, color, opts = {}) {
2221
- const {
2222
- x: targetX = 0,
2223
- y: targetY = 0,
2224
- w: width = dst.width,
2225
- h: height = dst.height,
2226
- alpha: globalAlpha = 255,
2227
- blendFn = sourceOverPerfect
2228
- } = opts;
2207
+ // src/PixelData/applyAlphaMaskToPixelData.ts
2208
+ function applyAlphaMaskToPixelData(target, mask, opts) {
2209
+ const targetX = opts?.x ?? 0;
2210
+ const targetY = opts?.y ?? 0;
2211
+ const width = opts?.w ?? target.width;
2212
+ const height = opts?.h ?? target.height;
2213
+ const globalAlpha = opts?.alpha ?? 255;
2214
+ const mx = opts?.mx ?? 0;
2215
+ const my = opts?.my ?? 0;
2216
+ const invertMask = opts?.invertMask ?? false;
2229
2217
  if (globalAlpha === 0) return false;
2230
- const baseSrcAlpha = color >>> 24;
2231
- const isOverwrite = blendFn.isOverwrite || false;
2232
- if (baseSrcAlpha === 0 && !isOverwrite) return false;
2233
2218
  let x = targetX;
2234
2219
  let y = targetY;
2235
2220
  let w = width;
@@ -2242,112 +2227,318 @@ function blendColorPixelData(dst, color, opts = {}) {
2242
2227
  h += y;
2243
2228
  y = 0;
2244
2229
  }
2245
- const actualW = Math.min(w, dst.width - x);
2246
- const actualH = Math.min(h, dst.height - y);
2247
- if (actualW <= 0 || actualH <= 0) return false;
2248
- let finalSrcColor = color;
2249
- if (globalAlpha < 255) {
2250
- const a = baseSrcAlpha * globalAlpha + 128 >> 8;
2251
- if (a === 0 && !isOverwrite) return false;
2252
- finalSrcColor = (color & 16777215 | a << 24) >>> 0;
2253
- }
2254
- const dst32 = dst.data32;
2255
- const dw = dst.width;
2256
- let dIdx = y * dw + x | 0;
2257
- const dStride = dw - actualW | 0;
2230
+ w = Math.min(w, target.width - x);
2231
+ h = Math.min(h, target.height - y);
2232
+ if (w <= 0) return false;
2233
+ if (h <= 0) return false;
2234
+ const mPitch = mask.w;
2235
+ if (mPitch <= 0) return false;
2236
+ const startX = mx + (x - targetX);
2237
+ const startY = my + (y - targetY);
2238
+ const sX0 = Math.max(0, startX);
2239
+ const sY0 = Math.max(0, startY);
2240
+ const sX1 = Math.min(mPitch, startX + w);
2241
+ const sY1 = Math.min(mask.h, startY + h);
2242
+ const finalW = sX1 - sX0;
2243
+ const finalH = sY1 - sY0;
2244
+ if (finalW <= 0) return false;
2245
+ if (finalH <= 0) return false;
2246
+ const xShift = sX0 - startX;
2247
+ const yShift = sY0 - startY;
2248
+ const dst32 = target.data32;
2249
+ const dw = target.width;
2250
+ const dStride = dw - finalW;
2251
+ const mStride = mPitch - finalW;
2252
+ const maskData = mask.data;
2253
+ let dIdx = (y + yShift) * dw + (x + xShift);
2254
+ let mIdx = sY0 * mPitch + sX0;
2258
2255
  let didChange = false;
2259
- for (let iy = 0; iy < actualH; iy++) {
2260
- for (let ix = 0; ix < actualW; ix++) {
2261
- const current = dst32[dIdx];
2262
- const next = blendFn(finalSrcColor, current);
2263
- if (current !== next) {
2264
- dst32[dIdx] = next;
2256
+ for (let iy = 0; iy < h; iy++) {
2257
+ for (let ix = 0; ix < w; ix++) {
2258
+ const mVal = maskData[mIdx];
2259
+ const effectiveM = invertMask ? 255 - mVal : mVal;
2260
+ let weight = 0;
2261
+ if (effectiveM === 0) {
2262
+ weight = 0;
2263
+ } else if (effectiveM === 255) {
2264
+ weight = globalAlpha;
2265
+ } else if (globalAlpha === 255) {
2266
+ weight = effectiveM;
2267
+ } else {
2268
+ weight = effectiveM * globalAlpha + 128 >> 8;
2269
+ }
2270
+ if (weight === 0) {
2271
+ dst32[dIdx] = (dst32[dIdx] & 16777215) >>> 0;
2265
2272
  didChange = true;
2273
+ } else if (weight !== 255) {
2274
+ const d = dst32[dIdx];
2275
+ const da = d >>> 24;
2276
+ if (da !== 0) {
2277
+ const finalAlpha = da === 255 ? weight : da * weight + 128 >> 8;
2278
+ const current = dst32[dIdx];
2279
+ const next = (d & 16777215 | finalAlpha << 24) >>> 0;
2280
+ if (current !== next) {
2281
+ dst32[dIdx] = next;
2282
+ didChange = true;
2283
+ }
2284
+ }
2266
2285
  }
2267
2286
  dIdx++;
2287
+ mIdx++;
2268
2288
  }
2269
2289
  dIdx += dStride;
2290
+ mIdx += mStride;
2270
2291
  }
2271
2292
  return didChange;
2272
2293
  }
2273
2294
 
2274
- // src/History/PixelMutator/mutatorBlendColor.ts
2275
- var defaults2 = {
2276
- blendColorPixelData
2277
- };
2278
- var mutatorBlendColor = ((writer, deps = defaults2) => {
2295
+ // src/ImageData/resizeImageData.ts
2296
+ function resizeImageData(target, newWidth, newHeight, offsetX = 0, offsetY = 0) {
2297
+ const result = new ImageData(newWidth, newHeight);
2279
2298
  const {
2280
- blendColorPixelData: blendColorPixelData2 = defaults2.blendColorPixelData
2281
- } = deps;
2282
- return {
2283
- blendColor(color, opts = {}) {
2284
- const target = writer.config.target;
2285
- const {
2286
- x = 0,
2287
- y = 0,
2288
- w = target.width,
2289
- h = target.height
2290
- } = opts;
2291
- const didChange = writer.accumulator.storeRegionBeforeState(x, y, w, h);
2292
- return didChange(blendColorPixelData2(target, color, opts));
2293
- }
2294
- };
2295
- });
2296
-
2297
- // src/PixelData/blendPixel.ts
2298
- function blendPixel(target, x, y, color, alpha = 255, blendFn = sourceOverPerfect) {
2299
- if (alpha === 0) return false;
2300
- let width = target.width;
2301
- let height = target.height;
2302
- if (x < 0 || x >= width || y < 0 || y >= height) return false;
2303
- let srcAlpha = color >>> 24;
2304
- let isOverwrite = blendFn.isOverwrite;
2305
- if (srcAlpha === 0 && !isOverwrite) return false;
2306
- let dst32 = target.data32;
2307
- let index = y * width + x;
2308
- let finalColor = color;
2309
- if (alpha !== 255) {
2310
- let finalAlpha = srcAlpha * alpha + 128 >> 8;
2311
- if (finalAlpha === 0 && !isOverwrite) return false;
2312
- finalColor = (color & 16777215 | finalAlpha << 24) >>> 0;
2299
+ width: oldW,
2300
+ height: oldH,
2301
+ data: oldData
2302
+ } = target;
2303
+ const newData = result.data;
2304
+ const x0 = Math.max(0, offsetX);
2305
+ const y0 = Math.max(0, offsetY);
2306
+ const x1 = Math.min(newWidth, offsetX + oldW);
2307
+ const y1 = Math.min(newHeight, offsetY + oldH);
2308
+ if (x1 <= x0 || y1 <= y0) {
2309
+ return result;
2313
2310
  }
2314
- let current = dst32[index];
2315
- let next = blendFn(finalColor, current);
2316
- if (current !== next) {
2317
- dst32[index] = next;
2318
- return true;
2311
+ const rowCount = y1 - y0;
2312
+ const rowLen = (x1 - x0) * 4;
2313
+ for (let row = 0; row < rowCount; row++) {
2314
+ const dstY = y0 + row;
2315
+ const srcY = dstY - offsetY;
2316
+ const srcX = x0 - offsetX;
2317
+ const dstStart = (dstY * newWidth + x0) * 4;
2318
+ const srcStart = (srcY * oldW + srcX) * 4;
2319
+ newData.set(oldData.subarray(srcStart, srcStart + rowLen), dstStart);
2319
2320
  }
2320
- return false;
2321
+ return result;
2321
2322
  }
2322
2323
 
2323
- // src/History/PixelMutator/mutatorBlendPixel.ts
2324
- var defaults3 = {
2325
- blendPixel
2326
- };
2327
- var mutatorBlendPixel = ((writer, deps = defaults3) => {
2328
- const {
2329
- blendPixel: blendPixel2 = defaults3.blendPixel
2330
- } = deps;
2331
- return {
2332
- blendPixel(x, y, color, alpha, blendFn) {
2333
- const didChange = writer.accumulator.storePixelBeforeState(x, y);
2334
- return didChange(blendPixel2(writer.config.target, x, y, color, alpha, blendFn));
2335
- }
2324
+ // src/Rect/trimRectBounds.ts
2325
+ function trimRectBounds(x, y, w, h, targetWidth, targetHeight, out) {
2326
+ const res = out ?? {
2327
+ x: 0,
2328
+ y: 0,
2329
+ w: 0,
2330
+ h: 0
2336
2331
  };
2337
- });
2332
+ const left = Math.max(0, x);
2333
+ const top = Math.max(0, y);
2334
+ const right = Math.min(targetWidth, x + w);
2335
+ const bottom = Math.min(targetHeight, y + h);
2336
+ res.x = left;
2337
+ res.y = top;
2338
+ res.w = Math.max(0, right - left);
2339
+ res.h = Math.max(0, bottom - top);
2340
+ return res;
2341
+ }
2342
+
2343
+ // src/Paint/PaintBuffer.ts
2344
+ var PaintBuffer = class {
2345
+ constructor(config, tilePool) {
2346
+ this.config = config;
2347
+ this.tilePool = tilePool;
2348
+ this.lookup = [];
2349
+ }
2350
+ lookup;
2351
+ scratchBounds = {
2352
+ x: 0,
2353
+ y: 0,
2354
+ w: 0,
2355
+ h: 0
2356
+ };
2357
+ eachTileInBounds(bounds, callback) {
2358
+ const {
2359
+ tileShift,
2360
+ targetColumns,
2361
+ targetRows,
2362
+ tileSize
2363
+ } = this.config;
2364
+ const x1 = Math.max(0, bounds.x >> tileShift);
2365
+ const y1 = Math.max(0, bounds.y >> tileShift);
2366
+ const x2 = Math.min(targetColumns - 1, bounds.x + bounds.w - 1 >> tileShift);
2367
+ const y2 = Math.min(targetRows - 1, bounds.y + bounds.h - 1 >> tileShift);
2368
+ if (x1 > x2 || y1 > y2) return;
2369
+ const lookup = this.lookup;
2370
+ const tilePool = this.tilePool;
2371
+ for (let ty = y1; ty <= y2; ty++) {
2372
+ const rowOffset = ty * targetColumns;
2373
+ const tileTop = ty << tileShift;
2374
+ for (let tx = x1; tx <= x2; tx++) {
2375
+ const id = rowOffset + tx;
2376
+ const tile = lookup[id] ?? (lookup[id] = tilePool.getTile(id, tx, ty));
2377
+ const tileLeft = tx << tileShift;
2378
+ const startX = bounds.x > tileLeft ? bounds.x : tileLeft;
2379
+ const startY = bounds.y > tileTop ? bounds.y : tileTop;
2380
+ const maskEndX = bounds.x + bounds.w;
2381
+ const tileEndX = tileLeft + tileSize;
2382
+ const endX = maskEndX < tileEndX ? maskEndX : tileEndX;
2383
+ const maskEndY = bounds.y + bounds.h;
2384
+ const tileEndY = tileTop + tileSize;
2385
+ const endY = maskEndY < tileEndY ? maskEndY : tileEndY;
2386
+ callback(tile, startX, startY, endX - startX, endY - startY);
2387
+ }
2388
+ }
2389
+ }
2390
+ writePaintAlphaMaskStroke(color, brush, x0, y0, x1, y1) {
2391
+ const cA = color >>> 24;
2392
+ if (cA === 0) return false;
2393
+ const {
2394
+ tileShift,
2395
+ tileMask,
2396
+ target
2397
+ } = this.config;
2398
+ const {
2399
+ w: bW,
2400
+ h: bH,
2401
+ data: bD,
2402
+ centerOffsetX,
2403
+ centerOffsetY
2404
+ } = brush;
2405
+ const cRGB = color & 16777215;
2406
+ const scratch = this.scratchBounds;
2407
+ let changed = false;
2408
+ forEachLinePoint(x0, y0, x1, y1, (px, py) => {
2409
+ const topLeftX = Math.floor(px + centerOffsetX);
2410
+ const topLeftY = Math.floor(py + centerOffsetY);
2411
+ trimRectBounds(topLeftX, topLeftY, bW, bH, target.width, target.height, scratch);
2412
+ if (scratch.w <= 0 || scratch.h <= 0) return;
2413
+ this.eachTileInBounds(scratch, (tile, bX, bY, bW_t, bH_t) => {
2414
+ const d32 = tile.data32;
2415
+ let tileChanged = false;
2416
+ for (let i = 0; i < bH_t; i++) {
2417
+ const canvasY = bY + i;
2418
+ const bOff = (canvasY - topLeftY) * bW;
2419
+ const tOff = (canvasY & tileMask) << tileShift;
2420
+ const dS = tOff + (bX & tileMask);
2421
+ for (let j = 0; j < bW_t; j++) {
2422
+ const canvasX = bX + j;
2423
+ const brushA = bD[bOff + (canvasX - topLeftX)];
2424
+ if (brushA === 0) continue;
2425
+ const t = cA * brushA + 128;
2426
+ const blendedA = t + (t >> 8) >> 8;
2427
+ const idx = dS + j;
2428
+ const cur = d32[idx];
2429
+ if (brushA > cur >>> 24) {
2430
+ const next = (cRGB | blendedA << 24) >>> 0;
2431
+ if (cur !== next) {
2432
+ d32[idx] = next;
2433
+ tileChanged = true;
2434
+ }
2435
+ }
2436
+ }
2437
+ }
2438
+ if (tileChanged) changed = true;
2439
+ });
2440
+ });
2441
+ return changed;
2442
+ }
2443
+ writePaintBinaryMaskStroke(color, brush, x0, y0, x1, y1) {
2444
+ const alphaIsZero = color >>> 24 === 0;
2445
+ if (alphaIsZero) return false;
2446
+ const {
2447
+ tileShift,
2448
+ tileMask,
2449
+ target
2450
+ } = this.config;
2451
+ const {
2452
+ w: bW,
2453
+ h: bH,
2454
+ data: bD,
2455
+ centerOffsetX,
2456
+ centerOffsetY
2457
+ } = brush;
2458
+ const scratch = this.scratchBounds;
2459
+ let changed = false;
2460
+ forEachLinePoint(x0, y0, x1, y1, (px, py) => {
2461
+ const topLeftX = Math.floor(px + centerOffsetX);
2462
+ const topLeftY = Math.floor(py + centerOffsetY);
2463
+ trimRectBounds(topLeftX, topLeftY, bW, bH, target.width, target.height, scratch);
2464
+ if (scratch.w <= 0 || scratch.h <= 0) return;
2465
+ this.eachTileInBounds(scratch, (tile, bX, bY, bW_t, bH_t) => {
2466
+ const d32 = tile.data32;
2467
+ let tileChanged = false;
2468
+ for (let i = 0; i < bH_t; i++) {
2469
+ const canvasY = bY + i;
2470
+ const bOff = (canvasY - topLeftY) * bW;
2471
+ const tOff = (canvasY & tileMask) << tileShift;
2472
+ const dS = tOff + (bX & tileMask);
2473
+ for (let j = 0; j < bW_t; j++) {
2474
+ const canvasX = bX + j;
2475
+ if (bD[bOff + (canvasX - topLeftX)]) {
2476
+ const idx = dS + j;
2477
+ if (d32[idx] !== color) {
2478
+ d32[idx] = color;
2479
+ tileChanged = true;
2480
+ }
2481
+ }
2482
+ }
2483
+ }
2484
+ if (tileChanged) changed = true;
2485
+ });
2486
+ });
2487
+ return changed;
2488
+ }
2489
+ writeRectStroke(color, brushWidth, brushHeight, x0, y0, x1, y1) {
2490
+ const alphaIsZero = color >>> 24 === 0;
2491
+ if (alphaIsZero) return false;
2492
+ const config = this.config;
2493
+ const tileShift = config.tileShift;
2494
+ const tileMask = config.tileMask;
2495
+ const target = config.target;
2496
+ const scratch = this.scratchBounds;
2497
+ const centerOffsetX = -(brushWidth - 1 >> 1);
2498
+ const centerOffsetY = -(brushHeight - 1 >> 1);
2499
+ let changed = false;
2500
+ forEachLinePoint(x0, y0, x1, y1, (px, py) => {
2501
+ const topLeftX = Math.floor(px + centerOffsetX);
2502
+ const topLeftY = Math.floor(py + centerOffsetY);
2503
+ trimRectBounds(topLeftX, topLeftY, brushWidth, brushHeight, target.width, target.height, scratch);
2504
+ if (scratch.w <= 0 || scratch.h <= 0) return;
2505
+ this.eachTileInBounds(scratch, (tile, bX, bY, bW_t, bH_t) => {
2506
+ const d32 = tile.data32;
2507
+ let tileChanged = false;
2508
+ for (let i = 0; i < bH_t; i++) {
2509
+ const canvasY = bY + i;
2510
+ const tOff = (canvasY & tileMask) << tileShift;
2511
+ const dS = tOff + (bX & tileMask);
2512
+ for (let j = 0; j < bW_t; j++) {
2513
+ const idx = dS + j;
2514
+ if (d32[idx] !== color) {
2515
+ d32[idx] = color;
2516
+ tileChanged = true;
2517
+ }
2518
+ }
2519
+ }
2520
+ if (tileChanged) {
2521
+ changed = true;
2522
+ }
2523
+ });
2524
+ });
2525
+ return changed;
2526
+ }
2527
+ clear() {
2528
+ this.tilePool.releaseTiles(this.lookup);
2529
+ }
2530
+ };
2338
2531
 
2339
2532
  // src/PixelData/blendPixelData.ts
2340
- function blendPixelData(dst, src, opts = {}) {
2341
- const {
2342
- x: targetX = 0,
2343
- y: targetY = 0,
2344
- sx: sourceX = 0,
2345
- sy: sourceY = 0,
2346
- w: width = src.width,
2347
- h: height = src.height,
2348
- alpha: globalAlpha = 255,
2349
- blendFn = sourceOverPerfect
2350
- } = opts;
2533
+ function blendPixelData(target, src, opts) {
2534
+ const targetX = opts?.x ?? 0;
2535
+ const targetY = opts?.y ?? 0;
2536
+ const sourceX = opts?.sx ?? 0;
2537
+ const sourceY = opts?.sy ?? 0;
2538
+ const width = opts?.w ?? src.width;
2539
+ const height = opts?.h ?? src.height;
2540
+ const globalAlpha = opts?.alpha ?? 255;
2541
+ const blendFn = opts?.blendFn ?? sourceOverPerfect;
2351
2542
  if (globalAlpha === 0) return false;
2352
2543
  let x = targetX;
2353
2544
  let y = targetY;
@@ -2377,12 +2568,12 @@ function blendPixelData(dst, src, opts = {}) {
2377
2568
  h += y;
2378
2569
  y = 0;
2379
2570
  }
2380
- const actualW = Math.min(w, dst.width - x);
2381
- const actualH = Math.min(h, dst.height - y);
2571
+ const actualW = Math.min(w, target.width - x);
2572
+ const actualH = Math.min(h, target.height - y);
2382
2573
  if (actualW <= 0 || actualH <= 0) return false;
2383
- const dst32 = dst.data32;
2574
+ const dst32 = target.data32;
2384
2575
  const src32 = src.data32;
2385
- const dw = dst.width;
2576
+ const dw = target.width;
2386
2577
  const sw = src.width;
2387
2578
  let dIdx = y * dw + x | 0;
2388
2579
  let sIdx = sy * sw + sx | 0;
@@ -2425,44 +2616,334 @@ function blendPixelData(dst, src, opts = {}) {
2425
2616
  return didChange;
2426
2617
  }
2427
2618
 
2428
- // src/History/PixelMutator/mutatorBlendPixelData.ts
2429
- var defaults4 = {
2430
- blendPixelData
2619
+ // src/PixelTile/PixelTile.ts
2620
+ var PixelTile = class {
2621
+ constructor(id, tx, ty, tileSize, tileArea) {
2622
+ this.id = id;
2623
+ this.tx = tx;
2624
+ this.ty = ty;
2625
+ this.width = this.height = tileSize;
2626
+ this.data32 = new Uint32Array(tileArea);
2627
+ const data8 = new Uint8ClampedArray(this.data32.buffer);
2628
+ this.imageData = new ImageData(data8, tileSize, tileSize);
2629
+ }
2630
+ data32;
2631
+ width;
2632
+ height;
2633
+ imageData;
2431
2634
  };
2432
- var mutatorBlendPixelData = ((writer, deps = defaults4) => {
2433
- const {
2434
- blendPixelData: blendPixelData2 = defaults4.blendPixelData
2435
- } = deps;
2436
- return {
2437
- blendPixelData(src, opts = {}) {
2438
- const {
2439
- x = 0,
2440
- y = 0,
2441
- w = src.width,
2442
- h = src.height
2443
- } = opts;
2444
- const didChange = writer.accumulator.storeRegionBeforeState(x, y, w, h);
2445
- return didChange(blendPixelData2(writer.config.target, src, opts));
2446
- }
2447
- };
2448
- });
2449
2635
 
2450
- // src/PixelData/blendPixelDataAlphaMask.ts
2451
- function blendPixelDataAlphaMask(dst, src, alphaMask, opts = {}) {
2452
- const {
2453
- x: targetX = 0,
2454
- y: targetY = 0,
2455
- sx: sourceX = 0,
2456
- sy: sourceY = 0,
2457
- w: width = src.width,
2458
- h: height = src.height,
2459
- alpha: globalAlpha = 255,
2460
- blendFn = sourceOverPerfect,
2461
- mx = 0,
2462
- my = 0,
2463
- invertMask = false
2464
- } = opts;
2465
- if (globalAlpha === 0) return false;
2636
+ // src/PixelTile/PixelTilePool.ts
2637
+ var PixelTilePool = class {
2638
+ pool;
2639
+ tileSize;
2640
+ tileArea;
2641
+ constructor(config) {
2642
+ this.pool = [];
2643
+ this.tileSize = config.tileSize;
2644
+ this.tileArea = config.tileArea;
2645
+ }
2646
+ getTile(id, tx, ty) {
2647
+ let tile = this.pool.pop();
2648
+ if (tile) {
2649
+ tile.id = id;
2650
+ tile.tx = tx;
2651
+ tile.ty = ty;
2652
+ tile.data32.fill(0);
2653
+ return tile;
2654
+ }
2655
+ return new PixelTile(id, tx, ty, this.tileSize, this.tileArea);
2656
+ }
2657
+ releaseTile(tile) {
2658
+ this.pool.push(tile);
2659
+ }
2660
+ releaseTiles(tiles) {
2661
+ let length = tiles.length;
2662
+ for (let i = 0; i < length; i++) {
2663
+ let tile = tiles[i];
2664
+ if (tile) {
2665
+ this.pool.push(tile);
2666
+ }
2667
+ }
2668
+ tiles.length = 0;
2669
+ }
2670
+ };
2671
+
2672
+ // src/History/PixelWriter.ts
2673
+ var PixelWriter = class {
2674
+ historyManager;
2675
+ accumulator;
2676
+ historyActionFactory;
2677
+ config;
2678
+ pixelTilePool;
2679
+ paintBuffer;
2680
+ mutator;
2681
+ blendPixelDataOpts = {
2682
+ alpha: 255,
2683
+ blendFn: sourceOverPerfect,
2684
+ x: 0,
2685
+ y: 0,
2686
+ w: 0,
2687
+ h: 0
2688
+ };
2689
+ _inProgress = false;
2690
+ constructor(target, mutatorFactory, options) {
2691
+ const tileSize = options?.tileSize ?? 256;
2692
+ const maxHistorySteps = options?.maxHistorySteps ?? 50;
2693
+ this.config = new PixelEngineConfig(tileSize, target);
2694
+ this.historyManager = options?.historyManager ?? new HistoryManager(maxHistorySteps);
2695
+ this.historyActionFactory = options?.historyActionFactory ?? makeHistoryAction;
2696
+ this.pixelTilePool = options?.pixelTilePool ?? new PixelTilePool(this.config);
2697
+ this.accumulator = options?.accumulator ?? new PixelAccumulator(this.config, this.pixelTilePool);
2698
+ this.mutator = mutatorFactory(this);
2699
+ this.paintBuffer = new PaintBuffer(this.config, this.pixelTilePool);
2700
+ }
2701
+ /**
2702
+ * Executes `transaction` and commits the resulting pixel changes as a single
2703
+ * undoable history action.
2704
+ *
2705
+ * - If `transaction` throws, all accumulated changes are rolled back and the error
2706
+ * is re-thrown. No action is committed.
2707
+ * - If `transaction` completes without modifying any pixels, no action is committed.
2708
+ * - `withHistory` is not re-entrant. Calling it again from inside `transaction` will
2709
+ * throw immediately to prevent silent data loss from a nested extractPatch.
2710
+ *
2711
+ * @param transaction Callback to be executed inside the transaction.
2712
+ * @param after Called after both undo and redo — use for generic change notifications.
2713
+ * @param afterUndo Called after undo only — use for dimension or state changes specific to undo.
2714
+ * @param afterRedo Called after redo only.
2715
+ */
2716
+ withHistory(transaction, after, afterUndo, afterRedo) {
2717
+ if (this._inProgress) {
2718
+ throw new Error("withHistory is not re-entrant \u2014 commit or rollback the current operation first");
2719
+ }
2720
+ this._inProgress = true;
2721
+ try {
2722
+ transaction(this.mutator);
2723
+ } catch (e) {
2724
+ this.accumulator.rollbackAfterError();
2725
+ throw e;
2726
+ } finally {
2727
+ this._inProgress = false;
2728
+ }
2729
+ if (this.accumulator.beforeTiles.length === 0) return;
2730
+ const patch = this.accumulator.extractPatch();
2731
+ const action = this.historyActionFactory(this.config, this.accumulator, patch, after, afterUndo, afterRedo);
2732
+ this.historyManager.commit(action);
2733
+ }
2734
+ resize(newWidth, newHeight, offsetX = 0, offsetY = 0, after, afterUndo, afterRedo, resizeImageDataFn = resizeImageData) {
2735
+ if (this._inProgress) {
2736
+ throw new Error("Cannot resize inside a withHistory callback");
2737
+ }
2738
+ if (this.accumulator.beforeTiles.length > 0) {
2739
+ throw new Error("Cannot resize with an open accumulator \u2014 commit or rollback first");
2740
+ }
2741
+ const config = this.config;
2742
+ const target = config.target;
2743
+ const beforeImageData = target.imageData;
2744
+ const afterImageData = resizeImageDataFn(beforeImageData, newWidth, newHeight, offsetX, offsetY);
2745
+ target.set(afterImageData);
2746
+ this.historyManager.commit({
2747
+ undo: () => {
2748
+ target.set(beforeImageData);
2749
+ afterUndo?.(beforeImageData);
2750
+ after?.(beforeImageData);
2751
+ },
2752
+ redo: () => {
2753
+ target.set(afterImageData);
2754
+ afterRedo?.(afterImageData);
2755
+ after?.(afterImageData);
2756
+ }
2757
+ });
2758
+ }
2759
+ commitPaintBuffer(alpha = 255, blendFn = sourceOverPerfect, blendPixelDataFn = blendPixelData) {
2760
+ const paintBuffer = this.paintBuffer;
2761
+ const tileShift = paintBuffer.config.tileShift;
2762
+ const lookup = paintBuffer.lookup;
2763
+ const opts = this.blendPixelDataOpts;
2764
+ opts.alpha = alpha;
2765
+ opts.blendFn = blendFn;
2766
+ for (let i = 0; i < lookup.length; i++) {
2767
+ const tile = lookup[i];
2768
+ if (tile) {
2769
+ const didChange = this.accumulator.storeTileBeforeState(tile.id, tile.tx, tile.ty);
2770
+ const dx = tile.tx << tileShift;
2771
+ const dy = tile.ty << tileShift;
2772
+ opts.x = dx;
2773
+ opts.y = dy;
2774
+ opts.w = tile.width;
2775
+ opts.h = tile.height;
2776
+ didChange(blendPixelDataFn(this.config.target, tile, opts));
2777
+ }
2778
+ }
2779
+ paintBuffer.clear();
2780
+ }
2781
+ };
2782
+
2783
+ // src/History/PixelMutator/mutatorApplyAlphaMask.ts
2784
+ var defaults2 = {
2785
+ applyAlphaMaskToPixelData
2786
+ };
2787
+ var mutatorApplyAlphaMask = ((writer, deps = defaults2) => {
2788
+ const {
2789
+ applyAlphaMaskToPixelData: applyAlphaMaskToPixelData2 = defaults2.applyAlphaMaskToPixelData
2790
+ } = deps;
2791
+ return {
2792
+ applyAlphaMask(mask, opts) {
2793
+ const target = writer.config.target;
2794
+ const x = opts?.x ?? 0;
2795
+ const y = opts?.y ?? 0;
2796
+ const w = opts?.w ?? target.width;
2797
+ const h = opts?.h ?? target.height;
2798
+ const didChange = writer.accumulator.storeRegionBeforeState(x, y, w, h);
2799
+ return didChange(applyAlphaMaskToPixelData2(target, mask, opts));
2800
+ }
2801
+ };
2802
+ });
2803
+
2804
+ // src/PixelData/applyBinaryMaskToPixelData.ts
2805
+ function applyBinaryMaskToPixelData(target, mask, opts) {
2806
+ const targetX = opts?.x ?? 0;
2807
+ const targetY = opts?.y ?? 0;
2808
+ const width = opts?.w ?? target.width;
2809
+ const height = opts?.h ?? target.height;
2810
+ const globalAlpha = opts?.alpha ?? 255;
2811
+ const mx = opts?.mx ?? 0;
2812
+ const my = opts?.my ?? 0;
2813
+ const invertMask = opts?.invertMask ?? false;
2814
+ if (globalAlpha === 0) return false;
2815
+ let x = targetX;
2816
+ let y = targetY;
2817
+ let w = width;
2818
+ let h = height;
2819
+ if (x < 0) {
2820
+ w += x;
2821
+ x = 0;
2822
+ }
2823
+ if (y < 0) {
2824
+ h += y;
2825
+ y = 0;
2826
+ }
2827
+ w = Math.min(w, target.width - x);
2828
+ h = Math.min(h, target.height - y);
2829
+ if (w <= 0 || h <= 0) return false;
2830
+ const mPitch = mask.w;
2831
+ if (mPitch <= 0) return false;
2832
+ const startX = mx + (x - targetX);
2833
+ const startY = my + (y - targetY);
2834
+ const sX0 = Math.max(0, startX);
2835
+ const sY0 = Math.max(0, startY);
2836
+ const sX1 = Math.min(mPitch, startX + w);
2837
+ const sY1 = Math.min(mask.h, startY + h);
2838
+ const finalW = sX1 - sX0;
2839
+ const finalH = sY1 - sY0;
2840
+ if (finalW <= 0 || finalH <= 0) {
2841
+ return false;
2842
+ }
2843
+ const xShift = sX0 - startX;
2844
+ const yShift = sY0 - startY;
2845
+ const dst32 = target.data32;
2846
+ const dw = target.width;
2847
+ const dStride = dw - finalW;
2848
+ const mStride = mPitch - finalW;
2849
+ const maskData = mask.data;
2850
+ let dIdx = (y + yShift) * dw + (x + xShift);
2851
+ let mIdx = sY0 * mPitch + sX0;
2852
+ let didChange = false;
2853
+ for (let iy = 0; iy < finalH; iy++) {
2854
+ for (let ix = 0; ix < finalW; ix++) {
2855
+ const mVal = maskData[mIdx];
2856
+ const isMaskedOut = invertMask ? mVal !== 0 : mVal === 0;
2857
+ if (isMaskedOut) {
2858
+ const current = dst32[dIdx];
2859
+ const next = (current & 16777215) >>> 0;
2860
+ if (current !== next) {
2861
+ dst32[dIdx] = next;
2862
+ didChange = true;
2863
+ }
2864
+ } else if (globalAlpha !== 255) {
2865
+ const d = dst32[dIdx];
2866
+ const da = d >>> 24;
2867
+ if (da !== 0) {
2868
+ const finalAlpha = da === 255 ? globalAlpha : da * globalAlpha + 128 >> 8;
2869
+ const next = (d & 16777215 | finalAlpha << 24) >>> 0;
2870
+ if (d !== next) {
2871
+ dst32[dIdx] = next;
2872
+ didChange = true;
2873
+ }
2874
+ }
2875
+ }
2876
+ dIdx++;
2877
+ mIdx++;
2878
+ }
2879
+ dIdx += dStride;
2880
+ mIdx += mStride;
2881
+ }
2882
+ return didChange;
2883
+ }
2884
+
2885
+ // src/History/PixelMutator/mutatorApplyBinaryMask.ts
2886
+ var defaults3 = {
2887
+ applyBinaryMaskToPixelData
2888
+ };
2889
+ var mutatorApplyBinaryMask = ((writer, deps = defaults3) => {
2890
+ const {
2891
+ applyBinaryMaskToPixelData: applyBinaryMaskToPixelData2 = defaults3.applyBinaryMaskToPixelData
2892
+ } = deps;
2893
+ return {
2894
+ applyBinaryMask(mask, opts) {
2895
+ const target = writer.config.target;
2896
+ const x = opts?.x ?? 0;
2897
+ const y = opts?.y ?? 0;
2898
+ const w = opts?.w ?? target.width;
2899
+ const h = opts?.h ?? target.height;
2900
+ const didChange = writer.accumulator.storeRegionBeforeState(x, y, w, h);
2901
+ return didChange(applyBinaryMaskToPixelData2(target, mask, opts));
2902
+ }
2903
+ };
2904
+ });
2905
+
2906
+ // src/History/PixelMutator/mutatorApplyMask.ts
2907
+ var defaults4 = {
2908
+ applyBinaryMaskToPixelData,
2909
+ applyAlphaMaskToPixelData
2910
+ };
2911
+ var mutatorApplyMask = ((writer, deps = defaults4) => {
2912
+ const {
2913
+ applyBinaryMaskToPixelData: applyBinaryMaskToPixelData2 = defaults4.applyBinaryMaskToPixelData,
2914
+ applyAlphaMaskToPixelData: applyAlphaMaskToPixelData2 = defaults4.applyAlphaMaskToPixelData
2915
+ } = deps;
2916
+ return {
2917
+ applyMask(mask, opts) {
2918
+ const target = writer.config.target;
2919
+ const x = opts?.x ?? 0;
2920
+ const y = opts?.y ?? 0;
2921
+ const w = opts?.w ?? target.width;
2922
+ const h = opts?.h ?? target.height;
2923
+ const didChange = writer.accumulator.storeRegionBeforeState(x, y, w, h);
2924
+ if (mask.type === 1 /* BINARY */) {
2925
+ return didChange(applyBinaryMaskToPixelData2(target, mask, opts));
2926
+ } else {
2927
+ return didChange(applyAlphaMaskToPixelData2(target, mask, opts));
2928
+ }
2929
+ }
2930
+ };
2931
+ });
2932
+
2933
+ // src/PixelData/blendPixelDataAlphaMask.ts
2934
+ function blendPixelDataAlphaMask(target, src, alphaMask, opts) {
2935
+ const targetX = opts?.x ?? 0;
2936
+ const targetY = opts?.y ?? 0;
2937
+ const sourceX = opts?.sx ?? 0;
2938
+ const sourceY = opts?.sy ?? 0;
2939
+ const width = opts?.w ?? src.width;
2940
+ const height = opts?.h ?? src.height;
2941
+ const globalAlpha = opts?.alpha ?? 255;
2942
+ const blendFn = opts?.blendFn ?? sourceOverPerfect;
2943
+ const mx = opts?.mx ?? 0;
2944
+ const my = opts?.my ?? 0;
2945
+ const invertMask = opts?.invertMask ?? false;
2946
+ if (globalAlpha === 0) return false;
2466
2947
  let x = targetX;
2467
2948
  let y = targetY;
2468
2949
  let sx = sourceX;
@@ -2491,16 +2972,16 @@ function blendPixelDataAlphaMask(dst, src, alphaMask, opts = {}) {
2491
2972
  h += y;
2492
2973
  y = 0;
2493
2974
  }
2494
- const actualW = Math.min(w, dst.width - x);
2495
- const actualH = Math.min(h, dst.height - y);
2975
+ const actualW = Math.min(w, target.width - x);
2976
+ const actualH = Math.min(h, target.height - y);
2496
2977
  if (actualW <= 0 || actualH <= 0) return false;
2497
- const dw = dst.width;
2978
+ const dw = target.width;
2498
2979
  const sw = src.width;
2499
2980
  const mPitch = alphaMask.w;
2500
2981
  const maskData = alphaMask.data;
2501
2982
  const dx = x - targetX | 0;
2502
2983
  const dy = y - targetY | 0;
2503
- const dst32 = dst.data32;
2984
+ const dst32 = target.data32;
2504
2985
  const src32 = src.data32;
2505
2986
  let dIdx = y * dw + x | 0;
2506
2987
  let sIdx = sy * sw + sx | 0;
@@ -2569,20 +3050,20 @@ function blendPixelDataAlphaMask(dst, src, alphaMask, opts = {}) {
2569
3050
  return didChange;
2570
3051
  }
2571
3052
 
2572
- // src/History/PixelMutator/mutatorBlendPixelDataAlphaMask.ts
3053
+ // src/History/PixelMutator/mutatorBlendAlphaMask.ts
2573
3054
  var defaults5 = {
2574
3055
  blendPixelDataAlphaMask
2575
3056
  };
2576
- var mutatorBlendPixelDataAlphaMask = ((writer, deps = defaults5) => {
3057
+ var mutatorBlendAlphaMask = ((writer, deps = defaults5) => {
2577
3058
  const {
2578
3059
  blendPixelDataAlphaMask: blendPixelDataAlphaMask2 = defaults5.blendPixelDataAlphaMask
2579
3060
  } = deps;
2580
3061
  return {
2581
- blendPixelDataAlphaMask(src, mask, opts = {}) {
2582
- const x = opts.x ?? 0;
2583
- const y = opts.y ?? 0;
2584
- const w = opts.w ?? src.width;
2585
- const h = opts.h ?? src.height;
3062
+ blendAlphaMask(src, mask, opts) {
3063
+ const x = opts?.x ?? 0;
3064
+ const y = opts?.y ?? 0;
3065
+ const w = opts?.w ?? src.width;
3066
+ const h = opts?.h ?? src.height;
2586
3067
  const didChange = writer.accumulator.storeRegionBeforeState(x, y, w, h);
2587
3068
  return didChange(blendPixelDataAlphaMask2(writer.config.target, src, mask, opts));
2588
3069
  }
@@ -2590,20 +3071,18 @@ var mutatorBlendPixelDataAlphaMask = ((writer, deps = defaults5) => {
2590
3071
  });
2591
3072
 
2592
3073
  // src/PixelData/blendPixelDataBinaryMask.ts
2593
- function blendPixelDataBinaryMask(dst, src, binaryMask, opts = {}) {
2594
- const {
2595
- x: targetX = 0,
2596
- y: targetY = 0,
2597
- sx: sourceX = 0,
2598
- sy: sourceY = 0,
2599
- w: width = src.width,
2600
- h: height = src.height,
2601
- alpha: globalAlpha = 255,
2602
- blendFn = sourceOverPerfect,
2603
- mx = 0,
2604
- my = 0,
2605
- invertMask = false
2606
- } = opts;
3074
+ function blendPixelDataBinaryMask(target, src, binaryMask, opts) {
3075
+ const targetX = opts?.x ?? 0;
3076
+ const targetY = opts?.y ?? 0;
3077
+ const sourceX = opts?.sx ?? 0;
3078
+ const sourceY = opts?.sy ?? 0;
3079
+ const width = opts?.w ?? src.width;
3080
+ const height = opts?.h ?? src.height;
3081
+ const globalAlpha = opts?.alpha ?? 255;
3082
+ const blendFn = opts?.blendFn ?? sourceOverPerfect;
3083
+ const mx = opts?.mx ?? 0;
3084
+ const my = opts?.my ?? 0;
3085
+ const invertMask = opts?.invertMask ?? false;
2607
3086
  if (globalAlpha === 0) return false;
2608
3087
  let x = targetX;
2609
3088
  let y = targetY;
@@ -2633,14 +3112,14 @@ function blendPixelDataBinaryMask(dst, src, binaryMask, opts = {}) {
2633
3112
  h += y;
2634
3113
  y = 0;
2635
3114
  }
2636
- const actualW = Math.min(w, dst.width - x);
2637
- const actualH = Math.min(h, dst.height - y);
3115
+ const actualW = Math.min(w, target.width - x);
3116
+ const actualH = Math.min(h, target.height - y);
2638
3117
  if (actualW <= 0 || actualH <= 0) return false;
2639
3118
  const dx = x - targetX | 0;
2640
3119
  const dy = y - targetY | 0;
2641
- const dst32 = dst.data32;
3120
+ const dst32 = target.data32;
2642
3121
  const src32 = src.data32;
2643
- const dw = dst.width;
3122
+ const dw = target.width;
2644
3123
  const sw = src.width;
2645
3124
  const mPitch = binaryMask.w;
2646
3125
  const maskData = binaryMask.data;
@@ -2698,1146 +3177,744 @@ function blendPixelDataBinaryMask(dst, src, binaryMask, opts = {}) {
2698
3177
  return didChange;
2699
3178
  }
2700
3179
 
2701
- // src/History/PixelMutator/mutatorBlendPixelDataBinaryMask.ts
3180
+ // src/History/PixelMutator/mutatorBlendBinaryMask.ts
2702
3181
  var defaults6 = {
2703
3182
  blendPixelDataBinaryMask
2704
3183
  };
2705
- var mutatorBlendPixelDataBinaryMask = ((writer, deps = defaults6) => {
3184
+ var mutatorBlendBinaryMask = ((writer, deps = defaults6) => {
2706
3185
  const {
2707
3186
  blendPixelDataBinaryMask: blendPixelDataBinaryMask2 = defaults6.blendPixelDataBinaryMask
2708
3187
  } = deps;
2709
3188
  return {
2710
- blendPixelDataBinaryMask(src, mask, opts = {}) {
2711
- const x = opts.x ?? 0;
2712
- const y = opts.y ?? 0;
2713
- const w = opts.w ?? src.width;
2714
- const h = opts.h ?? src.height;
3189
+ blendBinaryMask(src, mask, opts) {
3190
+ const x = opts?.x ?? 0;
3191
+ const y = opts?.y ?? 0;
3192
+ const w = opts?.w ?? src.width;
3193
+ const h = opts?.h ?? src.height;
2715
3194
  const didChange = writer.accumulator.storeRegionBeforeState(x, y, w, h);
2716
3195
  return didChange(blendPixelDataBinaryMask2(writer.config.target, src, mask, opts));
2717
3196
  }
2718
3197
  };
2719
3198
  });
2720
3199
 
2721
- // src/PixelData/fillPixelDataFast.ts
2722
- var SCRATCH_RECT = makeClippedRect();
2723
- function fillPixelDataFast(dst, color, _x, _y, _w, _h) {
2724
- let x;
2725
- let y;
2726
- let w;
2727
- let h;
2728
- if (typeof _x === "object") {
2729
- x = _x.x ?? 0;
2730
- y = _x.y ?? 0;
2731
- w = _x.w ?? dst.width;
2732
- h = _x.h ?? dst.height;
2733
- } else if (typeof _x === "number") {
2734
- x = _x;
2735
- y = _y;
2736
- w = _w;
2737
- h = _h;
2738
- } else {
3200
+ // src/PixelData/blendColorPixelData.ts
3201
+ function blendColorPixelData(target, color, opts) {
3202
+ const targetX = opts?.x ?? 0;
3203
+ const targetY = opts?.y ?? 0;
3204
+ const width = opts?.w ?? target.width;
3205
+ const height = opts?.h ?? target.height;
3206
+ const globalAlpha = opts?.alpha ?? 255;
3207
+ const blendFn = opts?.blendFn ?? sourceOverPerfect;
3208
+ if (globalAlpha === 0) return false;
3209
+ const baseSrcAlpha = color >>> 24;
3210
+ const isOverwrite = blendFn.isOverwrite || false;
3211
+ if (baseSrcAlpha === 0 && !isOverwrite) return false;
3212
+ let x = targetX;
3213
+ let y = targetY;
3214
+ let w = width;
3215
+ let h = height;
3216
+ if (x < 0) {
3217
+ w += x;
2739
3218
  x = 0;
3219
+ }
3220
+ if (y < 0) {
3221
+ h += y;
2740
3222
  y = 0;
2741
- w = dst.width;
2742
- h = dst.height;
2743
3223
  }
2744
- const clip = resolveRectClipping(x, y, w, h, dst.width, dst.height, SCRATCH_RECT);
2745
- if (!clip.inBounds) return;
2746
- const {
2747
- x: finalX,
2748
- y: finalY,
2749
- w: actualW,
2750
- h: actualH
2751
- } = clip;
2752
- const dst32 = dst.data32;
2753
- const dw = dst.width;
2754
- if (actualW === dw && actualH === dst.height && finalX === 0 && finalY === 0) {
2755
- dst32.fill(color);
2756
- return;
3224
+ const actualW = Math.min(w, target.width - x);
3225
+ const actualH = Math.min(h, target.height - y);
3226
+ if (actualW <= 0 || actualH <= 0) return false;
3227
+ let finalSrcColor = color;
3228
+ if (globalAlpha < 255) {
3229
+ const a = baseSrcAlpha * globalAlpha + 128 >> 8;
3230
+ if (a === 0 && !isOverwrite) return false;
3231
+ finalSrcColor = (color & 16777215 | a << 24) >>> 0;
2757
3232
  }
3233
+ const dst32 = target.data32;
3234
+ const dw = target.width;
3235
+ let dIdx = y * dw + x | 0;
3236
+ const dStride = dw - actualW | 0;
3237
+ let didChange = false;
2758
3238
  for (let iy = 0; iy < actualH; iy++) {
2759
- const start = (finalY + iy) * dw + finalX;
2760
- const end = start + actualW;
2761
- dst32.fill(color, start, end);
3239
+ for (let ix = 0; ix < actualW; ix++) {
3240
+ const current = dst32[dIdx];
3241
+ const next = blendFn(finalSrcColor, current);
3242
+ if (current !== next) {
3243
+ dst32[dIdx] = next;
3244
+ didChange = true;
3245
+ }
3246
+ dIdx++;
3247
+ }
3248
+ dIdx += dStride;
2762
3249
  }
3250
+ return didChange;
2763
3251
  }
2764
3252
 
2765
- // src/History/PixelMutator/mutatorClear.ts
3253
+ // src/History/PixelMutator/mutatorBlendColor.ts
2766
3254
  var defaults7 = {
2767
- fillPixelData: fillPixelDataFast
3255
+ blendColorPixelData
2768
3256
  };
2769
- var mutatorClear = ((writer, deps = defaults7) => {
3257
+ var mutatorBlendColor = ((writer, deps = defaults7) => {
2770
3258
  const {
2771
- fillPixelData: fillPixelData2 = defaults7.fillPixelData
3259
+ blendColorPixelData: blendColorPixelData2 = defaults7.blendColorPixelData
2772
3260
  } = deps;
2773
3261
  return {
2774
- clear(rect = {}) {
3262
+ blendColor(color, opts) {
2775
3263
  const target = writer.config.target;
2776
- const x = rect.x ?? 0;
2777
- const y = rect.y ?? 0;
2778
- const w = rect.w ?? target.width;
2779
- const h = rect.h ?? target.height;
2780
- writer.accumulator.storeRegionBeforeState(x, y, w, h);
2781
- fillPixelData2(target, 0, x, y, w, h);
3264
+ const x = opts?.x ?? 0;
3265
+ const y = opts?.y ?? 0;
3266
+ const w = opts?.w ?? target.width;
3267
+ const h = opts?.h ?? target.height;
3268
+ const didChange = writer.accumulator.storeRegionBeforeState(x, y, w, h);
3269
+ return didChange(blendColorPixelData2(target, color, opts));
2782
3270
  }
2783
3271
  };
2784
3272
  });
2785
3273
 
2786
- // src/PixelData/fillPixelData.ts
2787
- var SCRATCH_RECT2 = makeClippedRect();
2788
- function fillPixelData(dst, color, _x, _y, _w, _h) {
2789
- let x;
2790
- let y;
2791
- let w;
2792
- let h;
2793
- if (typeof _x === "object") {
2794
- x = _x.x ?? 0;
2795
- y = _x.y ?? 0;
2796
- w = _x.w ?? dst.width;
2797
- h = _x.h ?? dst.height;
2798
- } else if (typeof _x === "number") {
2799
- x = _x;
2800
- y = _y;
2801
- w = _w;
2802
- h = _h;
2803
- } else {
3274
+ // src/PixelData/blendColorPixelDataAlphaMask.ts
3275
+ function blendColorPixelDataAlphaMask(target, color, mask, opts) {
3276
+ const targetX = opts?.x ?? 0;
3277
+ const targetY = opts?.y ?? 0;
3278
+ const w = opts?.w ?? mask.w;
3279
+ const h = opts?.h ?? mask.h;
3280
+ const globalAlpha = opts?.alpha ?? 255;
3281
+ const blendFn = opts?.blendFn ?? sourceOverPerfect;
3282
+ const mx = opts?.mx ?? 0;
3283
+ const my = opts?.my ?? 0;
3284
+ const invertMask = opts?.invertMask ?? false;
3285
+ if (globalAlpha === 0) return false;
3286
+ const baseSrcAlpha = color >>> 24;
3287
+ const isOverwrite = blendFn.isOverwrite || false;
3288
+ if (baseSrcAlpha === 0 && !isOverwrite) return false;
3289
+ let x = targetX;
3290
+ let y = targetY;
3291
+ let actualW = w;
3292
+ let actualH = h;
3293
+ if (x < 0) {
3294
+ actualW += x;
2804
3295
  x = 0;
3296
+ }
3297
+ if (y < 0) {
3298
+ actualH += y;
2805
3299
  y = 0;
2806
- w = dst.width;
2807
- h = dst.height;
2808
3300
  }
2809
- const clip = resolveRectClipping(x, y, w, h, dst.width, dst.height, SCRATCH_RECT2);
2810
- if (!clip.inBounds) return false;
2811
- const {
2812
- x: finalX,
2813
- y: finalY,
2814
- w: actualW,
2815
- h: actualH
2816
- } = clip;
2817
- const dst32 = dst.data32;
2818
- const dw = dst.width;
2819
- let hasChanged = false;
3301
+ actualW = Math.min(actualW, target.width - x);
3302
+ actualH = Math.min(actualH, target.height - y);
3303
+ if (actualW <= 0 || actualH <= 0) return false;
3304
+ const dx = x - targetX | 0;
3305
+ const dy = y - targetY | 0;
3306
+ const dst32 = target.data32;
3307
+ const dw = target.width;
3308
+ const mPitch = mask.w;
3309
+ const maskData = mask.data;
3310
+ let dIdx = y * dw + x | 0;
3311
+ let mIdx = (my + dy) * mPitch + (mx + dx) | 0;
3312
+ const dStride = dw - actualW | 0;
3313
+ const mStride = mPitch - actualW | 0;
3314
+ const isOpaque = globalAlpha === 255;
3315
+ const colorRGB = color & 16777215;
3316
+ let didChange = false;
2820
3317
  for (let iy = 0; iy < actualH; iy++) {
2821
- const rowOffset = (finalY + iy) * dw;
2822
- const start = rowOffset + finalX;
2823
- const end = start + actualW;
2824
- for (let i = start; i < end; i++) {
2825
- if (dst32[i] !== color) {
2826
- dst32[i] = color;
2827
- hasChanged = true;
3318
+ for (let ix = 0; ix < actualW; ix++) {
3319
+ const mVal = maskData[mIdx];
3320
+ const effM = invertMask ? 255 - mVal : mVal;
3321
+ if (effM === 0) {
3322
+ dIdx++;
3323
+ mIdx++;
3324
+ continue;
3325
+ }
3326
+ let weight = globalAlpha;
3327
+ if (isOpaque) {
3328
+ weight = effM;
3329
+ } else if (effM !== 255) {
3330
+ weight = effM * globalAlpha + 128 >> 8;
2828
3331
  }
3332
+ if (weight === 0) {
3333
+ dIdx++;
3334
+ mIdx++;
3335
+ continue;
3336
+ }
3337
+ let finalCol = color;
3338
+ if (weight < 255) {
3339
+ const a = baseSrcAlpha * weight + 128 >> 8;
3340
+ if (a === 0 && !isOverwrite) {
3341
+ dIdx++;
3342
+ mIdx++;
3343
+ continue;
3344
+ }
3345
+ finalCol = (colorRGB | a << 24) >>> 0;
3346
+ }
3347
+ const current = dst32[dIdx];
3348
+ const next = blendFn(finalCol, current);
3349
+ if (current !== next) {
3350
+ dst32[dIdx] = next;
3351
+ didChange = true;
3352
+ }
3353
+ dIdx++;
3354
+ mIdx++;
2829
3355
  }
3356
+ dIdx += dStride;
3357
+ mIdx += mStride;
2830
3358
  }
2831
- return hasChanged;
3359
+ return didChange;
2832
3360
  }
2833
3361
 
2834
- // src/History/PixelMutator/mutatorFill.ts
3362
+ // src/History/PixelMutator/mutatorBlendColorPaintAlphaMask.ts
2835
3363
  var defaults8 = {
2836
- fillPixelData
3364
+ blendColorPixelDataAlphaMask
2837
3365
  };
2838
- var mutatorFill = ((writer, deps = defaults8) => {
3366
+ var mutatorBlendColorPaintAlphaMask = ((writer, deps = defaults8) => {
2839
3367
  const {
2840
- fillPixelData: fillPixelData2 = defaults8.fillPixelData
3368
+ blendColorPixelDataAlphaMask: blendColorPixelDataAlphaMask2 = defaults8.blendColorPixelDataAlphaMask
2841
3369
  } = deps;
2842
- return {
2843
- fill(color, x = 0, y = 0, w = writer.config.target.width, h = writer.config.target.height) {
2844
- const target = writer.config.target;
2845
- const didChange = writer.accumulator.storeRegionBeforeState(x, y, w, h);
2846
- return didChange(fillPixelData2(target, color, x, y, w, h));
2847
- }
3370
+ const OPTS = {
3371
+ x: 0,
3372
+ y: 0,
3373
+ blendFn: sourceOverPerfect,
3374
+ alpha: 255
2848
3375
  };
2849
- });
2850
- var mutatorFillRect = ((writer, deps = defaults8) => {
2851
- const {
2852
- fillPixelData: fillPixelData2 = defaults8.fillPixelData
2853
- } = deps;
2854
3376
  return {
2855
- fillRect(color, rect) {
2856
- const target = writer.config.target;
2857
- const didChange = writer.accumulator.storeRegionBeforeState(rect.x, rect.y, rect.w, rect.h);
2858
- return didChange(fillPixelData2(target, color, rect.x, rect.y, rect.w, rect.h));
3377
+ blendColorPaintAlphaMask(color, mask, x, y, alpha = 255, blendFn = sourceOverPerfect) {
3378
+ const tx = x + mask.centerOffsetX;
3379
+ const ty = y + mask.centerOffsetY;
3380
+ const didChange = writer.accumulator.storeRegionBeforeState(tx, ty, mask.w, mask.h);
3381
+ OPTS.x = tx;
3382
+ OPTS.y = ty;
3383
+ OPTS.alpha = alpha;
3384
+ OPTS.blendFn = blendFn;
3385
+ return didChange(blendColorPixelDataAlphaMask2(writer.config.target, color, mask, OPTS));
2859
3386
  }
2860
3387
  };
2861
3388
  });
2862
3389
 
2863
- // src/PixelData/fillPixelDataBinaryMask.ts
2864
- var SCRATCH_RECT3 = makeClippedRect();
2865
- function fillPixelDataBinaryMask(dst, color, mask, alpha = 255, x = 0, y = 0) {
2866
- if (alpha === 0) return false;
2867
- const maskW = mask.w;
2868
- const maskH = mask.h;
2869
- const clip = resolveRectClipping(x, y, maskW, maskH, dst.width, dst.height, SCRATCH_RECT3);
2870
- if (!clip.inBounds) return false;
2871
- const {
2872
- x: finalX,
2873
- y: finalY,
2874
- w: actualW,
2875
- h: actualH
2876
- } = clip;
2877
- const maskData = mask.data;
2878
- const dst32 = dst.data32;
2879
- const dw = dst.width;
2880
- let finalCol = color;
2881
- if (alpha < 255) {
2882
- const baseSrcAlpha = color >>> 24;
2883
- const colorRGB = color & 16777215;
2884
- const a = baseSrcAlpha * alpha + 128 >> 8;
2885
- finalCol = (colorRGB | a << 24) >>> 0;
2886
- }
2887
- let hasChanged = false;
2888
- for (let iy = 0; iy < actualH; iy++) {
2889
- const currentY = finalY + iy;
2890
- const maskY = currentY - y;
2891
- const maskOffset = maskY * maskW;
2892
- const dstRowOffset = currentY * dw;
2893
- for (let ix = 0; ix < actualW; ix++) {
2894
- const currentX = finalX + ix;
2895
- const maskX = currentX - x;
2896
- const maskIndex = maskOffset + maskX;
2897
- if (maskData[maskIndex]) {
2898
- const current = dst32[dstRowOffset + currentX];
2899
- if (current !== finalCol) {
2900
- dst32[dstRowOffset + currentX] = finalCol;
2901
- hasChanged = true;
2902
- }
2903
- }
2904
- }
3390
+ // src/PixelData/blendColorPixelDataBinaryMask.ts
3391
+ function blendColorPixelDataBinaryMask(target, color, mask, opts) {
3392
+ const targetX = opts?.x ?? 0;
3393
+ const targetY = opts?.y ?? 0;
3394
+ let w = opts?.w ?? mask.w;
3395
+ let h = opts?.h ?? mask.h;
3396
+ const globalAlpha = opts?.alpha ?? 255;
3397
+ const blendFn = opts?.blendFn ?? sourceOverPerfect;
3398
+ const mx = opts?.mx ?? 0;
3399
+ const my = opts?.my ?? 0;
3400
+ const invertMask = opts?.invertMask ?? false;
3401
+ if (globalAlpha === 0) return false;
3402
+ const baseSrcAlpha = color >>> 24;
3403
+ const isOverwrite = blendFn.isOverwrite || false;
3404
+ if (baseSrcAlpha === 0 && !isOverwrite) return false;
3405
+ let x = targetX;
3406
+ let y = targetY;
3407
+ if (x < 0) {
3408
+ w += x;
3409
+ x = 0;
2905
3410
  }
2906
- return hasChanged;
3411
+ if (y < 0) {
3412
+ h += y;
3413
+ y = 0;
3414
+ }
3415
+ const actualW = Math.min(w, target.width - x);
3416
+ const actualH = Math.min(h, target.height - y);
3417
+ if (actualW <= 0 || actualH <= 0) return false;
3418
+ let baseColorWithGlobalAlpha = color;
3419
+ if (globalAlpha < 255) {
3420
+ const a = baseSrcAlpha * globalAlpha + 128 >> 8;
3421
+ if (a === 0 && !isOverwrite) return false;
3422
+ baseColorWithGlobalAlpha = (color & 16777215 | a << 24) >>> 0;
3423
+ }
3424
+ const dx = x - targetX | 0;
3425
+ const dy = y - targetY | 0;
3426
+ const dst32 = target.data32;
3427
+ const dw = target.width;
3428
+ const mPitch = mask.w;
3429
+ const maskData = mask.data;
3430
+ let dIdx = y * dw + x | 0;
3431
+ let mIdx = (my + dy) * mPitch + (mx + dx) | 0;
3432
+ const dStride = dw - actualW | 0;
3433
+ const mStride = mPitch - actualW | 0;
3434
+ const skipVal = invertMask ? 1 : 0;
3435
+ let didChange = false;
3436
+ for (let iy = 0; iy < actualH; iy++) {
3437
+ for (let ix = 0; ix < actualW; ix++) {
3438
+ if (maskData[mIdx] === skipVal) {
3439
+ dIdx++;
3440
+ mIdx++;
3441
+ continue;
3442
+ }
3443
+ const current = dst32[dIdx];
3444
+ const next = blendFn(baseColorWithGlobalAlpha, current);
3445
+ if (current !== next) {
3446
+ dst32[dIdx] = next;
3447
+ didChange = true;
3448
+ }
3449
+ dIdx++;
3450
+ mIdx++;
3451
+ }
3452
+ dIdx += dStride;
3453
+ mIdx += mStride;
3454
+ }
3455
+ return didChange;
2907
3456
  }
2908
3457
 
2909
- // src/History/PixelMutator/mutatorFillBinaryMask.ts
3458
+ // src/History/PixelMutator/mutatorBlendColorPaintBinaryMask.ts
2910
3459
  var defaults9 = {
2911
- fillPixelDataBinaryMask
3460
+ blendColorPixelDataBinaryMask
2912
3461
  };
2913
- var mutatorFillBinaryMask = ((writer, deps = defaults9) => {
3462
+ var mutatorBlendColorPaintBinaryMask = ((writer, deps = defaults9) => {
2914
3463
  const {
2915
- fillPixelDataBinaryMask: fillPixelDataBinaryMask2 = defaults9.fillPixelDataBinaryMask
3464
+ blendColorPixelDataBinaryMask: blendColorPixelDataBinaryMask2 = defaults9.blendColorPixelDataBinaryMask
2916
3465
  } = deps;
3466
+ const OPTS = {
3467
+ x: 0,
3468
+ y: 0,
3469
+ blendFn: sourceOverPerfect,
3470
+ alpha: 255
3471
+ };
2917
3472
  return {
2918
- fillBinaryMask(color, mask, alpha = 255, x = 0, y = 0) {
2919
- const didChange = writer.accumulator.storeRegionBeforeState(x, y, mask.w, mask.h);
2920
- return didChange(fillPixelDataBinaryMask2(writer.config.target, color, mask, alpha, x, y));
3473
+ blendColorPaintBinaryMask(color, mask, x, y, alpha = 255, blendFn = sourceOverPerfect) {
3474
+ const tx = x + mask.centerOffsetX;
3475
+ const ty = y + mask.centerOffsetY;
3476
+ const didChange = writer.accumulator.storeRegionBeforeState(tx, ty, mask.w, mask.h);
3477
+ OPTS.x = tx;
3478
+ OPTS.y = ty;
3479
+ OPTS.alpha = alpha;
3480
+ OPTS.blendFn = blendFn;
3481
+ return didChange(blendColorPixelDataBinaryMask2(writer.config.target, color, mask, OPTS));
2921
3482
  }
2922
3483
  };
2923
3484
  });
2924
3485
 
2925
- // src/PixelData/invertPixelData.ts
2926
- var SCRATCH_RECT4 = makeClippedRect();
2927
- function invertPixelData(pixelData, opts = {}) {
2928
- const dst = pixelData;
3486
+ // src/History/PixelMutator/mutatorBlendColorPaintMask.ts
3487
+ var defaults10 = {
3488
+ blendColorPixelDataAlphaMask,
3489
+ blendColorPixelDataBinaryMask
3490
+ };
3491
+ var mutatorBlendColorPaintMask = ((writer, deps = defaults10) => {
2929
3492
  const {
2930
- x: targetX = 0,
2931
- y: targetY = 0,
2932
- w: width = pixelData.width,
2933
- h: height = pixelData.height,
2934
- mask,
2935
- mx = 0,
2936
- my = 0,
2937
- invertMask = false
2938
- } = opts;
2939
- const clip = resolveRectClipping(targetX, targetY, width, height, dst.width, dst.height, SCRATCH_RECT4);
3493
+ blendColorPixelDataBinaryMask: blendColorPixelDataBinaryMask2 = defaults10.blendColorPixelDataBinaryMask,
3494
+ blendColorPixelDataAlphaMask: blendColorPixelDataAlphaMask2 = defaults10.blendColorPixelDataAlphaMask
3495
+ } = deps;
3496
+ const OPTS = {
3497
+ x: 0,
3498
+ y: 0,
3499
+ blendFn: sourceOverPerfect,
3500
+ alpha: 255
3501
+ };
3502
+ return {
3503
+ blendColorPaintMask(color, mask, x, y, alpha = 255, blendFn = sourceOverPerfect) {
3504
+ const tx = x + mask.centerOffsetX;
3505
+ const ty = y + mask.centerOffsetY;
3506
+ const didChange = writer.accumulator.storeRegionBeforeState(tx, ty, mask.w, mask.h);
3507
+ OPTS.x = tx;
3508
+ OPTS.y = ty;
3509
+ OPTS.alpha = alpha;
3510
+ OPTS.blendFn = blendFn;
3511
+ if (mask.type === 1 /* BINARY */) {
3512
+ return didChange(blendColorPixelDataBinaryMask2(writer.config.target, color, mask, OPTS));
3513
+ } else {
3514
+ return didChange(blendColorPixelDataAlphaMask2(writer.config.target, color, mask, OPTS));
3515
+ }
3516
+ }
3517
+ };
3518
+ });
3519
+
3520
+ // src/History/PixelMutator/mutatorBlendMask.ts
3521
+ var defaults11 = {
3522
+ blendPixelDataAlphaMask,
3523
+ blendPixelDataBinaryMask
3524
+ };
3525
+ var mutatorBlendMask = ((writer, deps = defaults11) => {
3526
+ const {
3527
+ blendPixelDataAlphaMask: blendPixelDataAlphaMask2 = defaults11.blendPixelDataAlphaMask,
3528
+ blendPixelDataBinaryMask: blendPixelDataBinaryMask2 = defaults11.blendPixelDataBinaryMask
3529
+ } = deps;
3530
+ return {
3531
+ blendMask(src, mask, opts) {
3532
+ const x = opts?.x ?? 0;
3533
+ const y = opts?.y ?? 0;
3534
+ const w = opts?.w ?? src.width;
3535
+ const h = opts?.h ?? src.height;
3536
+ const didChange = writer.accumulator.storeRegionBeforeState(x, y, w, h);
3537
+ if (mask.type === 1 /* BINARY */) {
3538
+ return didChange(blendPixelDataBinaryMask2(writer.config.target, src, mask, opts));
3539
+ } else {
3540
+ return didChange(blendPixelDataAlphaMask2(writer.config.target, src, mask, opts));
3541
+ }
3542
+ }
3543
+ };
3544
+ });
3545
+
3546
+ // src/History/PixelMutator/mutatorBlendPaintRect.ts
3547
+ var defaults12 = {
3548
+ blendColorPixelData
3549
+ };
3550
+ var mutatorBlendPaintRect = ((writer, deps = defaults12) => {
3551
+ const {
3552
+ blendColorPixelData: blendColorPixelData2 = defaults12.blendColorPixelData
3553
+ } = deps;
3554
+ const OPTS = {
3555
+ x: 0,
3556
+ y: 0,
3557
+ w: 0,
3558
+ h: 0,
3559
+ blendFn: sourceOverPerfect,
3560
+ alpha: 255
3561
+ };
3562
+ return {
3563
+ blendPaintRect(color, centerX, centerY, brushWidth, brushHeight, alpha = 255, blendFn = sourceOverPerfect) {
3564
+ const target = writer.config.target;
3565
+ const topLeftX = centerX + -(brushWidth - 1 >> 1);
3566
+ const topLeftY = centerY + -(brushHeight - 1 >> 1);
3567
+ OPTS.x = topLeftX;
3568
+ OPTS.y = topLeftY;
3569
+ OPTS.w = brushWidth;
3570
+ OPTS.h = brushHeight;
3571
+ OPTS.blendFn = blendFn;
3572
+ OPTS.alpha = alpha;
3573
+ const didChange = writer.accumulator.storeRegionBeforeState(topLeftX, topLeftY, brushWidth, brushHeight);
3574
+ return didChange(blendColorPixelData2(target, color, OPTS));
3575
+ }
3576
+ };
3577
+ });
3578
+
3579
+ // src/PixelData/blendPixel.ts
3580
+ function blendPixel(target, x, y, color, alpha = 255, blendFn = sourceOverPerfect) {
3581
+ if (alpha === 0) return false;
3582
+ let width = target.width;
3583
+ let height = target.height;
3584
+ if (x < 0 || x >= width || y < 0 || y >= height) return false;
3585
+ let srcAlpha = color >>> 24;
3586
+ let isOverwrite = blendFn.isOverwrite;
3587
+ if (srcAlpha === 0 && !isOverwrite) return false;
3588
+ let dst32 = target.data32;
3589
+ let index = y * width + x;
3590
+ let finalColor = color;
3591
+ if (alpha !== 255) {
3592
+ let finalAlpha = srcAlpha * alpha + 128 >> 8;
3593
+ if (finalAlpha === 0 && !isOverwrite) return false;
3594
+ finalColor = (color & 16777215 | finalAlpha << 24) >>> 0;
3595
+ }
3596
+ let current = dst32[index];
3597
+ let next = blendFn(finalColor, current);
3598
+ if (current !== next) {
3599
+ dst32[index] = next;
3600
+ return true;
3601
+ }
3602
+ return false;
3603
+ }
3604
+
3605
+ // src/History/PixelMutator/mutatorBlendPixel.ts
3606
+ var defaults13 = {
3607
+ blendPixel
3608
+ };
3609
+ var mutatorBlendPixel = ((writer, deps = defaults13) => {
3610
+ const {
3611
+ blendPixel: blendPixel2 = defaults13.blendPixel
3612
+ } = deps;
3613
+ return {
3614
+ blendPixel(x, y, color, alpha, blendFn) {
3615
+ const didChange = writer.accumulator.storePixelBeforeState(x, y);
3616
+ return didChange(blendPixel2(writer.config.target, x, y, color, alpha, blendFn));
3617
+ }
3618
+ };
3619
+ });
3620
+
3621
+ // src/History/PixelMutator/mutatorBlendPixelData.ts
3622
+ var defaults14 = {
3623
+ blendPixelData
3624
+ };
3625
+ var mutatorBlendPixelData = ((writer, deps = defaults14) => {
3626
+ const {
3627
+ blendPixelData: blendPixelData2 = defaults14.blendPixelData
3628
+ } = deps;
3629
+ return {
3630
+ blendPixelData(src, opts) {
3631
+ const x = opts?.x ?? 0;
3632
+ const y = opts?.y ?? 0;
3633
+ const w = opts?.w ?? src.width;
3634
+ const h = opts?.h ?? src.height;
3635
+ const didChange = writer.accumulator.storeRegionBeforeState(x, y, w, h);
3636
+ return didChange(blendPixelData2(writer.config.target, src, opts));
3637
+ }
3638
+ };
3639
+ });
3640
+
3641
+ // src/PixelData/fillPixelData.ts
3642
+ var SCRATCH_RECT = makeClippedRect();
3643
+ function fillPixelData(dst, color, _x, _y, _w, _h) {
3644
+ let x;
3645
+ let y;
3646
+ let w;
3647
+ let h;
3648
+ if (typeof _x === "object") {
3649
+ x = _x.x ?? 0;
3650
+ y = _x.y ?? 0;
3651
+ w = _x.w ?? dst.width;
3652
+ h = _x.h ?? dst.height;
3653
+ } else if (typeof _x === "number") {
3654
+ x = _x;
3655
+ y = _y;
3656
+ w = _w;
3657
+ h = _h;
3658
+ } else {
3659
+ x = 0;
3660
+ y = 0;
3661
+ w = dst.width;
3662
+ h = dst.height;
3663
+ }
3664
+ const clip = resolveRectClipping(x, y, w, h, dst.width, dst.height, SCRATCH_RECT);
2940
3665
  if (!clip.inBounds) return false;
2941
3666
  const {
2942
- x,
2943
- y,
3667
+ x: finalX,
3668
+ y: finalY,
2944
3669
  w: actualW,
2945
3670
  h: actualH
2946
3671
  } = clip;
2947
3672
  const dst32 = dst.data32;
2948
3673
  const dw = dst.width;
2949
- const mPitch = mask?.w ?? width;
2950
- const dx = x - targetX;
2951
- const dy = y - targetY;
2952
- let dIdx = y * dw + x;
2953
- let mIdx = (my + dy) * mPitch + (mx + dx);
2954
- const dStride = dw - actualW;
2955
- const mStride = mPitch - actualW;
2956
- if (mask) {
2957
- const maskData = mask.data;
2958
- for (let iy = 0; iy < actualH; iy++) {
2959
- for (let ix = 0; ix < actualW; ix++) {
2960
- const mVal = maskData[mIdx];
2961
- const isHit = invertMask ? mVal === 0 : mVal === 1;
2962
- if (isHit) {
2963
- dst32[dIdx] = dst32[dIdx] ^ 16777215;
2964
- }
2965
- dIdx++;
2966
- mIdx++;
2967
- }
2968
- dIdx += dStride;
2969
- mIdx += mStride;
2970
- }
2971
- } else {
2972
- for (let iy = 0; iy < actualH; iy++) {
2973
- for (let ix = 0; ix < actualW; ix++) {
2974
- dst32[dIdx] = dst32[dIdx] ^ 16777215;
2975
- dIdx++;
3674
+ let hasChanged = false;
3675
+ for (let iy = 0; iy < actualH; iy++) {
3676
+ const rowOffset = (finalY + iy) * dw;
3677
+ const start = rowOffset + finalX;
3678
+ const end = start + actualW;
3679
+ for (let i = start; i < end; i++) {
3680
+ if (dst32[i] !== color) {
3681
+ dst32[i] = color;
3682
+ hasChanged = true;
2976
3683
  }
2977
- dIdx += dStride;
2978
3684
  }
2979
3685
  }
2980
- return true;
3686
+ return hasChanged;
2981
3687
  }
2982
3688
 
2983
- // src/History/PixelMutator/mutatorInvert.ts
2984
- var defaults10 = {
2985
- invertPixelData
3689
+ // src/History/PixelMutator/mutatorClear.ts
3690
+ var defaults15 = {
3691
+ fillPixelData
2986
3692
  };
2987
- var mutatorInvert = ((writer, deps = defaults10) => {
3693
+ var mutatorClear = ((writer, deps = defaults15) => {
2988
3694
  const {
2989
- invertPixelData: invertPixelData2 = defaults10.invertPixelData
3695
+ fillPixelData: fillPixelData2 = defaults15.fillPixelData
2990
3696
  } = deps;
2991
3697
  return {
2992
- invert(opts = {}) {
3698
+ clear(rect) {
2993
3699
  const target = writer.config.target;
2994
- const {
2995
- x = 0,
2996
- y = 0,
2997
- w = target.width,
2998
- h = target.height
2999
- } = opts;
3700
+ const x = rect?.x ?? 0;
3701
+ const y = rect?.y ?? 0;
3702
+ const w = rect?.w ?? target.width;
3703
+ const h = rect?.h ?? target.height;
3000
3704
  const didChange = writer.accumulator.storeRegionBeforeState(x, y, w, h);
3001
- return didChange(invertPixelData2(target, opts));
3705
+ return didChange(fillPixelData2(target, 0, x, y, w, h));
3002
3706
  }
3003
3707
  };
3004
3708
  });
3005
3709
 
3006
- // src/History/PixelMutator.ts
3007
- function makeFullPixelMutator(writer) {
3008
- return {
3009
- // @sort
3010
- ...mutatorBlendColor(writer),
3011
- ...mutatorBlendPixel(writer),
3012
- ...mutatorBlendPixelData(writer),
3013
- ...mutatorBlendPixelDataAlphaMask(writer),
3014
- ...mutatorBlendPixelDataBinaryMask(writer),
3015
- ...mutatorClear(writer),
3016
- ...mutatorFill(writer),
3017
- ...mutatorFillBinaryMask(writer),
3018
- ...mutatorFillRect(writer),
3019
- ...mutatorInvert(writer)
3020
- };
3021
- }
3022
-
3023
- // src/ImageData/resizeImageData.ts
3024
- function resizeImageData(target, newWidth, newHeight, offsetX = 0, offsetY = 0) {
3025
- const result = new ImageData(newWidth, newHeight);
3026
- const {
3027
- width: oldW,
3028
- height: oldH,
3029
- data: oldData
3030
- } = target;
3031
- const newData = result.data;
3032
- const x0 = Math.max(0, offsetX);
3033
- const y0 = Math.max(0, offsetY);
3034
- const x1 = Math.min(newWidth, offsetX + oldW);
3035
- const y1 = Math.min(newHeight, offsetY + oldH);
3036
- if (x1 <= x0 || y1 <= y0) {
3037
- return result;
3038
- }
3039
- const rowCount = y1 - y0;
3040
- const rowLen = (x1 - x0) * 4;
3041
- for (let row = 0; row < rowCount; row++) {
3042
- const dstY = y0 + row;
3043
- const srcY = dstY - offsetY;
3044
- const srcX = x0 - offsetX;
3045
- const dstStart = (dstY * newWidth + x0) * 4;
3046
- const srcStart = (srcY * oldW + srcX) * 4;
3047
- newData.set(oldData.subarray(srcStart, srcStart + rowLen), dstStart);
3048
- }
3049
- return result;
3050
- }
3051
-
3052
- // src/Internal/helpers.ts
3053
- var macro_halfAndFloor = (value) => value >> 1;
3054
-
3055
- // src/Rect/trimRectBounds.ts
3056
- function trimRectBounds(x, y, w, h, targetWidth, targetHeight, out) {
3057
- const res = out ?? {
3058
- x: 0,
3059
- y: 0,
3060
- w: 0,
3061
- h: 0
3062
- };
3063
- const left = Math.max(0, x);
3064
- const top = Math.max(0, y);
3065
- const right = Math.min(targetWidth, x + w);
3066
- const bottom = Math.min(targetHeight, y + h);
3067
- res.x = left;
3068
- res.y = top;
3069
- res.w = Math.max(0, right - left);
3070
- res.h = Math.max(0, bottom - top);
3071
- return res;
3072
- }
3073
-
3074
- // src/Paint/PaintBuffer.ts
3075
- var PaintBuffer = class {
3076
- constructor(config, tilePool) {
3077
- this.config = config;
3078
- this.tilePool = tilePool;
3079
- this.lookup = [];
3080
- }
3081
- lookup;
3082
- scratchBounds = {
3083
- x: 0,
3084
- y: 0,
3085
- w: 0,
3086
- h: 0
3087
- };
3088
- eachTileInBounds(bounds, callback) {
3089
- const {
3090
- tileShift,
3091
- targetColumns,
3092
- targetRows,
3093
- tileSize
3094
- } = this.config;
3095
- const x1 = Math.max(0, bounds.x >> tileShift);
3096
- const y1 = Math.max(0, bounds.y >> tileShift);
3097
- const x2 = Math.min(targetColumns - 1, bounds.x + bounds.w - 1 >> tileShift);
3098
- const y2 = Math.min(targetRows - 1, bounds.y + bounds.h - 1 >> tileShift);
3099
- if (x1 > x2 || y1 > y2) return;
3100
- const lookup = this.lookup;
3101
- const tilePool = this.tilePool;
3102
- for (let ty = y1; ty <= y2; ty++) {
3103
- const rowOffset = ty * targetColumns;
3104
- const tileTop = ty << tileShift;
3105
- for (let tx = x1; tx <= x2; tx++) {
3106
- const id = rowOffset + tx;
3107
- const tile = lookup[id] ?? (lookup[id] = tilePool.getTile(id, tx, ty));
3108
- const tileLeft = tx << tileShift;
3109
- const startX = bounds.x > tileLeft ? bounds.x : tileLeft;
3110
- const startY = bounds.y > tileTop ? bounds.y : tileTop;
3111
- const maskEndX = bounds.x + bounds.w;
3112
- const tileEndX = tileLeft + tileSize;
3113
- const endX = maskEndX < tileEndX ? maskEndX : tileEndX;
3114
- const maskEndY = bounds.y + bounds.h;
3115
- const tileEndY = tileTop + tileSize;
3116
- const endY = maskEndY < tileEndY ? maskEndY : tileEndY;
3117
- callback(tile, startX, startY, endX - startX, endY - startY);
3118
- }
3119
- }
3120
- }
3121
- writePaintAlphaMaskStroke(color, brush, x0, y0, x1, y1) {
3122
- const cA = color >>> 24;
3123
- if (cA === 0) return false;
3124
- const {
3125
- tileShift,
3126
- tileMask,
3127
- target
3128
- } = this.config;
3129
- const {
3130
- w: bW,
3131
- h: bH,
3132
- data: bD,
3133
- centerOffsetX,
3134
- centerOffsetY
3135
- } = brush;
3136
- const cRGB = color & 16777215;
3137
- const scratch = this.scratchBounds;
3138
- let changed = false;
3139
- forEachLinePoint(x0, y0, x1, y1, (px, py) => {
3140
- const topLeftX = Math.floor(px + centerOffsetX);
3141
- const topLeftY = Math.floor(py + centerOffsetY);
3142
- trimRectBounds(topLeftX, topLeftY, bW, bH, target.width, target.height, scratch);
3143
- if (scratch.w <= 0 || scratch.h <= 0) return;
3144
- this.eachTileInBounds(scratch, (tile, bX, bY, bW_t, bH_t) => {
3145
- const d32 = tile.data32;
3146
- let tileChanged = false;
3147
- for (let i = 0; i < bH_t; i++) {
3148
- const canvasY = bY + i;
3149
- const bOff = (canvasY - topLeftY) * bW;
3150
- const tOff = (canvasY & tileMask) << tileShift;
3151
- const dS = tOff + (bX & tileMask);
3152
- for (let j = 0; j < bW_t; j++) {
3153
- const canvasX = bX + j;
3154
- const brushA = bD[bOff + (canvasX - topLeftX)];
3155
- if (brushA === 0) continue;
3156
- const t = cA * brushA + 128;
3157
- const blendedA = t + (t >> 8) >> 8;
3158
- const idx = dS + j;
3159
- const cur = d32[idx];
3160
- if (brushA > cur >>> 24) {
3161
- const next = (cRGB | blendedA << 24) >>> 0;
3162
- if (cur !== next) {
3163
- d32[idx] = next;
3164
- tileChanged = true;
3165
- }
3166
- }
3167
- }
3168
- }
3169
- if (tileChanged) changed = true;
3170
- });
3171
- });
3172
- return changed;
3173
- }
3174
- writePaintBinaryMaskStroke(color, brush, x0, y0, x1, y1) {
3175
- const alphaIsZero = color >>> 24 === 0;
3176
- if (alphaIsZero) return false;
3177
- const {
3178
- tileShift,
3179
- tileMask,
3180
- target
3181
- } = this.config;
3182
- const {
3183
- w: bW,
3184
- h: bH,
3185
- data: bD,
3186
- centerOffsetX,
3187
- centerOffsetY
3188
- } = brush;
3189
- const scratch = this.scratchBounds;
3190
- let changed = false;
3191
- forEachLinePoint(x0, y0, x1, y1, (px, py) => {
3192
- const topLeftX = Math.floor(px + centerOffsetX);
3193
- const topLeftY = Math.floor(py + centerOffsetY);
3194
- trimRectBounds(topLeftX, topLeftY, bW, bH, target.width, target.height, scratch);
3195
- if (scratch.w <= 0 || scratch.h <= 0) return;
3196
- this.eachTileInBounds(scratch, (tile, bX, bY, bW_t, bH_t) => {
3197
- const d32 = tile.data32;
3198
- let tileChanged = false;
3199
- for (let i = 0; i < bH_t; i++) {
3200
- const canvasY = bY + i;
3201
- const bOff = (canvasY - topLeftY) * bW;
3202
- const tOff = (canvasY & tileMask) << tileShift;
3203
- const dS = tOff + (bX & tileMask);
3204
- for (let j = 0; j < bW_t; j++) {
3205
- const canvasX = bX + j;
3206
- if (bD[bOff + (canvasX - topLeftX)]) {
3207
- const idx = dS + j;
3208
- if (d32[idx] !== color) {
3209
- d32[idx] = color;
3210
- tileChanged = true;
3211
- }
3212
- }
3213
- }
3214
- }
3215
- if (tileChanged) changed = true;
3216
- });
3217
- });
3218
- return changed;
3219
- }
3220
- writeRectStroke(color, brushWidth, brushHeight, x0, y0, x1, y1) {
3221
- const alphaIsZero = color >>> 24 === 0;
3222
- if (alphaIsZero) return false;
3223
- const config = this.config;
3224
- const tileShift = config.tileShift;
3225
- const tileMask = config.tileMask;
3226
- const target = config.target;
3227
- const scratch = this.scratchBounds;
3228
- const centerOffsetX = macro_halfAndFloor(brushWidth - 1);
3229
- const centerOffsetY = macro_halfAndFloor(brushHeight - 1);
3230
- let changed = false;
3231
- forEachLinePoint(x0, y0, x1, y1, (px, py) => {
3232
- const topLeftX = Math.floor(px + centerOffsetX);
3233
- const topLeftY = Math.floor(py + centerOffsetY);
3234
- trimRectBounds(topLeftX, topLeftY, brushWidth, brushHeight, target.width, target.height, scratch);
3235
- if (scratch.w <= 0 || scratch.h <= 0) return;
3236
- this.eachTileInBounds(scratch, (tile, bX, bY, bW_t, bH_t) => {
3237
- const d32 = tile.data32;
3238
- let tileChanged = false;
3239
- for (let i = 0; i < bH_t; i++) {
3240
- const canvasY = bY + i;
3241
- const tOff = (canvasY & tileMask) << tileShift;
3242
- const dS = tOff + (bX & tileMask);
3243
- for (let j = 0; j < bW_t; j++) {
3244
- const idx = dS + j;
3245
- if (d32[idx] !== color) {
3246
- d32[idx] = color;
3247
- tileChanged = true;
3248
- }
3249
- }
3250
- }
3251
- if (tileChanged) {
3252
- changed = true;
3253
- }
3254
- });
3255
- });
3256
- return changed;
3257
- }
3258
- clear() {
3259
- this.tilePool.releaseTiles(this.lookup);
3260
- }
3261
- };
3262
-
3263
- // src/PixelTile/PixelTile.ts
3264
- var PixelTile = class {
3265
- constructor(id, tx, ty, tileSize, tileArea) {
3266
- this.id = id;
3267
- this.tx = tx;
3268
- this.ty = ty;
3269
- this.width = this.height = tileSize;
3270
- this.data32 = new Uint32Array(tileArea);
3271
- const data8 = new Uint8ClampedArray(this.data32.buffer);
3272
- this.imageData = new ImageData(data8, tileSize, tileSize);
3273
- }
3274
- data32;
3275
- width;
3276
- height;
3277
- imageData;
3278
- };
3279
-
3280
- // src/PixelTile/PixelTilePool.ts
3281
- var PixelTilePool = class {
3282
- pool;
3283
- tileSize;
3284
- tileArea;
3285
- constructor(config) {
3286
- this.pool = [];
3287
- this.tileSize = config.tileSize;
3288
- this.tileArea = config.tileArea;
3289
- }
3290
- getTile(id, tx, ty) {
3291
- let tile = this.pool.pop();
3292
- if (tile) {
3293
- tile.id = id;
3294
- tile.tx = tx;
3295
- tile.ty = ty;
3296
- tile.data32.fill(0);
3297
- return tile;
3298
- }
3299
- return new PixelTile(id, tx, ty, this.tileSize, this.tileArea);
3300
- }
3301
- releaseTile(tile) {
3302
- this.pool.push(tile);
3303
- }
3304
- releaseTiles(tiles) {
3305
- let length = tiles.length;
3306
- for (let i = 0; i < length; i++) {
3307
- let tile = tiles[i];
3308
- if (tile) {
3309
- this.pool.push(tile);
3310
- }
3311
- }
3312
- tiles.length = 0;
3313
- }
3314
- };
3315
-
3316
- // src/History/PixelWriter.ts
3317
- var PixelWriter = class {
3318
- historyManager;
3319
- accumulator;
3320
- historyActionFactory;
3321
- config;
3322
- pixelTilePool;
3323
- paintBuffer;
3324
- mutator;
3325
- blendPixelDataOpts = {
3326
- alpha: 255,
3327
- blendFn: sourceOverPerfect,
3328
- x: 0,
3329
- y: 0,
3330
- w: 0,
3331
- h: 0
3332
- };
3333
- _inProgress = false;
3334
- constructor(target, mutatorFactory, {
3335
- tileSize = 256,
3336
- maxHistorySteps = 50,
3337
- historyManager = new HistoryManager(maxHistorySteps),
3338
- historyActionFactory = makeHistoryAction,
3339
- pixelTilePool,
3340
- accumulator
3341
- } = {}) {
3342
- this.config = new PixelEngineConfig(tileSize, target);
3343
- this.historyManager = historyManager;
3344
- this.pixelTilePool = pixelTilePool ?? new PixelTilePool(this.config);
3345
- this.accumulator = accumulator ?? new PixelAccumulator(this.config, this.pixelTilePool);
3346
- this.historyActionFactory = historyActionFactory;
3347
- this.mutator = mutatorFactory(this);
3348
- this.paintBuffer = new PaintBuffer(this.config, this.pixelTilePool);
3349
- }
3350
- /**
3351
- * Executes `transaction` and commits the resulting pixel changes as a single
3352
- * undoable history action.
3353
- *
3354
- * - If `transaction` throws, all accumulated changes are rolled back and the error
3355
- * is re-thrown. No action is committed.
3356
- * - If `transaction` completes without modifying any pixels, no action is committed.
3357
- * - `withHistory` is not re-entrant. Calling it again from inside `transaction` will
3358
- * throw immediately to prevent silent data loss from a nested extractPatch.
3359
- *
3360
- * @param transaction Callback to be executed inside the transaction.
3361
- * @param after Called after both undo and redo — use for generic change notifications.
3362
- * @param afterUndo Called after undo only — use for dimension or state changes specific to undo.
3363
- * @param afterRedo Called after redo only.
3364
- */
3365
- withHistory(transaction, after, afterUndo, afterRedo) {
3366
- if (this._inProgress) {
3367
- throw new Error("withHistory is not re-entrant \u2014 commit or rollback the current operation first");
3368
- }
3369
- this._inProgress = true;
3370
- try {
3371
- transaction(this.mutator);
3372
- } catch (e) {
3373
- this.accumulator.rollbackAfterError();
3374
- throw e;
3375
- } finally {
3376
- this._inProgress = false;
3377
- }
3378
- if (this.accumulator.beforeTiles.length === 0) return;
3379
- const patch = this.accumulator.extractPatch();
3380
- const action = this.historyActionFactory(this, patch, after, afterUndo, afterRedo);
3381
- this.historyManager.commit(action);
3382
- }
3383
- resize(newWidth, newHeight, offsetX = 0, offsetY = 0, after, afterUndo, afterRedo, resizeImageDataFn = resizeImageData) {
3384
- if (this._inProgress) {
3385
- throw new Error("Cannot resize inside a withHistory callback");
3386
- }
3387
- if (this.accumulator.beforeTiles.length > 0) {
3388
- throw new Error("Cannot resize with an open accumulator \u2014 commit or rollback first");
3389
- }
3390
- const config = this.config;
3391
- const target = config.target;
3392
- const beforeImageData = target.imageData;
3393
- const afterImageData = resizeImageDataFn(beforeImageData, newWidth, newHeight, offsetX, offsetY);
3394
- target.set(afterImageData);
3395
- this.historyManager.commit({
3396
- undo: () => {
3397
- target.set(beforeImageData);
3398
- afterUndo?.(beforeImageData);
3399
- after?.(beforeImageData);
3400
- },
3401
- redo: () => {
3402
- target.set(afterImageData);
3403
- afterRedo?.(afterImageData);
3404
- after?.(afterImageData);
3405
- }
3406
- });
3407
- }
3408
- commitPaintBuffer(alpha = 255, blendFn = sourceOverPerfect, blendPixelDataFn = blendPixelData) {
3409
- const paintBuffer = this.paintBuffer;
3410
- const tileShift = paintBuffer.config.tileShift;
3411
- const lookup = paintBuffer.lookup;
3412
- const opts = this.blendPixelDataOpts;
3413
- opts.alpha = alpha;
3414
- opts.blendFn = blendFn;
3415
- for (let i = 0; i < lookup.length; i++) {
3416
- const tile = lookup[i];
3417
- if (tile) {
3418
- const didChange = this.accumulator.storeTileBeforeState(tile.id, tile.tx, tile.ty);
3419
- const dx = tile.tx << tileShift;
3420
- const dy = tile.ty << tileShift;
3421
- opts.x = dx;
3422
- opts.y = dy;
3423
- opts.w = tile.width;
3424
- opts.h = tile.height;
3425
- didChange(blendPixelDataFn(this.config.target, tile, opts));
3426
- }
3427
- }
3428
- paintBuffer.clear();
3429
- }
3710
+ // src/History/PixelMutator/mutatorFill.ts
3711
+ var defaults16 = {
3712
+ fillPixelData
3430
3713
  };
3431
-
3432
- // src/PixelData/applyAlphaMaskToPixelData.ts
3433
- function applyAlphaMaskToPixelData(dst, mask, opts = {}) {
3714
+ var mutatorFill = ((writer, deps = defaults16) => {
3434
3715
  const {
3435
- x: targetX = 0,
3436
- y: targetY = 0,
3437
- w: width = dst.width,
3438
- h: height = dst.height,
3439
- alpha: globalAlpha = 255,
3440
- mx = 0,
3441
- my = 0,
3442
- invertMask = false
3443
- } = opts;
3444
- if (globalAlpha === 0) return false;
3445
- let x = targetX;
3446
- let y = targetY;
3447
- let w = width;
3448
- let h = height;
3449
- if (x < 0) {
3450
- w += x;
3451
- x = 0;
3452
- }
3453
- if (y < 0) {
3454
- h += y;
3455
- y = 0;
3456
- }
3457
- w = Math.min(w, dst.width - x);
3458
- h = Math.min(h, dst.height - y);
3459
- if (w <= 0) return false;
3460
- if (h <= 0) return false;
3461
- const mPitch = mask.w;
3462
- if (mPitch <= 0) return false;
3463
- const startX = mx + (x - targetX);
3464
- const startY = my + (y - targetY);
3465
- const sX0 = Math.max(0, startX);
3466
- const sY0 = Math.max(0, startY);
3467
- const sX1 = Math.min(mPitch, startX + w);
3468
- const sY1 = Math.min(mask.h, startY + h);
3469
- const finalW = sX1 - sX0;
3470
- const finalH = sY1 - sY0;
3471
- if (finalW <= 0) return false;
3472
- if (finalH <= 0) return false;
3473
- const xShift = sX0 - startX;
3474
- const yShift = sY0 - startY;
3475
- const dst32 = dst.data32;
3476
- const dw = dst.width;
3477
- const dStride = dw - finalW;
3478
- const mStride = mPitch - finalW;
3479
- const maskData = mask.data;
3480
- let dIdx = (y + yShift) * dw + (x + xShift);
3481
- let mIdx = sY0 * mPitch + sX0;
3482
- let didChange = false;
3483
- for (let iy = 0; iy < h; iy++) {
3484
- for (let ix = 0; ix < w; ix++) {
3485
- const mVal = maskData[mIdx];
3486
- const effectiveM = invertMask ? 255 - mVal : mVal;
3487
- let weight = 0;
3488
- if (effectiveM === 0) {
3489
- weight = 0;
3490
- } else if (effectiveM === 255) {
3491
- weight = globalAlpha;
3492
- } else if (globalAlpha === 255) {
3493
- weight = effectiveM;
3494
- } else {
3495
- weight = effectiveM * globalAlpha + 128 >> 8;
3496
- }
3497
- if (weight === 0) {
3498
- dst32[dIdx] = (dst32[dIdx] & 16777215) >>> 0;
3499
- didChange = true;
3500
- } else if (weight !== 255) {
3501
- const d = dst32[dIdx];
3502
- const da = d >>> 24;
3503
- if (da !== 0) {
3504
- const finalAlpha = da === 255 ? weight : da * weight + 128 >> 8;
3505
- const current = dst32[dIdx];
3506
- const next = (d & 16777215 | finalAlpha << 24) >>> 0;
3507
- if (current !== next) {
3508
- dst32[dIdx] = next;
3509
- didChange = true;
3510
- }
3511
- }
3512
- }
3513
- dIdx++;
3514
- mIdx++;
3716
+ fillPixelData: fillPixelData2 = defaults16.fillPixelData
3717
+ } = deps;
3718
+ return {
3719
+ fill(color, x = 0, y = 0, w = writer.config.target.width, h = writer.config.target.height) {
3720
+ const target = writer.config.target;
3721
+ const didChange = writer.accumulator.storeRegionBeforeState(x, y, w, h);
3722
+ return didChange(fillPixelData2(target, color, x, y, w, h));
3515
3723
  }
3516
- dIdx += dStride;
3517
- mIdx += mStride;
3518
- }
3519
- return didChange;
3520
- }
3521
-
3522
- // src/History/PixelMutator/mutatorApplyAlphaMask.ts
3523
- var defaults11 = {
3524
- applyAlphaMaskToPixelData
3525
- };
3526
- var mutatorApplyAlphaMask = ((writer, deps = defaults11) => {
3724
+ };
3725
+ });
3726
+ var mutatorFillRect = ((writer, deps = defaults16) => {
3527
3727
  const {
3528
- applyAlphaMaskToPixelData: applyAlphaMaskToPixelData2 = defaults11.applyAlphaMaskToPixelData
3728
+ fillPixelData: fillPixelData2 = defaults16.fillPixelData
3529
3729
  } = deps;
3530
3730
  return {
3531
- applyAlphaMask(mask, opts = {}) {
3532
- let target = writer.config.target;
3533
- const {
3534
- x = 0,
3535
- y = 0,
3536
- w = target.width,
3537
- h = target.height
3538
- } = opts;
3539
- const didChange = writer.accumulator.storeRegionBeforeState(x, y, w, h);
3540
- return didChange(applyAlphaMaskToPixelData2(target, mask, opts));
3731
+ fillRect(color, rect) {
3732
+ const target = writer.config.target;
3733
+ const didChange = writer.accumulator.storeRegionBeforeState(rect.x, rect.y, rect.w, rect.h);
3734
+ return didChange(fillPixelData2(target, color, rect.x, rect.y, rect.w, rect.h));
3541
3735
  }
3542
3736
  };
3543
3737
  });
3544
3738
 
3545
- // src/PixelData/applyBinaryMaskToPixelData.ts
3546
- function applyBinaryMaskToPixelData(dst, mask, opts = {}) {
3739
+ // src/PixelData/fillPixelDataBinaryMask.ts
3740
+ var SCRATCH_RECT2 = makeClippedRect();
3741
+ function fillPixelDataBinaryMask(target, color, mask, x = 0, y = 0) {
3742
+ const maskW = mask.w;
3743
+ const maskH = mask.h;
3744
+ const clip = resolveRectClipping(x, y, maskW, maskH, target.width, target.height, SCRATCH_RECT2);
3745
+ if (!clip.inBounds) return false;
3547
3746
  const {
3548
- x: targetX = 0,
3549
- y: targetY = 0,
3550
- w: width = dst.width,
3551
- h: height = dst.height,
3552
- alpha: globalAlpha = 255,
3553
- mx = 0,
3554
- my = 0,
3555
- invertMask = false
3556
- } = opts;
3557
- if (globalAlpha === 0) return false;
3558
- let x = targetX;
3559
- let y = targetY;
3560
- let w = width;
3561
- let h = height;
3562
- if (x < 0) {
3563
- w += x;
3564
- x = 0;
3565
- }
3566
- if (y < 0) {
3567
- h += y;
3568
- y = 0;
3569
- }
3570
- w = Math.min(w, dst.width - x);
3571
- h = Math.min(h, dst.height - y);
3572
- if (w <= 0 || h <= 0) return false;
3573
- const mPitch = mask.w;
3574
- if (mPitch <= 0) return false;
3575
- const startX = mx + (x - targetX);
3576
- const startY = my + (y - targetY);
3577
- const sX0 = Math.max(0, startX);
3578
- const sY0 = Math.max(0, startY);
3579
- const sX1 = Math.min(mPitch, startX + w);
3580
- const sY1 = Math.min(mask.h, startY + h);
3581
- const finalW = sX1 - sX0;
3582
- const finalH = sY1 - sY0;
3583
- if (finalW <= 0 || finalH <= 0) {
3584
- return false;
3585
- }
3586
- const xShift = sX0 - startX;
3587
- const yShift = sY0 - startY;
3588
- const dst32 = dst.data32;
3589
- const dw = dst.width;
3590
- const dStride = dw - finalW;
3591
- const mStride = mPitch - finalW;
3747
+ x: finalX,
3748
+ y: finalY,
3749
+ w: actualW,
3750
+ h: actualH
3751
+ } = clip;
3592
3752
  const maskData = mask.data;
3593
- let dIdx = (y + yShift) * dw + (x + xShift);
3594
- let mIdx = sY0 * mPitch + sX0;
3595
- let didChange = false;
3596
- for (let iy = 0; iy < finalH; iy++) {
3597
- for (let ix = 0; ix < finalW; ix++) {
3598
- const mVal = maskData[mIdx];
3599
- const isMaskedOut = invertMask ? mVal !== 0 : mVal === 0;
3600
- if (isMaskedOut) {
3601
- const current = dst32[dIdx];
3602
- const next = (current & 16777215) >>> 0;
3603
- if (current !== next) {
3604
- dst32[dIdx] = next;
3605
- didChange = true;
3606
- }
3607
- } else if (globalAlpha !== 255) {
3608
- const d = dst32[dIdx];
3609
- const da = d >>> 24;
3610
- if (da !== 0) {
3611
- const finalAlpha = da === 255 ? globalAlpha : da * globalAlpha + 128 >> 8;
3612
- const next = (d & 16777215 | finalAlpha << 24) >>> 0;
3613
- if (d !== next) {
3614
- dst32[dIdx] = next;
3615
- didChange = true;
3616
- }
3753
+ const dst32 = target.data32;
3754
+ const dw = target.width;
3755
+ let hasChanged = false;
3756
+ for (let iy = 0; iy < actualH; iy++) {
3757
+ const currentY = finalY + iy;
3758
+ const maskY = currentY - y;
3759
+ const maskOffset = maskY * maskW;
3760
+ const dstRowOffset = currentY * dw;
3761
+ for (let ix = 0; ix < actualW; ix++) {
3762
+ const currentX = finalX + ix;
3763
+ const maskX = currentX - x;
3764
+ const maskIndex = maskOffset + maskX;
3765
+ if (maskData[maskIndex]) {
3766
+ const current = dst32[dstRowOffset + currentX];
3767
+ if (current !== color) {
3768
+ dst32[dstRowOffset + currentX] = color;
3769
+ hasChanged = true;
3617
3770
  }
3618
3771
  }
3619
- dIdx++;
3620
- mIdx++;
3621
3772
  }
3622
- dIdx += dStride;
3623
- mIdx += mStride;
3624
3773
  }
3625
- return didChange;
3774
+ return hasChanged;
3626
3775
  }
3627
3776
 
3628
- // src/History/PixelMutator/mutatorApplyBinaryMask.ts
3629
- var defaults12 = {
3630
- applyBinaryMaskToPixelData
3777
+ // src/History/PixelMutator/mutatorFillBinaryMask.ts
3778
+ var defaults17 = {
3779
+ fillPixelDataBinaryMask
3631
3780
  };
3632
- var mutatorApplyBinaryMask = ((writer, deps = defaults12) => {
3781
+ var mutatorFillBinaryMask = ((writer, deps = defaults17) => {
3633
3782
  const {
3634
- applyBinaryMaskToPixelData: applyBinaryMaskToPixelData2 = defaults12.applyBinaryMaskToPixelData
3783
+ fillPixelDataBinaryMask: fillPixelDataBinaryMask2 = defaults17.fillPixelDataBinaryMask
3635
3784
  } = deps;
3636
3785
  return {
3637
- applyBinaryMask(mask, opts = {}) {
3638
- let target = writer.config.target;
3639
- const {
3640
- x = 0,
3641
- y = 0,
3642
- w = target.width,
3643
- h = target.height
3644
- } = opts;
3645
- const didChange = writer.accumulator.storeRegionBeforeState(x, y, w, h);
3646
- return didChange(applyBinaryMaskToPixelData2(target, mask, opts));
3786
+ fillBinaryMask(color, mask, x = 0, y = 0) {
3787
+ const didChange = writer.accumulator.storeRegionBeforeState(x, y, mask.w, mask.h);
3788
+ return didChange(fillPixelDataBinaryMask2(writer.config.target, color, mask, x, y));
3647
3789
  }
3648
3790
  };
3649
3791
  });
3650
3792
 
3651
- // src/PixelData/blendColorPixelDataAlphaMask.ts
3652
- function blendColorPixelDataAlphaMask(dst, color, mask, opts = {}) {
3653
- const targetX = opts.x ?? 0;
3654
- const targetY = opts.y ?? 0;
3655
- const w = opts.w ?? mask.w;
3656
- const h = opts.h ?? mask.h;
3657
- const globalAlpha = opts.alpha ?? 255;
3658
- const blendFn = opts.blendFn ?? sourceOverPerfect;
3659
- const mx = opts.mx ?? 0;
3660
- const my = opts.my ?? 0;
3661
- const invertMask = opts.invertMask ?? false;
3662
- if (globalAlpha === 0) return false;
3663
- const baseSrcAlpha = color >>> 24;
3664
- const isOverwrite = blendFn.isOverwrite || false;
3665
- if (baseSrcAlpha === 0 && !isOverwrite) return false;
3666
- let x = targetX;
3667
- let y = targetY;
3668
- let actualW = w;
3669
- let actualH = h;
3670
- if (x < 0) {
3671
- actualW += x;
3672
- x = 0;
3673
- }
3674
- if (y < 0) {
3675
- actualH += y;
3676
- y = 0;
3677
- }
3678
- actualW = Math.min(actualW, dst.width - x);
3679
- actualH = Math.min(actualH, dst.height - y);
3680
- if (actualW <= 0 || actualH <= 0) return false;
3681
- const dx = x - targetX | 0;
3682
- const dy = y - targetY | 0;
3683
- const dst32 = dst.data32;
3684
- const dw = dst.width;
3685
- const mPitch = mask.w;
3686
- const maskData = mask.data;
3687
- let dIdx = y * dw + x | 0;
3688
- let mIdx = (my + dy) * mPitch + (mx + dx) | 0;
3689
- const dStride = dw - actualW | 0;
3690
- const mStride = mPitch - actualW | 0;
3691
- const isOpaque = globalAlpha === 255;
3692
- const colorRGB = color & 16777215;
3693
- let didChange = false;
3694
- for (let iy = 0; iy < actualH; iy++) {
3695
- for (let ix = 0; ix < actualW; ix++) {
3696
- const mVal = maskData[mIdx];
3697
- const effM = invertMask ? 255 - mVal : mVal;
3698
- if (effM === 0) {
3699
- dIdx++;
3700
- mIdx++;
3701
- continue;
3702
- }
3703
- let weight = globalAlpha;
3704
- if (isOpaque) {
3705
- weight = effM;
3706
- } else if (effM !== 255) {
3707
- weight = effM * globalAlpha + 128 >> 8;
3708
- }
3709
- if (weight === 0) {
3710
- dIdx++;
3711
- mIdx++;
3712
- continue;
3713
- }
3714
- let finalCol = color;
3715
- if (weight < 255) {
3716
- const a = baseSrcAlpha * weight + 128 >> 8;
3717
- if (a === 0 && !isOverwrite) {
3718
- dIdx++;
3719
- mIdx++;
3720
- continue;
3793
+ // src/PixelData/invertPixelData.ts
3794
+ var SCRATCH_RECT3 = makeClippedRect();
3795
+ function invertPixelData(target, opts) {
3796
+ const mask = opts?.mask;
3797
+ const targetX = opts?.x ?? 0;
3798
+ const targetY = opts?.y ?? 0;
3799
+ const mx = opts?.mx ?? 0;
3800
+ const my = opts?.my ?? 0;
3801
+ const width = opts?.w ?? target.width;
3802
+ const height = opts?.h ?? target.height;
3803
+ const invertMask = opts?.invertMask ?? false;
3804
+ const clip = resolveRectClipping(targetX, targetY, width, height, target.width, target.height, SCRATCH_RECT3);
3805
+ if (!clip.inBounds) return false;
3806
+ const {
3807
+ x,
3808
+ y,
3809
+ w: actualW,
3810
+ h: actualH
3811
+ } = clip;
3812
+ const dst32 = target.data32;
3813
+ const dw = target.width;
3814
+ const mPitch = mask?.w ?? width;
3815
+ const dx = x - targetX;
3816
+ const dy = y - targetY;
3817
+ let dIdx = y * dw + x;
3818
+ let mIdx = (my + dy) * mPitch + (mx + dx);
3819
+ const dStride = dw - actualW;
3820
+ const mStride = mPitch - actualW;
3821
+ if (mask) {
3822
+ const maskData = mask.data;
3823
+ for (let iy = 0; iy < actualH; iy++) {
3824
+ for (let ix = 0; ix < actualW; ix++) {
3825
+ const mVal = maskData[mIdx];
3826
+ const isHit = invertMask ? mVal === 0 : mVal === 1;
3827
+ if (isHit) {
3828
+ dst32[dIdx] = dst32[dIdx] ^ 16777215;
3721
3829
  }
3722
- finalCol = (colorRGB | a << 24) >>> 0;
3723
- }
3724
- const current = dst32[dIdx];
3725
- const next = blendFn(finalCol, current);
3726
- if (current !== next) {
3727
- dst32[dIdx] = next;
3728
- didChange = true;
3729
- }
3730
- dIdx++;
3731
- mIdx++;
3732
- }
3733
- dIdx += dStride;
3734
- mIdx += mStride;
3735
- }
3736
- return didChange;
3737
- }
3738
-
3739
- // src/PixelData/blendColorPixelDataBinaryMask.ts
3740
- function blendColorPixelDataBinaryMask(dst, color, mask, opts = {}) {
3741
- const targetX = opts.x ?? 0;
3742
- const targetY = opts.y ?? 0;
3743
- let w = opts.w ?? mask.w;
3744
- let h = opts.h ?? mask.h;
3745
- const globalAlpha = opts.alpha ?? 255;
3746
- const blendFn = opts.blendFn ?? sourceOverPerfect;
3747
- const mx = opts.mx ?? 0;
3748
- const my = opts.my ?? 0;
3749
- const invertMask = opts.invertMask ?? false;
3750
- if (globalAlpha === 0) return false;
3751
- const baseSrcAlpha = color >>> 24;
3752
- const isOverwrite = blendFn.isOverwrite || false;
3753
- if (baseSrcAlpha === 0 && !isOverwrite) return false;
3754
- let x = targetX;
3755
- let y = targetY;
3756
- if (x < 0) {
3757
- w += x;
3758
- x = 0;
3759
- }
3760
- if (y < 0) {
3761
- h += y;
3762
- y = 0;
3763
- }
3764
- const actualW = Math.min(w, dst.width - x);
3765
- const actualH = Math.min(h, dst.height - y);
3766
- if (actualW <= 0 || actualH <= 0) return false;
3767
- let baseColorWithGlobalAlpha = color;
3768
- if (globalAlpha < 255) {
3769
- const a = baseSrcAlpha * globalAlpha + 128 >> 8;
3770
- if (a === 0 && !isOverwrite) return false;
3771
- baseColorWithGlobalAlpha = (color & 16777215 | a << 24) >>> 0;
3772
- }
3773
- const dx = x - targetX | 0;
3774
- const dy = y - targetY | 0;
3775
- const dst32 = dst.data32;
3776
- const dw = dst.width;
3777
- const mPitch = mask.w;
3778
- const maskData = mask.data;
3779
- let dIdx = y * dw + x | 0;
3780
- let mIdx = (my + dy) * mPitch + (mx + dx) | 0;
3781
- const dStride = dw - actualW | 0;
3782
- const mStride = mPitch - actualW | 0;
3783
- const skipVal = invertMask ? 1 : 0;
3784
- let didChange = false;
3785
- for (let iy = 0; iy < actualH; iy++) {
3786
- for (let ix = 0; ix < actualW; ix++) {
3787
- if (maskData[mIdx] === skipVal) {
3788
3830
  dIdx++;
3789
3831
  mIdx++;
3790
- continue;
3791
3832
  }
3792
- const current = dst32[dIdx];
3793
- const next = blendFn(baseColorWithGlobalAlpha, current);
3794
- if (current !== next) {
3795
- dst32[dIdx] = next;
3796
- didChange = true;
3833
+ dIdx += dStride;
3834
+ mIdx += mStride;
3835
+ }
3836
+ } else {
3837
+ for (let iy = 0; iy < actualH; iy++) {
3838
+ for (let ix = 0; ix < actualW; ix++) {
3839
+ dst32[dIdx] = dst32[dIdx] ^ 16777215;
3840
+ dIdx++;
3797
3841
  }
3798
- dIdx++;
3799
- mIdx++;
3842
+ dIdx += dStride;
3800
3843
  }
3801
- dIdx += dStride;
3802
- mIdx += mStride;
3803
3844
  }
3804
- return didChange;
3845
+ return true;
3805
3846
  }
3806
3847
 
3807
- // src/History/PixelMutator/mutatorBlendPaintMask.ts
3808
- var defaults13 = {
3809
- blendColorPixelDataAlphaMask,
3810
- blendColorPixelDataBinaryMask
3848
+ // src/History/PixelMutator/mutatorInvert.ts
3849
+ var defaults18 = {
3850
+ invertPixelData
3811
3851
  };
3812
- var mutatorBlendPaintMask = ((writer, deps = defaults13) => {
3852
+ var mutatorInvert = ((writer, deps = defaults18) => {
3813
3853
  const {
3814
- blendColorPixelDataBinaryMask: blendColorPixelDataBinaryMask2 = defaults13.blendColorPixelDataBinaryMask,
3815
- blendColorPixelDataAlphaMask: blendColorPixelDataAlphaMask2 = defaults13.blendColorPixelDataAlphaMask
3854
+ invertPixelData: invertPixelData2 = defaults18.invertPixelData
3816
3855
  } = deps;
3817
- const OPTS = {
3818
- x: 0,
3819
- y: 0,
3820
- blendFn: sourceOverPerfect,
3821
- alpha: 255
3822
- };
3823
3856
  return {
3824
- blendColorPaintMask(color, mask, x, y, alpha = 255, blendFn = sourceOverPerfect) {
3825
- const tx = x + mask.centerOffsetX;
3826
- const ty = y + mask.centerOffsetY;
3827
- const didChange = writer.accumulator.storeRegionBeforeState(tx, ty, mask.w, mask.h);
3828
- OPTS.x = tx;
3829
- OPTS.y = ty;
3830
- OPTS.alpha = alpha;
3831
- OPTS.blendFn = blendFn;
3832
- if (mask.type === 1 /* BINARY */) {
3833
- return didChange(blendColorPixelDataBinaryMask2(writer.config.target, color, mask, OPTS));
3834
- } else {
3835
- return didChange(blendColorPixelDataAlphaMask2(writer.config.target, color, mask, OPTS));
3836
- }
3857
+ invert(opts) {
3858
+ const target = writer.config.target;
3859
+ const x = opts?.x ?? 0;
3860
+ const y = opts?.y ?? 0;
3861
+ const w = opts?.w ?? target.width;
3862
+ const h = opts?.h ?? target.height;
3863
+ const didChange = writer.accumulator.storeRegionBeforeState(x, y, w, h);
3864
+ return didChange(invertPixelData2(target, opts));
3837
3865
  }
3838
3866
  };
3839
3867
  });
3840
3868
 
3869
+ // src/History/PixelMutator.ts
3870
+ function makeFullPixelMutator(writer) {
3871
+ return {
3872
+ // @sort
3873
+ ...mutatorApplyAlphaMask(writer),
3874
+ ...mutatorApplyBinaryMask(writer),
3875
+ ...mutatorApplyMask(writer),
3876
+ ...mutatorBlendAlphaMask(writer),
3877
+ ...mutatorBlendBinaryMask(writer),
3878
+ ...mutatorBlendColor(writer),
3879
+ ...mutatorBlendColorPaintAlphaMask(writer),
3880
+ ...mutatorBlendColorPaintBinaryMask(writer),
3881
+ ...mutatorBlendColorPaintMask(writer),
3882
+ ...mutatorBlendMask(writer),
3883
+ ...mutatorBlendPaintRect(writer),
3884
+ ...mutatorBlendPixel(writer),
3885
+ ...mutatorBlendPixelData(writer),
3886
+ ...mutatorClear(writer),
3887
+ ...mutatorFill(writer),
3888
+ ...mutatorFillBinaryMask(writer),
3889
+ ...mutatorFillRect(writer),
3890
+ ...mutatorInvert(writer)
3891
+ };
3892
+ }
3893
+
3894
+ // src/ImageData/ImageDataLike.ts
3895
+ function makeImageDataLike(width, height, data) {
3896
+ const size = width * height * 4;
3897
+ const buffer = data ? new Uint8ClampedArray(data.buffer, data.byteOffset, size) : new Uint8ClampedArray(size);
3898
+ return {
3899
+ width,
3900
+ height,
3901
+ data: buffer
3902
+ };
3903
+ }
3904
+
3905
+ // src/ImageData/ReusableImageData.ts
3906
+ function makeReusableImageData() {
3907
+ let imageData = null;
3908
+ return function getReusableImageData(width, height) {
3909
+ if (imageData === null || imageData.width !== width || imageData.height !== height) {
3910
+ imageData = new ImageData(width, height);
3911
+ } else {
3912
+ imageData.data.fill(0);
3913
+ }
3914
+ return imageData;
3915
+ };
3916
+ }
3917
+
3841
3918
  // src/ImageData/copyImageData.ts
3842
3919
  function copyImageData({
3843
3920
  data,
@@ -3858,17 +3935,6 @@ function copyImageDataLike({
3858
3935
  };
3859
3936
  }
3860
3937
 
3861
- // src/ImageData/ImageDataLike.ts
3862
- function makeImageDataLike(width, height, data) {
3863
- const size = width * height * 4;
3864
- const buffer = data ? new Uint8ClampedArray(data.buffer, data.byteOffset, size) : new Uint8ClampedArray(size);
3865
- return {
3866
- width,
3867
- height,
3868
- data: buffer
3869
- };
3870
- }
3871
-
3872
3938
  // src/ImageData/imageDataToAlphaMaskBuffer.ts
3873
3939
  function imageDataToAlphaMaskBuffer(imageData) {
3874
3940
  const {
@@ -3959,19 +4025,6 @@ function resampleImageData(source, factor) {
3959
4025
  return new ImageData(uint8ClampedArray, width, height);
3960
4026
  }
3961
4027
 
3962
- // src/ImageData/ReusableImageData.ts
3963
- function makeReusableImageData() {
3964
- let imageData = null;
3965
- return function getReusableImageData(width, height) {
3966
- if (imageData === null || imageData.width !== width || imageData.height !== height) {
3967
- imageData = new ImageData(width, height);
3968
- } else {
3969
- imageData.data.fill(0);
3970
- }
3971
- return imageData;
3972
- };
3973
- }
3974
-
3975
4028
  // src/ImageData/serialization.ts
3976
4029
  function base64EncodeArrayBuffer(buffer) {
3977
4030
  const uint8 = new Uint8Array(buffer);
@@ -4126,18 +4179,6 @@ function writeImageDataBuffer(target, data, _x, _y, _w, _h) {
4126
4179
  }
4127
4180
  }
4128
4181
 
4129
- // src/IndexedImage/getIndexedImageColorCounts.ts
4130
- function getIndexedImageColorCounts(indexedImage) {
4131
- const data = indexedImage.data;
4132
- const palette = indexedImage.palette;
4133
- const frequencies = new Int32Array(palette.length);
4134
- for (let i = 0; i < data.length; i++) {
4135
- const colorIndex = data[i];
4136
- frequencies[colorIndex]++;
4137
- }
4138
- return frequencies;
4139
- }
4140
-
4141
4182
  // src/IndexedImage/IndexedImage.ts
4142
4183
  var IndexedImage = class _IndexedImage {
4143
4184
  /** The width of the image in pixels. */
@@ -4216,6 +4257,18 @@ var IndexedImage = class _IndexedImage {
4216
4257
  }
4217
4258
  };
4218
4259
 
4260
+ // src/IndexedImage/getIndexedImageColorCounts.ts
4261
+ function getIndexedImageColorCounts(indexedImage) {
4262
+ const data = indexedImage.data;
4263
+ const palette = indexedImage.palette;
4264
+ const frequencies = new Int32Array(palette.length);
4265
+ for (let i = 0; i < data.length; i++) {
4266
+ const colorIndex = data[i];
4267
+ frequencies[colorIndex]++;
4268
+ }
4269
+ return frequencies;
4270
+ }
4271
+
4219
4272
  // src/IndexedImage/indexedImageToAverageColor.ts
4220
4273
  function indexedImageToAverageColor(indexedImage, includeTransparent = false) {
4221
4274
  const {
@@ -4374,16 +4427,14 @@ function makeBinaryMask(w, h, data) {
4374
4427
  }
4375
4428
 
4376
4429
  // src/Mask/applyBinaryMaskToAlphaMask.ts
4377
- function applyBinaryMaskToAlphaMask(alphaMaskDst, binaryMaskSrc, opts = {}) {
4378
- const {
4379
- x: targetX = 0,
4380
- y: targetY = 0,
4381
- w: reqWidth = 0,
4382
- h: reqHeight = 0,
4383
- mx = 0,
4384
- my = 0,
4385
- invertMask = false
4386
- } = opts;
4430
+ function applyBinaryMaskToAlphaMask(alphaMaskDst, binaryMaskSrc, opts) {
4431
+ const targetX = opts?.x ?? 0;
4432
+ const targetY = opts?.y ?? 0;
4433
+ const reqWidth = opts?.w ?? 0;
4434
+ const reqHeight = opts?.h ?? 0;
4435
+ const mx = opts?.mx ?? 0;
4436
+ const my = opts?.my ?? 0;
4437
+ const invertMask = opts?.invertMask ?? false;
4387
4438
  const dstWidth = alphaMaskDst.w;
4388
4439
  if (dstWidth <= 0) return;
4389
4440
  if (binaryMaskSrc.data.length === 0) return;
@@ -4796,14 +4847,156 @@ function pushPiece(dest, r, x, y, w, h) {
4796
4847
  for (let row = 0; row < h; row++) {
4797
4848
  data.set(r.data.subarray((ly + row) * r.w + lx, (ly + row) * r.w + lx + w), row * w);
4798
4849
  }
4799
- dest.push({
4800
- x,
4801
- y,
4802
- w,
4803
- h,
4850
+ dest.push({
4851
+ x,
4852
+ y,
4853
+ w,
4854
+ h,
4855
+ data,
4856
+ type: 1 /* BINARY */
4857
+ });
4858
+ }
4859
+
4860
+ // src/Paint/PaintBufferCanvasRenderer.ts
4861
+ function makePaintBufferCanvasRenderer(paintBuffer, offscreenCanvasClass = OffscreenCanvas) {
4862
+ const config = paintBuffer.config;
4863
+ const tileSize = config.tileSize;
4864
+ const tileShift = config.tileShift;
4865
+ const lookup = paintBuffer.lookup;
4866
+ const canvas = new offscreenCanvasClass(tileSize, tileSize);
4867
+ const ctx = canvas.getContext("2d");
4868
+ if (!ctx) throw new Error(CANVAS_CTX_FAILED);
4869
+ ctx.imageSmoothingEnabled = false;
4870
+ return function drawPaintBuffer(targetCtx, alpha = 255, compOperation = "source-over") {
4871
+ targetCtx.globalAlpha = alpha / 255;
4872
+ targetCtx.globalCompositeOperation = compOperation;
4873
+ for (let i = 0; i < lookup.length; i++) {
4874
+ const tile = lookup[i];
4875
+ if (tile) {
4876
+ const dx = tile.tx << tileShift;
4877
+ const dy = tile.ty << tileShift;
4878
+ ctx.putImageData(tile.imageData, 0, 0);
4879
+ targetCtx.drawImage(canvas, dx, dy);
4880
+ }
4881
+ }
4882
+ targetCtx.globalAlpha = 1;
4883
+ targetCtx.globalCompositeOperation = "source-over";
4884
+ };
4885
+ }
4886
+
4887
+ // src/Paint/makeCirclePaintAlphaMask.ts
4888
+ function makeCirclePaintAlphaMask(size, fallOff = (d) => d) {
4889
+ const area = size * size;
4890
+ const data = new Uint8Array(area);
4891
+ const radius = size / 2;
4892
+ const invR = 1 / radius;
4893
+ const centerOffset = -Math.ceil(radius - 0.5);
4894
+ for (let y = 0; y < size; y++) {
4895
+ const rowOffset = y * size;
4896
+ const dy = y - radius + 0.5;
4897
+ const dy2 = dy * dy;
4898
+ for (let x = 0; x < size; x++) {
4899
+ const dx = x - radius + 0.5;
4900
+ const distSqr = dx * dx + dy2;
4901
+ if (distSqr <= radius * radius) {
4902
+ const dist = Math.sqrt(distSqr) * invR;
4903
+ const strength = fallOff(1 - dist);
4904
+ if (strength > 0) {
4905
+ const intensity = strength * 255 | 0;
4906
+ data[rowOffset + x] = Math.max(0, Math.min(255, intensity));
4907
+ }
4908
+ }
4909
+ }
4910
+ }
4911
+ return {
4912
+ type: 0 /* ALPHA */,
4913
+ data,
4914
+ w: size,
4915
+ h: size,
4916
+ centerOffsetX: centerOffset,
4917
+ centerOffsetY: centerOffset
4918
+ };
4919
+ }
4920
+
4921
+ // src/Paint/makeCirclePaintBinaryMask.ts
4922
+ function makeCirclePaintBinaryMask(size) {
4923
+ const area = size * size;
4924
+ const data = new Uint8Array(area);
4925
+ const radius = size / 2;
4926
+ const centerOffset = -Math.ceil(radius - 0.5);
4927
+ for (let y = 0; y < size; y++) {
4928
+ for (let x = 0; x < size; x++) {
4929
+ const dx = x - radius + 0.5;
4930
+ const dy = y - radius + 0.5;
4931
+ const distSqr = dx * dx + dy * dy;
4932
+ if (distSqr <= radius * radius) {
4933
+ data[y * size + x] = 1;
4934
+ }
4935
+ }
4936
+ }
4937
+ return {
4938
+ type: 1 /* BINARY */,
4939
+ data,
4940
+ w: size,
4941
+ h: size,
4942
+ centerOffsetX: centerOffset,
4943
+ centerOffsetY: centerOffset
4944
+ };
4945
+ }
4946
+
4947
+ // src/Paint/makePaintMask.ts
4948
+ function makePaintBinaryMask(mask) {
4949
+ return {
4950
+ type: 1 /* BINARY */,
4951
+ data: mask.data,
4952
+ w: mask.w,
4953
+ h: mask.h,
4954
+ centerOffsetX: -(mask.w >> 1),
4955
+ centerOffsetY: -(mask.h >> 1)
4956
+ };
4957
+ }
4958
+ function makePaintAlphaMask(mask) {
4959
+ return {
4960
+ type: 0 /* ALPHA */,
4961
+ data: mask.data,
4962
+ w: mask.w,
4963
+ h: mask.h,
4964
+ centerOffsetX: -(mask.w >> 1),
4965
+ centerOffsetY: -(mask.h >> 1)
4966
+ };
4967
+ }
4968
+
4969
+ // src/Paint/makeRectFalloffPaintAlphaMask.ts
4970
+ function makeRectFalloffPaintAlphaMask(width, height, fallOff = (d) => d) {
4971
+ const fPx = Math.floor(width / 2);
4972
+ const fPy = Math.floor(height / 2);
4973
+ const invHalfW = 2 / width;
4974
+ const invHalfH = 2 / height;
4975
+ const offX = width % 2 === 0 ? 0.5 : 0;
4976
+ const offY = height % 2 === 0 ? 0.5 : 0;
4977
+ const area = width * height;
4978
+ const data = new Uint8Array(area);
4979
+ for (let y = 0; y < height; y++) {
4980
+ const dy = Math.abs(y - fPy + offY) * invHalfH;
4981
+ const rowOffset = y * width;
4982
+ for (let x = 0; x < width; x++) {
4983
+ const dx = Math.abs(x - fPx + offX) * invHalfW;
4984
+ const dist = dx > dy ? dx : dy;
4985
+ const strength = fallOff(1 - dist);
4986
+ if (strength > 0) {
4987
+ const intensity = strength * 255 | 0;
4988
+ data[rowOffset + x] = Math.max(0, Math.min(255, intensity));
4989
+ }
4990
+ }
4991
+ }
4992
+ return {
4993
+ type: 0 /* ALPHA */,
4804
4994
  data,
4805
- type: 1 /* BINARY */
4806
- });
4995
+ w: width,
4996
+ h: height,
4997
+ centerOffsetX: -(width >> 1),
4998
+ centerOffsetY: -(height >> 1)
4999
+ };
4807
5000
  }
4808
5001
 
4809
5002
  // src/PixelData/PixelData.ts
@@ -4827,14 +5020,96 @@ var PixelData = class {
4827
5020
  }
4828
5021
  };
4829
5022
 
4830
- // src/PixelData/blendPixelDataPaintBuffer.ts
5023
+ // src/PixelData/applyMaskToPixelData.ts
5024
+ function applyMaskToPixelData(dst, mask, opts) {
5025
+ if (mask.type === 1 /* BINARY */) {
5026
+ return applyBinaryMaskToPixelData(dst, mask, opts);
5027
+ } else {
5028
+ return applyAlphaMaskToPixelData(dst, mask, opts);
5029
+ }
5030
+ }
5031
+
5032
+ // src/PixelData/blendColorPixelDataMask.ts
5033
+ function blendColorPixelDataMask(dst, color, mask, opts) {
5034
+ if (mask.type === 1 /* BINARY */) {
5035
+ return blendColorPixelDataBinaryMask(dst, color, mask, opts);
5036
+ } else {
5037
+ return blendColorPixelDataAlphaMask(dst, color, mask, opts);
5038
+ }
5039
+ }
5040
+
5041
+ // src/PixelData/blendColorPixelDataPaintAlphaMask.ts
4831
5042
  var SCRATCH_OPTS = {
5043
+ x: 0,
5044
+ y: 0,
5045
+ alpha: 255,
5046
+ blendFn: sourceOverPerfect
5047
+ };
5048
+ function blendColorPixelDataPaintAlphaMask(dst, color, mask, x, y, alpha = 255, blendFn = sourceOverPerfect) {
5049
+ const tx = x + mask.centerOffsetX;
5050
+ const ty = y + mask.centerOffsetY;
5051
+ SCRATCH_OPTS.x = tx;
5052
+ SCRATCH_OPTS.y = ty;
5053
+ SCRATCH_OPTS.alpha = alpha;
5054
+ SCRATCH_OPTS.blendFn = blendFn;
5055
+ return blendColorPixelDataAlphaMask(dst, color, mask, SCRATCH_OPTS);
5056
+ }
5057
+
5058
+ // src/PixelData/blendColorPixelDataPaintBinaryMask.ts
5059
+ var SCRATCH_OPTS2 = {
5060
+ x: 0,
5061
+ y: 0,
5062
+ alpha: 255,
5063
+ blendFn: sourceOverPerfect
5064
+ };
5065
+ function blendColorPixelDataPaintBinaryMask(dst, color, mask, x, y, alpha = 255, blendFn = sourceOverPerfect) {
5066
+ const tx = x + mask.centerOffsetX;
5067
+ const ty = y + mask.centerOffsetY;
5068
+ SCRATCH_OPTS2.x = tx;
5069
+ SCRATCH_OPTS2.y = ty;
5070
+ SCRATCH_OPTS2.alpha = alpha;
5071
+ SCRATCH_OPTS2.blendFn = blendFn;
5072
+ return blendColorPixelDataBinaryMask(dst, color, mask, SCRATCH_OPTS2);
5073
+ }
5074
+
5075
+ // src/PixelData/blendColorPixelDataPaintMask.ts
5076
+ var SCRATCH_OPTS3 = {
5077
+ x: 0,
5078
+ y: 0,
5079
+ alpha: 255,
5080
+ blendFn: sourceOverPerfect
5081
+ };
5082
+ function blendColorPixelDataPaintMask(dst, color, mask, x, y, alpha = 255, blendFn = sourceOverPerfect) {
5083
+ const tx = x + mask.centerOffsetX;
5084
+ const ty = y + mask.centerOffsetY;
5085
+ SCRATCH_OPTS3.x = tx;
5086
+ SCRATCH_OPTS3.y = ty;
5087
+ SCRATCH_OPTS3.alpha = alpha;
5088
+ SCRATCH_OPTS3.blendFn = blendFn;
5089
+ if (mask.type === 1 /* BINARY */) {
5090
+ return blendColorPixelDataBinaryMask(dst, color, mask, SCRATCH_OPTS3);
5091
+ } else {
5092
+ return blendColorPixelDataAlphaMask(dst, color, mask, SCRATCH_OPTS3);
5093
+ }
5094
+ }
5095
+
5096
+ // src/PixelData/blendPixelDataMask.ts
5097
+ function blendPixelDataMask(target, src, mask, opts) {
5098
+ if (mask.type === 1 /* BINARY */) {
5099
+ return blendPixelDataBinaryMask(target, src, mask, opts);
5100
+ } else {
5101
+ return blendPixelDataAlphaMask(target, src, mask, opts);
5102
+ }
5103
+ }
5104
+
5105
+ // src/PixelData/blendPixelDataPaintBuffer.ts
5106
+ var SCRATCH_OPTS4 = {
4832
5107
  x: 0,
4833
5108
  y: 0,
4834
5109
  alpha: 255,
4835
5110
  blendFn: void 0
4836
5111
  };
4837
- function blendPixelDataPaintBuffer(paintBuffer, target, alpha = 255, blendFn, blendPixelDataFn = blendPixelData) {
5112
+ function blendPixelDataPaintBuffer(target, paintBuffer, alpha = 255, blendFn, blendPixelDataFn = blendPixelData) {
4838
5113
  const tileShift = paintBuffer.config.tileShift;
4839
5114
  const lookup = paintBuffer.lookup;
4840
5115
  for (let i = 0; i < lookup.length; i++) {
@@ -4842,17 +5117,61 @@ function blendPixelDataPaintBuffer(paintBuffer, target, alpha = 255, blendFn, bl
4842
5117
  if (tile) {
4843
5118
  const x = tile.tx << tileShift;
4844
5119
  const y = tile.ty << tileShift;
4845
- SCRATCH_OPTS.x = x;
4846
- SCRATCH_OPTS.y = y;
4847
- SCRATCH_OPTS.alpha = alpha;
4848
- SCRATCH_OPTS.blendFn = blendFn;
4849
- blendPixelDataFn(target, tile, SCRATCH_OPTS);
5120
+ SCRATCH_OPTS4.x = x;
5121
+ SCRATCH_OPTS4.y = y;
5122
+ SCRATCH_OPTS4.alpha = alpha;
5123
+ SCRATCH_OPTS4.blendFn = blendFn;
5124
+ blendPixelDataFn(target, tile, SCRATCH_OPTS4);
4850
5125
  }
4851
5126
  }
4852
5127
  }
4853
5128
 
4854
- // src/PixelData/clearPixelData.ts
4855
- function clearPixelData(dst, rect) {
5129
+ // src/PixelData/fillPixelDataFast.ts
5130
+ var SCRATCH_RECT4 = makeClippedRect();
5131
+ function fillPixelDataFast(dst, color, _x, _y, _w, _h) {
5132
+ let x;
5133
+ let y;
5134
+ let w;
5135
+ let h;
5136
+ if (typeof _x === "object") {
5137
+ x = _x.x ?? 0;
5138
+ y = _x.y ?? 0;
5139
+ w = _x.w ?? dst.width;
5140
+ h = _x.h ?? dst.height;
5141
+ } else if (typeof _x === "number") {
5142
+ x = _x;
5143
+ y = _y;
5144
+ w = _w;
5145
+ h = _h;
5146
+ } else {
5147
+ x = 0;
5148
+ y = 0;
5149
+ w = dst.width;
5150
+ h = dst.height;
5151
+ }
5152
+ const clip = resolveRectClipping(x, y, w, h, dst.width, dst.height, SCRATCH_RECT4);
5153
+ if (!clip.inBounds) return;
5154
+ const {
5155
+ x: finalX,
5156
+ y: finalY,
5157
+ w: actualW,
5158
+ h: actualH
5159
+ } = clip;
5160
+ const dst32 = dst.data32;
5161
+ const dw = dst.width;
5162
+ if (actualW === dw && actualH === dst.height && finalX === 0 && finalY === 0) {
5163
+ dst32.fill(color);
5164
+ return;
5165
+ }
5166
+ for (let iy = 0; iy < actualH; iy++) {
5167
+ const start = (finalY + iy) * dw + finalX;
5168
+ const end = start + actualW;
5169
+ dst32.fill(color, start, end);
5170
+ }
5171
+ }
5172
+
5173
+ // src/PixelData/clearPixelDataFast.ts
5174
+ function clearPixelDataFast(dst, rect) {
4856
5175
  fillPixelDataFast(dst, 0, rect);
4857
5176
  }
4858
5177
 
@@ -4915,26 +5234,6 @@ function extractPixelData(source, _x, _y, _w, _h) {
4915
5234
  return result;
4916
5235
  }
4917
5236
 
4918
- // src/PixelData/PixelBuffer32.ts
4919
- var PixelBuffer32 = class _PixelBuffer32 {
4920
- constructor(width, height, data32) {
4921
- this.width = width;
4922
- this.height = height;
4923
- this.data32 = data32 ?? new Uint32Array(width * height);
4924
- }
4925
- data32;
4926
- set(width, height, data32) {
4927
- ;
4928
- this.data32 = data32 ?? new Uint32Array(width * height);
4929
- this.width = width;
4930
- this.height = height;
4931
- }
4932
- copy() {
4933
- const newData32 = new Uint32Array(this.data32);
4934
- return new _PixelBuffer32(this.width, this.height, newData32);
4935
- }
4936
- };
4937
-
4938
5237
  // src/PixelData/pixelDataToAlphaMask.ts
4939
5238
  function pixelDataToAlphaMask(pixelData) {
4940
5239
  const {
@@ -5086,162 +5385,17 @@ function writePaintBufferToPixelData(target, paintBuffer, writePixelDataBufferFn
5086
5385
  }
5087
5386
  }
5088
5387
  }
5089
-
5090
- // src/Paint/makeCirclePaintAlphaMask.ts
5091
- function makeCirclePaintAlphaMask(size, fallOff = (d) => d) {
5092
- const area = size * size;
5093
- const data = new Uint8Array(area);
5094
- const radius = size / 2;
5095
- const invR = 1 / radius;
5096
- const centerOffset = -Math.ceil(radius - 0.5);
5097
- for (let y = 0; y < size; y++) {
5098
- const rowOffset = y * size;
5099
- const dy = y - radius + 0.5;
5100
- const dy2 = dy * dy;
5101
- for (let x = 0; x < size; x++) {
5102
- const dx = x - radius + 0.5;
5103
- const distSqr = dx * dx + dy2;
5104
- if (distSqr <= radius * radius) {
5105
- const dist = Math.sqrt(distSqr) * invR;
5106
- const strength = fallOff(1 - dist);
5107
- if (strength > 0) {
5108
- const intensity = strength * 255 | 0;
5109
- data[rowOffset + x] = Math.max(0, Math.min(255, intensity));
5110
- }
5111
- }
5112
- }
5113
- }
5114
- return {
5115
- type: 0 /* ALPHA */,
5116
- data,
5117
- w: size,
5118
- h: size,
5119
- centerOffsetX: centerOffset,
5120
- centerOffsetY: centerOffset
5121
- };
5122
- }
5123
-
5124
- // src/Paint/makeCirclePaintBinaryMask.ts
5125
- function makeCirclePaintBinaryMask(size) {
5126
- const area = size * size;
5127
- const data = new Uint8Array(area);
5128
- const radius = size / 2;
5129
- const centerOffset = -Math.ceil(radius - 0.5);
5130
- for (let y = 0; y < size; y++) {
5131
- for (let x = 0; x < size; x++) {
5132
- const dx = x - radius + 0.5;
5133
- const dy = y - radius + 0.5;
5134
- const distSqr = dx * dx + dy * dy;
5135
- if (distSqr <= radius * radius) {
5136
- data[y * size + x] = 1;
5137
- }
5138
- }
5139
- }
5140
- return {
5141
- type: 1 /* BINARY */,
5142
- data,
5143
- w: size,
5144
- h: size,
5145
- centerOffsetX: centerOffset,
5146
- centerOffsetY: centerOffset
5147
- };
5148
- }
5149
-
5150
- // src/Paint/makePaintMask.ts
5151
- function makePaintBinaryMask(mask) {
5152
- return {
5153
- type: 1 /* BINARY */,
5154
- data: mask.data,
5155
- w: mask.w,
5156
- h: mask.h,
5157
- centerOffsetX: -(mask.w >> 1),
5158
- centerOffsetY: -(mask.h >> 1)
5159
- };
5160
- }
5161
- function makePaintAlphaMask(mask) {
5162
- return {
5163
- type: 0 /* ALPHA */,
5164
- data: mask.data,
5165
- w: mask.w,
5166
- h: mask.h,
5167
- centerOffsetX: -(mask.w >> 1),
5168
- centerOffsetY: -(mask.h >> 1)
5169
- };
5170
- }
5171
-
5172
- // src/Paint/makeRectFalloffPaintAlphaMask.ts
5173
- function makeRectFalloffPaintAlphaMask(width, height, fallOff = (d) => d) {
5174
- const fPx = Math.floor(width / 2);
5175
- const fPy = Math.floor(height / 2);
5176
- const invHalfW = 2 / width;
5177
- const invHalfH = 2 / height;
5178
- const offX = width % 2 === 0 ? 0.5 : 0;
5179
- const offY = height % 2 === 0 ? 0.5 : 0;
5180
- const area = width * height;
5181
- const data = new Uint8Array(area);
5182
- for (let y = 0; y < height; y++) {
5183
- const dy = Math.abs(y - fPy + offY) * invHalfH;
5184
- const rowOffset = y * width;
5185
- for (let x = 0; x < width; x++) {
5186
- const dx = Math.abs(x - fPx + offX) * invHalfW;
5187
- const dist = dx > dy ? dx : dy;
5188
- const strength = fallOff(1 - dist);
5189
- if (strength > 0) {
5190
- const intensity = strength * 255 | 0;
5191
- data[rowOffset + x] = Math.max(0, Math.min(255, intensity));
5192
- }
5193
- }
5194
- }
5195
- return {
5196
- type: 0 /* ALPHA */,
5197
- data,
5198
- w: width,
5199
- h: height,
5200
- centerOffsetX: -macro_halfAndFloor(width),
5201
- centerOffsetY: -macro_halfAndFloor(height)
5202
- };
5203
- }
5204
-
5205
- // src/Paint/PaintBufferCanvasRenderer.ts
5206
- function makePaintBufferCanvasRenderer(paintBuffer, offscreenCanvasClass = OffscreenCanvas) {
5207
- const config = paintBuffer.config;
5208
- const tileSize = config.tileSize;
5209
- const tileShift = config.tileShift;
5210
- const lookup = paintBuffer.lookup;
5211
- const canvas = new offscreenCanvasClass(tileSize, tileSize);
5212
- const ctx = canvas.getContext("2d");
5213
- if (!ctx) throw new Error(CANVAS_CTX_FAILED);
5214
- ctx.imageSmoothingEnabled = false;
5215
- return function drawPaintBuffer(targetCtx, alpha = 255, compOperation = "source-over") {
5216
- targetCtx.globalAlpha = alpha / 255;
5217
- targetCtx.globalCompositeOperation = compOperation;
5218
- for (let i = 0; i < lookup.length; i++) {
5219
- const tile = lookup[i];
5220
- if (tile) {
5221
- const dx = tile.tx << tileShift;
5222
- const dy = tile.ty << tileShift;
5223
- ctx.putImageData(tile.imageData, 0, 0);
5224
- targetCtx.drawImage(canvas, dx, dy);
5225
- }
5226
- }
5227
- targetCtx.globalAlpha = 1;
5228
- targetCtx.globalCompositeOperation = "source-over";
5229
- };
5230
- }
5231
5388
  // Annotate the CommonJS export names for ESM import in node:
5232
5389
  0 && (module.exports = {
5233
5390
  BASE_FAST_BLEND_MODE_FUNCTIONS,
5234
5391
  BASE_PERFECT_BLEND_MODE_FUNCTIONS,
5235
5392
  BaseBlendMode,
5236
5393
  CANVAS_COMPOSITE_MAP,
5237
- CANVAS_CTX_FAILED,
5238
5394
  HistoryManager,
5239
5395
  IndexedImage,
5240
5396
  MaskType,
5241
- OFFSCREEN_CANVAS_CTX_FAILED,
5242
5397
  PaintBuffer,
5243
5398
  PixelAccumulator,
5244
- PixelBuffer32,
5245
5399
  PixelData,
5246
5400
  PixelEngineConfig,
5247
5401
  PixelTile,
@@ -5251,18 +5405,24 @@ function makePaintBufferCanvasRenderer(paintBuffer, offscreenCanvasClass = Offsc
5251
5405
  applyAlphaMaskToPixelData,
5252
5406
  applyBinaryMaskToAlphaMask,
5253
5407
  applyBinaryMaskToPixelData,
5408
+ applyMaskToPixelData,
5254
5409
  applyPatchTiles,
5255
5410
  base64DecodeArrayBuffer,
5256
5411
  base64EncodeArrayBuffer,
5257
5412
  blendColorPixelData,
5258
5413
  blendColorPixelDataAlphaMask,
5259
5414
  blendColorPixelDataBinaryMask,
5415
+ blendColorPixelDataMask,
5416
+ blendColorPixelDataPaintAlphaMask,
5417
+ blendColorPixelDataPaintBinaryMask,
5418
+ blendColorPixelDataPaintMask,
5260
5419
  blendPixel,
5261
5420
  blendPixelData,
5262
5421
  blendPixelDataAlphaMask,
5263
5422
  blendPixelDataBinaryMask,
5423
+ blendPixelDataMask,
5264
5424
  blendPixelDataPaintBuffer,
5265
- clearPixelData,
5425
+ clearPixelDataFast,
5266
5426
  color32ToCssRGBA,
5267
5427
  color32ToHex,
5268
5428
  colorBurnFast,
@@ -5335,6 +5495,8 @@ function makePaintBufferCanvasRenderer(paintBuffer, offscreenCanvasClass = Offsc
5335
5495
  makeCanvasFrameRenderer,
5336
5496
  makeCirclePaintAlphaMask,
5337
5497
  makeCirclePaintBinaryMask,
5498
+ makeClippedBlit,
5499
+ makeClippedRect,
5338
5500
  makeFastBlendModeRegistry,
5339
5501
  makeFullPixelMutator,
5340
5502
  makeHistoryAction,
@@ -5356,12 +5518,17 @@ function makePaintBufferCanvasRenderer(paintBuffer, offscreenCanvasClass = Offsc
5356
5518
  multiplyPerfect,
5357
5519
  mutatorApplyAlphaMask,
5358
5520
  mutatorApplyBinaryMask,
5521
+ mutatorApplyMask,
5522
+ mutatorBlendAlphaMask,
5523
+ mutatorBlendBinaryMask,
5359
5524
  mutatorBlendColor,
5360
- mutatorBlendPaintMask,
5525
+ mutatorBlendColorPaintAlphaMask,
5526
+ mutatorBlendColorPaintBinaryMask,
5527
+ mutatorBlendColorPaintMask,
5528
+ mutatorBlendMask,
5529
+ mutatorBlendPaintRect,
5361
5530
  mutatorBlendPixel,
5362
5531
  mutatorBlendPixelData,
5363
- mutatorBlendPixelDataAlphaMask,
5364
- mutatorBlendPixelDataBinaryMask,
5365
5532
  mutatorClear,
5366
5533
  mutatorFill,
5367
5534
  mutatorFillBinaryMask,
@@ -5379,10 +5546,13 @@ function makePaintBufferCanvasRenderer(paintBuffer, offscreenCanvasClass = Offsc
5379
5546
  pixelDataToAlphaMask,
5380
5547
  reflectPixelDataHorizontal,
5381
5548
  reflectPixelDataVertical,
5549
+ resample32,
5382
5550
  resampleImageData,
5383
5551
  resampleIndexedImage,
5384
5552
  resamplePixelData,
5385
5553
  resizeImageData,
5554
+ resolveBlitClipping,
5555
+ resolveRectClipping,
5386
5556
  rotatePixelData,
5387
5557
  screenFast,
5388
5558
  screenPerfect,