pixel-data-js 0.25.0 → 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 +1832 -1606
  2. package/dist/index.prod.cjs.map +1 -1
  3. package/dist/index.prod.d.ts +458 -324
  4. package/dist/index.prod.js +1811 -1600
  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/mutatorBlendColorPaintMask.ts +60 -0
  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 -29
  58. package/dist/index.dev.cjs +0 -5363
  59. package/dist/index.dev.cjs.map +0 -1
  60. package/dist/index.dev.js +0 -5154
  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
@@ -23,14 +23,12 @@ __export(src_exports, {
23
23
  BASE_FAST_BLEND_MODE_FUNCTIONS: () => BASE_FAST_BLEND_MODE_FUNCTIONS,
24
24
  BASE_PERFECT_BLEND_MODE_FUNCTIONS: () => BASE_PERFECT_BLEND_MODE_FUNCTIONS,
25
25
  BaseBlendMode: () => BaseBlendMode,
26
- CANVAS_CTX_FAILED: () => CANVAS_CTX_FAILED,
26
+ CANVAS_COMPOSITE_MAP: () => CANVAS_COMPOSITE_MAP,
27
27
  HistoryManager: () => HistoryManager,
28
28
  IndexedImage: () => IndexedImage,
29
29
  MaskType: () => MaskType,
30
- OFFSCREEN_CANVAS_CTX_FAILED: () => OFFSCREEN_CANVAS_CTX_FAILED,
31
30
  PaintBuffer: () => PaintBuffer,
32
31
  PixelAccumulator: () => PixelAccumulator,
33
- PixelBuffer32: () => PixelBuffer32,
34
32
  PixelData: () => PixelData,
35
33
  PixelEngineConfig: () => PixelEngineConfig,
36
34
  PixelTile: () => PixelTile,
@@ -40,18 +38,24 @@ __export(src_exports, {
40
38
  applyAlphaMaskToPixelData: () => applyAlphaMaskToPixelData,
41
39
  applyBinaryMaskToAlphaMask: () => applyBinaryMaskToAlphaMask,
42
40
  applyBinaryMaskToPixelData: () => applyBinaryMaskToPixelData,
41
+ applyMaskToPixelData: () => applyMaskToPixelData,
43
42
  applyPatchTiles: () => applyPatchTiles,
44
43
  base64DecodeArrayBuffer: () => base64DecodeArrayBuffer,
45
44
  base64EncodeArrayBuffer: () => base64EncodeArrayBuffer,
46
45
  blendColorPixelData: () => blendColorPixelData,
47
46
  blendColorPixelDataAlphaMask: () => blendColorPixelDataAlphaMask,
48
47
  blendColorPixelDataBinaryMask: () => blendColorPixelDataBinaryMask,
48
+ blendColorPixelDataMask: () => blendColorPixelDataMask,
49
+ blendColorPixelDataPaintAlphaMask: () => blendColorPixelDataPaintAlphaMask,
50
+ blendColorPixelDataPaintBinaryMask: () => blendColorPixelDataPaintBinaryMask,
51
+ blendColorPixelDataPaintMask: () => blendColorPixelDataPaintMask,
49
52
  blendPixel: () => blendPixel,
50
53
  blendPixelData: () => blendPixelData,
51
54
  blendPixelDataAlphaMask: () => blendPixelDataAlphaMask,
52
55
  blendPixelDataBinaryMask: () => blendPixelDataBinaryMask,
56
+ blendPixelDataMask: () => blendPixelDataMask,
53
57
  blendPixelDataPaintBuffer: () => blendPixelDataPaintBuffer,
54
- clearPixelData: () => clearPixelData,
58
+ clearPixelDataFast: () => clearPixelDataFast,
55
59
  color32ToCssRGBA: () => color32ToCssRGBA,
56
60
  color32ToHex: () => color32ToHex,
57
61
  colorBurnFast: () => colorBurnFast,
@@ -124,6 +128,8 @@ __export(src_exports, {
124
128
  makeCanvasFrameRenderer: () => makeCanvasFrameRenderer,
125
129
  makeCirclePaintAlphaMask: () => makeCirclePaintAlphaMask,
126
130
  makeCirclePaintBinaryMask: () => makeCirclePaintBinaryMask,
131
+ makeClippedBlit: () => makeClippedBlit,
132
+ makeClippedRect: () => makeClippedRect,
127
133
  makeFastBlendModeRegistry: () => makeFastBlendModeRegistry,
128
134
  makeFullPixelMutator: () => makeFullPixelMutator,
129
135
  makeHistoryAction: () => makeHistoryAction,
@@ -145,11 +151,17 @@ __export(src_exports, {
145
151
  multiplyPerfect: () => multiplyPerfect,
146
152
  mutatorApplyAlphaMask: () => mutatorApplyAlphaMask,
147
153
  mutatorApplyBinaryMask: () => mutatorApplyBinaryMask,
154
+ mutatorApplyMask: () => mutatorApplyMask,
155
+ mutatorBlendAlphaMask: () => mutatorBlendAlphaMask,
156
+ mutatorBlendBinaryMask: () => mutatorBlendBinaryMask,
148
157
  mutatorBlendColor: () => mutatorBlendColor,
158
+ mutatorBlendColorPaintAlphaMask: () => mutatorBlendColorPaintAlphaMask,
159
+ mutatorBlendColorPaintBinaryMask: () => mutatorBlendColorPaintBinaryMask,
160
+ mutatorBlendColorPaintMask: () => mutatorBlendColorPaintMask,
161
+ mutatorBlendMask: () => mutatorBlendMask,
162
+ mutatorBlendPaintRect: () => mutatorBlendPaintRect,
149
163
  mutatorBlendPixel: () => mutatorBlendPixel,
150
164
  mutatorBlendPixelData: () => mutatorBlendPixelData,
151
- mutatorBlendPixelDataAlphaMask: () => mutatorBlendPixelDataAlphaMask,
152
- mutatorBlendPixelDataBinaryMask: () => mutatorBlendPixelDataBinaryMask,
153
165
  mutatorClear: () => mutatorClear,
154
166
  mutatorFill: () => mutatorFill,
155
167
  mutatorFillBinaryMask: () => mutatorFillBinaryMask,
@@ -167,10 +179,13 @@ __export(src_exports, {
167
179
  pixelDataToAlphaMask: () => pixelDataToAlphaMask,
168
180
  reflectPixelDataHorizontal: () => reflectPixelDataHorizontal,
169
181
  reflectPixelDataVertical: () => reflectPixelDataVertical,
182
+ resample32: () => resample32,
170
183
  resampleImageData: () => resampleImageData,
171
184
  resampleIndexedImage: () => resampleIndexedImage,
172
185
  resamplePixelData: () => resamplePixelData,
173
186
  resizeImageData: () => resizeImageData,
187
+ resolveBlitClipping: () => resolveBlitClipping,
188
+ resolveRectClipping: () => resolveRectClipping,
174
189
  rotatePixelData: () => rotatePixelData,
175
190
  screenFast: () => screenFast,
176
191
  screenPerfect: () => screenPerfect,
@@ -505,37 +520,22 @@ function trimMaskRectBounds(target, bounds) {
505
520
  }
506
521
 
507
522
  // src/Algorithm/floodFillSelection.ts
508
- function floodFillSelection(img, startX, startY, {
509
- contiguous = true,
510
- tolerance = 0,
511
- bounds
512
- } = {}) {
513
- let imageData;
514
- let data32;
515
- if ("data32" in img) {
516
- data32 = img.data32;
517
- imageData = img.imageData;
518
- } else {
519
- data32 = new Uint32Array(img.data.buffer, img.data.byteOffset, img.data.byteLength >> 2);
520
- imageData = img;
521
- }
522
- const {
523
- width,
524
- height
525
- } = img;
526
- const limit = bounds || {
527
- x: 0,
528
- y: 0,
529
- w: width,
530
- h: height
531
- };
532
- const xMin = Math.max(0, limit.x);
533
- const xMax = Math.min(width - 1, limit.x + limit.w - 1);
534
- const yMin = Math.max(0, limit.y);
535
- 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);
536
535
  if (startX < xMin || startX > xMax || startY < yMin || startY > yMax) {
537
536
  return null;
538
537
  }
538
+ out = out ?? {};
539
539
  const baseColor = data32[startY * width + startX];
540
540
  let matchCount = 0;
541
541
  const matchX = new Uint16Array(width * height);
@@ -606,42 +606,33 @@ function floodFillSelection(img, startX, startY, {
606
606
  }
607
607
  }
608
608
  }
609
- if (matchCount === 0) {
610
- return null;
611
- }
609
+ if (matchCount === 0) return null;
612
610
  const w = maxX - minX + 1;
613
611
  const h = maxY - minY + 1;
614
- const selectionRect = {
615
- x: minX,
616
- y: minY,
617
- w,
618
- h,
619
- data: new Uint8Array(w * h),
620
- type: 1 /* BINARY */
621
- };
622
- const sw = selectionRect.w;
623
- const sh = selectionRect.h;
624
- 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;
625
621
  for (let i = 0; i < matchCount; i++) {
626
- const mx = matchX[i] - selectionRect.x;
627
- const my = matchY[i] - selectionRect.y;
628
- if (mx >= 0 && mx < sw && my >= 0 && my < sh) {
629
- 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;
630
626
  }
631
627
  }
632
- trimMaskRectBounds(selectionRect, {
628
+ trimMaskRectBounds(out, {
633
629
  x: 0,
634
630
  y: 0,
635
631
  w: width,
636
632
  h: height
637
633
  });
638
- const extracted = extractImageDataBuffer(imageData, selectionRect.x, selectionRect.y, selectionRect.w, selectionRect.h);
639
- return {
640
- startX,
641
- startY,
642
- selectionRect,
643
- pixels: extracted
644
- };
634
+ out.pixels = extractImageDataBuffer(target.imageData, out.x, out.y, out.w, out.h);
635
+ return out;
645
636
  }
646
637
 
647
638
  // src/Algorithm/forEachLinePoint.ts
@@ -664,35 +655,6 @@ function forEachLinePoint(x0, y0, x1, y1, callback) {
664
655
  }
665
656
  }
666
657
 
667
- // src/BlendModes/blend-modes.ts
668
- var BaseBlendMode = {
669
- overwrite: 0,
670
- sourceOver: 1,
671
- darken: 2,
672
- multiply: 3,
673
- colorBurn: 4,
674
- linearBurn: 5,
675
- darkerColor: 6,
676
- lighten: 7,
677
- screen: 8,
678
- colorDodge: 9,
679
- linearDodge: 10,
680
- lighterColor: 11,
681
- overlay: 12,
682
- softLight: 13,
683
- hardLight: 14,
684
- vividLight: 15,
685
- linearLight: 16,
686
- pinLight: 17,
687
- hardMix: 18,
688
- difference: 19,
689
- exclusion: 20,
690
- subtract: 21,
691
- divide: 22
692
- };
693
- var overwriteBase = (src, _dst) => src;
694
- overwriteBase.isOverwrite = true;
695
-
696
658
  // src/BlendModes/BlendModeRegistry.ts
697
659
  function makeBlendModeRegistry(blendModes, initialEntries, registryName = "anonymous") {
698
660
  const blendToName = /* @__PURE__ */ new Map();
@@ -732,6 +694,35 @@ function makeBlendModeRegistry(blendModes, initialEntries, registryName = "anony
732
694
  };
733
695
  }
734
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
+
735
726
  // src/BlendModes/blend-modes-fast.ts
736
727
  var overwriteFast = overwriteBase;
737
728
  var sourceOverFast = (src, dst) => {
@@ -1737,7 +1728,7 @@ var getKeyByValue = (obj, value) => {
1737
1728
  }
1738
1729
  };
1739
1730
 
1740
- // src/Canvas/_constants.ts
1731
+ // support/error-strings.ts
1741
1732
  var OFFSCREEN_CANVAS_CTX_FAILED = "Failed to create OffscreenCanvas context";
1742
1733
  var CANVAS_CTX_FAILED = "Failed to create Canvas context";
1743
1734
 
@@ -1839,6 +1830,24 @@ function makePixelCanvas(canvas) {
1839
1830
  };
1840
1831
  }
1841
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
+
1842
1851
  // src/ImageData/imgBlobToImageData.ts
1843
1852
  async function imgBlobToImageData(blob) {
1844
1853
  let bitmap = null;
@@ -1923,10 +1932,9 @@ function applyPatchTiles(target, tiles, tileSize) {
1923
1932
  }
1924
1933
 
1925
1934
  // src/History/HistoryAction.ts
1926
- function makeHistoryAction(writer, patch, after, afterUndo, afterRedo, applyPatchTilesFn = applyPatchTiles) {
1927
- const target = writer.config.target;
1928
- const tileSize = writer.config.tileSize;
1929
- 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;
1930
1938
  return {
1931
1939
  undo: () => {
1932
1940
  applyPatchTilesFn(target, patch.beforeTiles, tileSize);
@@ -2196,20 +2204,17 @@ var PixelEngineConfig = class {
2196
2204
  }
2197
2205
  };
2198
2206
 
2199
- // src/PixelData/blendColorPixelData.ts
2200
- function blendColorPixelData(dst, color, opts = {}) {
2201
- const {
2202
- x: targetX = 0,
2203
- y: targetY = 0,
2204
- w: width = dst.width,
2205
- h: height = dst.height,
2206
- alpha: globalAlpha = 255,
2207
- blendFn = sourceOverPerfect
2208
- } = 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;
2209
2217
  if (globalAlpha === 0) return false;
2210
- const baseSrcAlpha = color >>> 24;
2211
- const isOverwrite = blendFn.isOverwrite || false;
2212
- if (baseSrcAlpha === 0 && !isOverwrite) return false;
2213
2218
  let x = targetX;
2214
2219
  let y = targetY;
2215
2220
  let w = width;
@@ -2222,114 +2227,320 @@ function blendColorPixelData(dst, color, opts = {}) {
2222
2227
  h += y;
2223
2228
  y = 0;
2224
2229
  }
2225
- const actualW = Math.min(w, dst.width - x);
2226
- const actualH = Math.min(h, dst.height - y);
2227
- if (actualW <= 0 || actualH <= 0) return false;
2228
- let finalSrcColor = color;
2229
- if (globalAlpha < 255) {
2230
- const a = baseSrcAlpha * globalAlpha + 128 >> 8;
2231
- if (a === 0 && !isOverwrite) return false;
2232
- finalSrcColor = (color & 16777215 | a << 24) >>> 0;
2233
- }
2234
- const dst32 = dst.data32;
2235
- const dw = dst.width;
2236
- let dIdx = y * dw + x | 0;
2237
- 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;
2238
2255
  let didChange = false;
2239
- for (let iy = 0; iy < actualH; iy++) {
2240
- for (let ix = 0; ix < actualW; ix++) {
2241
- const current = dst32[dIdx];
2242
- const next = blendFn(finalSrcColor, current);
2243
- if (current !== next) {
2244
- 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;
2245
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
+ }
2246
2285
  }
2247
2286
  dIdx++;
2287
+ mIdx++;
2248
2288
  }
2249
2289
  dIdx += dStride;
2290
+ mIdx += mStride;
2250
2291
  }
2251
2292
  return didChange;
2252
2293
  }
2253
2294
 
2254
- // src/History/PixelMutator/mutatorBlendColor.ts
2255
- var defaults2 = {
2256
- blendColorPixelData
2257
- };
2258
- 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);
2259
2298
  const {
2260
- blendColorPixelData: blendColorPixelData2 = defaults2.blendColorPixelData
2261
- } = deps;
2262
- return {
2263
- blendColor(color, opts = {}) {
2264
- const target = writer.config.target;
2265
- const {
2266
- x = 0,
2267
- y = 0,
2268
- w = target.width,
2269
- h = target.height
2270
- } = opts;
2271
- const didChange = writer.accumulator.storeRegionBeforeState(x, y, w, h);
2272
- return didChange(blendColorPixelData2(target, color, opts));
2273
- }
2274
- };
2275
- });
2276
-
2277
- // src/PixelData/blendPixel.ts
2278
- function blendPixel(target, x, y, color, alpha = 255, blendFn = sourceOverPerfect) {
2279
- if (alpha === 0) return false;
2280
- let width = target.width;
2281
- let height = target.height;
2282
- if (x < 0 || x >= width || y < 0 || y >= height) return false;
2283
- let srcAlpha = color >>> 24;
2284
- let isOverwrite = blendFn.isOverwrite;
2285
- if (srcAlpha === 0 && !isOverwrite) return false;
2286
- let dst32 = target.data32;
2287
- let index = y * width + x;
2288
- let finalColor = color;
2289
- if (alpha !== 255) {
2290
- let finalAlpha = srcAlpha * alpha + 128 >> 8;
2291
- if (finalAlpha === 0 && !isOverwrite) return false;
2292
- 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;
2293
2310
  }
2294
- let current = dst32[index];
2295
- let next = blendFn(finalColor, current);
2296
- if (current !== next) {
2297
- dst32[index] = next;
2298
- 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);
2299
2320
  }
2300
- return false;
2321
+ return result;
2301
2322
  }
2302
2323
 
2303
- // src/History/PixelMutator/mutatorBlendPixel.ts
2304
- var defaults3 = {
2305
- blendPixel
2306
- };
2307
- var mutatorBlendPixel = ((writer, deps = defaults3) => {
2308
- const {
2309
- blendPixel: blendPixel2 = defaults3.blendPixel
2310
- } = deps;
2311
- return {
2312
- blendPixel(x, y, color, alpha, blendFn) {
2313
- const didChange = writer.accumulator.storePixelBeforeState(x, y);
2314
- return didChange(blendPixel2(writer.config.target, x, y, color, alpha, blendFn));
2315
- }
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
2316
2331
  };
2317
- });
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
+ }
2318
2342
 
2319
- // src/PixelData/blendPixelData.ts
2320
- function blendPixelData(dst, src, opts = {}) {
2321
- const {
2322
- x: targetX = 0,
2323
- y: targetY = 0,
2324
- sx: sourceX = 0,
2325
- sy: sourceY = 0,
2326
- w: width = src.width,
2327
- h: height = src.height,
2328
- alpha: globalAlpha = 255,
2329
- blendFn = sourceOverPerfect
2330
- } = opts;
2331
- if (globalAlpha === 0) return false;
2332
- let x = targetX;
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
+ };
2531
+
2532
+ // src/PixelData/blendPixelData.ts
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;
2542
+ if (globalAlpha === 0) return false;
2543
+ let x = targetX;
2333
2544
  let y = targetY;
2334
2545
  let sx = sourceX;
2335
2546
  let sy = sourceY;
@@ -2357,12 +2568,12 @@ function blendPixelData(dst, src, opts = {}) {
2357
2568
  h += y;
2358
2569
  y = 0;
2359
2570
  }
2360
- const actualW = Math.min(w, dst.width - x);
2361
- 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);
2362
2573
  if (actualW <= 0 || actualH <= 0) return false;
2363
- const dst32 = dst.data32;
2574
+ const dst32 = target.data32;
2364
2575
  const src32 = src.data32;
2365
- const dw = dst.width;
2576
+ const dw = target.width;
2366
2577
  const sw = src.width;
2367
2578
  let dIdx = y * dw + x | 0;
2368
2579
  let sIdx = sy * sw + sx | 0;
@@ -2405,240 +2616,528 @@ function blendPixelData(dst, src, opts = {}) {
2405
2616
  return didChange;
2406
2617
  }
2407
2618
 
2408
- // src/History/PixelMutator/mutatorBlendPixelData.ts
2409
- var defaults4 = {
2410
- 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;
2411
2634
  };
2412
- var mutatorBlendPixelData = ((writer, deps = defaults4) => {
2413
- const {
2414
- blendPixelData: blendPixelData2 = defaults4.blendPixelData
2415
- } = deps;
2416
- return {
2417
- blendPixelData(src, opts = {}) {
2418
- const {
2419
- x = 0,
2420
- y = 0,
2421
- w = src.width,
2422
- h = src.height
2423
- } = opts;
2424
- const didChange = writer.accumulator.storeRegionBeforeState(x, y, w, h);
2425
- return didChange(blendPixelData2(writer.config.target, src, opts));
2426
- }
2427
- };
2428
- });
2429
2635
 
2430
- // src/PixelData/blendPixelDataAlphaMask.ts
2431
- function blendPixelDataAlphaMask(dst, src, alphaMask, opts = {}) {
2432
- const {
2433
- x: targetX = 0,
2434
- y: targetY = 0,
2435
- sx: sourceX = 0,
2436
- sy: sourceY = 0,
2437
- w: width = src.width,
2438
- h: height = src.height,
2439
- alpha: globalAlpha = 255,
2440
- blendFn = sourceOverPerfect,
2441
- mx = 0,
2442
- my = 0,
2443
- invertMask = false
2444
- } = opts;
2445
- if (globalAlpha === 0) return false;
2446
- let x = targetX;
2447
- let y = targetY;
2448
- let sx = sourceX;
2449
- let sy = sourceY;
2450
- let w = width;
2451
- let h = height;
2452
- if (sx < 0) {
2453
- x -= sx;
2454
- w += sx;
2455
- sx = 0;
2456
- }
2457
- if (sy < 0) {
2458
- y -= sy;
2459
- h += sy;
2460
- sy = 0;
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;
2461
2645
  }
2462
- w = Math.min(w, src.width - sx);
2463
- h = Math.min(h, src.height - sy);
2464
- if (x < 0) {
2465
- sx -= x;
2466
- w += x;
2467
- x = 0;
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);
2468
2656
  }
2469
- if (y < 0) {
2470
- sy -= y;
2471
- h += y;
2472
- y = 0;
2657
+ releaseTile(tile) {
2658
+ this.pool.push(tile);
2473
2659
  }
2474
- const actualW = Math.min(w, dst.width - x);
2475
- const actualH = Math.min(h, dst.height - y);
2476
- if (actualW <= 0 || actualH <= 0) return false;
2477
- const dw = dst.width;
2478
- const sw = src.width;
2479
- const mPitch = alphaMask.w;
2480
- const maskData = alphaMask.data;
2481
- const dx = x - targetX | 0;
2482
- const dy = y - targetY | 0;
2483
- const dst32 = dst.data32;
2484
- const src32 = src.data32;
2485
- let dIdx = y * dw + x | 0;
2486
- let sIdx = sy * sw + sx | 0;
2487
- let mIdx = (my + dy) * mPitch + (mx + dx) | 0;
2488
- const dStride = dw - actualW | 0;
2489
- const sStride = sw - actualW | 0;
2490
- const mStride = mPitch - actualW | 0;
2491
- const isOpaque = globalAlpha === 255;
2492
- const isOverwrite = blendFn.isOverwrite || false;
2493
- let didChange = false;
2494
- for (let iy = 0; iy < actualH; iy++) {
2495
- for (let ix = 0; ix < actualW; ix++) {
2496
- const mVal = maskData[mIdx];
2497
- const effM = invertMask ? 255 - mVal : mVal;
2498
- if (effM === 0) {
2499
- dIdx++;
2500
- sIdx++;
2501
- mIdx++;
2502
- continue;
2503
- }
2504
- const srcCol = src32[sIdx];
2505
- const srcAlpha = srcCol >>> 24;
2506
- if (srcAlpha === 0 && !isOverwrite) {
2507
- dIdx++;
2508
- sIdx++;
2509
- mIdx++;
2510
- continue;
2511
- }
2512
- let weight = globalAlpha;
2513
- if (isOpaque) {
2514
- weight = effM;
2515
- } else if (effM !== 255) {
2516
- weight = effM * globalAlpha + 128 >> 8;
2517
- }
2518
- if (weight === 0) {
2519
- dIdx++;
2520
- sIdx++;
2521
- mIdx++;
2522
- continue;
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);
2523
2666
  }
2524
- let finalCol = srcCol;
2525
- if (weight < 255) {
2526
- const a = srcAlpha * weight + 128 >> 8;
2527
- if (a === 0 && !isOverwrite) {
2528
- dIdx++;
2529
- sIdx++;
2530
- mIdx++;
2531
- continue;
2532
- }
2533
- finalCol = (srcCol & 16777215 | a << 24) >>> 0;
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);
2534
2756
  }
2535
- const current = dst32[dIdx];
2536
- const next = blendFn(finalCol, dst32[dIdx]);
2537
- if (current !== next) {
2538
- dst32[dIdx] = next;
2539
- didChange = true;
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));
2540
2777
  }
2541
- dIdx++;
2542
- sIdx++;
2543
- mIdx++;
2544
2778
  }
2545
- dIdx += dStride;
2546
- sIdx += sStride;
2547
- mIdx += mStride;
2779
+ paintBuffer.clear();
2548
2780
  }
2549
- return didChange;
2550
- }
2781
+ };
2551
2782
 
2552
- // src/History/PixelMutator/mutatorBlendPixelDataAlphaMask.ts
2553
- var defaults5 = {
2554
- blendPixelDataAlphaMask
2783
+ // src/History/PixelMutator/mutatorApplyAlphaMask.ts
2784
+ var defaults2 = {
2785
+ applyAlphaMaskToPixelData
2555
2786
  };
2556
- var mutatorBlendPixelDataAlphaMask = ((writer, deps = defaults5) => {
2787
+ var mutatorApplyAlphaMask = ((writer, deps = defaults2) => {
2557
2788
  const {
2558
- blendPixelDataAlphaMask: blendPixelDataAlphaMask2 = defaults5.blendPixelDataAlphaMask
2789
+ applyAlphaMaskToPixelData: applyAlphaMaskToPixelData2 = defaults2.applyAlphaMaskToPixelData
2559
2790
  } = deps;
2560
2791
  return {
2561
- blendPixelDataAlphaMask(src, mask, opts = {}) {
2562
- const x = opts.x ?? 0;
2563
- const y = opts.y ?? 0;
2564
- const w = opts.w ?? src.width;
2565
- const h = opts.h ?? src.height;
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;
2566
2798
  const didChange = writer.accumulator.storeRegionBeforeState(x, y, w, h);
2567
- return didChange(blendPixelDataAlphaMask2(writer.config.target, src, mask, opts));
2799
+ return didChange(applyAlphaMaskToPixelData2(target, mask, opts));
2568
2800
  }
2569
2801
  };
2570
2802
  });
2571
2803
 
2572
- // src/PixelData/blendPixelDataBinaryMask.ts
2573
- function blendPixelDataBinaryMask(dst, src, binaryMask, opts = {}) {
2574
- const {
2575
- x: targetX = 0,
2576
- y: targetY = 0,
2577
- sx: sourceX = 0,
2578
- sy: sourceY = 0,
2579
- w: width = src.width,
2580
- h: height = src.height,
2581
- alpha: globalAlpha = 255,
2582
- blendFn = sourceOverPerfect,
2583
- mx = 0,
2584
- my = 0,
2585
- invertMask = false
2586
- } = opts;
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;
2587
2814
  if (globalAlpha === 0) return false;
2588
2815
  let x = targetX;
2589
2816
  let y = targetY;
2590
- let sx = sourceX;
2591
- let sy = sourceY;
2592
2817
  let w = width;
2593
2818
  let h = height;
2594
- if (sx < 0) {
2595
- x -= sx;
2596
- w += sx;
2597
- sx = 0;
2598
- }
2599
- if (sy < 0) {
2600
- y -= sy;
2601
- h += sy;
2602
- sy = 0;
2603
- }
2604
- w = Math.min(w, src.width - sx);
2605
- h = Math.min(h, src.height - sy);
2606
2819
  if (x < 0) {
2607
- sx -= x;
2608
2820
  w += x;
2609
2821
  x = 0;
2610
2822
  }
2611
2823
  if (y < 0) {
2612
- sy -= y;
2613
2824
  h += y;
2614
2825
  y = 0;
2615
2826
  }
2616
- const actualW = Math.min(w, dst.width - x);
2617
- const actualH = Math.min(h, dst.height - y);
2618
- if (actualW <= 0 || actualH <= 0) return false;
2619
- const dx = x - targetX | 0;
2620
- const dy = y - targetY | 0;
2621
- const dst32 = dst.data32;
2622
- const src32 = src.data32;
2623
- const dw = dst.width;
2624
- const sw = src.width;
2625
- const mPitch = binaryMask.w;
2626
- const maskData = binaryMask.data;
2627
- let dIdx = y * dw + x | 0;
2628
- let sIdx = sy * sw + sx | 0;
2629
- let mIdx = (my + dy) * mPitch + (mx + dx) | 0;
2630
- const dStride = dw - actualW | 0;
2631
- const sStride = sw - actualW | 0;
2632
- const mStride = mPitch - actualW | 0;
2633
- const skipVal = invertMask ? 1 : 0;
2634
- const isOpaque = globalAlpha === 255;
2635
- const isOverwrite = blendFn.isOverwrite || false;
2636
- let didChange = false;
2637
- for (let iy = 0; iy < actualH; iy++) {
2638
- for (let ix = 0; ix < actualW; ix++) {
2639
- if (maskData[mIdx] === skipVal) {
2640
- dIdx++;
2641
- sIdx++;
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;
2947
+ let x = targetX;
2948
+ let y = targetY;
2949
+ let sx = sourceX;
2950
+ let sy = sourceY;
2951
+ let w = width;
2952
+ let h = height;
2953
+ if (sx < 0) {
2954
+ x -= sx;
2955
+ w += sx;
2956
+ sx = 0;
2957
+ }
2958
+ if (sy < 0) {
2959
+ y -= sy;
2960
+ h += sy;
2961
+ sy = 0;
2962
+ }
2963
+ w = Math.min(w, src.width - sx);
2964
+ h = Math.min(h, src.height - sy);
2965
+ if (x < 0) {
2966
+ sx -= x;
2967
+ w += x;
2968
+ x = 0;
2969
+ }
2970
+ if (y < 0) {
2971
+ sy -= y;
2972
+ h += y;
2973
+ y = 0;
2974
+ }
2975
+ const actualW = Math.min(w, target.width - x);
2976
+ const actualH = Math.min(h, target.height - y);
2977
+ if (actualW <= 0 || actualH <= 0) return false;
2978
+ const dw = target.width;
2979
+ const sw = src.width;
2980
+ const mPitch = alphaMask.w;
2981
+ const maskData = alphaMask.data;
2982
+ const dx = x - targetX | 0;
2983
+ const dy = y - targetY | 0;
2984
+ const dst32 = target.data32;
2985
+ const src32 = src.data32;
2986
+ let dIdx = y * dw + x | 0;
2987
+ let sIdx = sy * sw + sx | 0;
2988
+ let mIdx = (my + dy) * mPitch + (mx + dx) | 0;
2989
+ const dStride = dw - actualW | 0;
2990
+ const sStride = sw - actualW | 0;
2991
+ const mStride = mPitch - actualW | 0;
2992
+ const isOpaque = globalAlpha === 255;
2993
+ const isOverwrite = blendFn.isOverwrite || false;
2994
+ let didChange = false;
2995
+ for (let iy = 0; iy < actualH; iy++) {
2996
+ for (let ix = 0; ix < actualW; ix++) {
2997
+ const mVal = maskData[mIdx];
2998
+ const effM = invertMask ? 255 - mVal : mVal;
2999
+ if (effM === 0) {
3000
+ dIdx++;
3001
+ sIdx++;
3002
+ mIdx++;
3003
+ continue;
3004
+ }
3005
+ const srcCol = src32[sIdx];
3006
+ const srcAlpha = srcCol >>> 24;
3007
+ if (srcAlpha === 0 && !isOverwrite) {
3008
+ dIdx++;
3009
+ sIdx++;
3010
+ mIdx++;
3011
+ continue;
3012
+ }
3013
+ let weight = globalAlpha;
3014
+ if (isOpaque) {
3015
+ weight = effM;
3016
+ } else if (effM !== 255) {
3017
+ weight = effM * globalAlpha + 128 >> 8;
3018
+ }
3019
+ if (weight === 0) {
3020
+ dIdx++;
3021
+ sIdx++;
3022
+ mIdx++;
3023
+ continue;
3024
+ }
3025
+ let finalCol = srcCol;
3026
+ if (weight < 255) {
3027
+ const a = srcAlpha * weight + 128 >> 8;
3028
+ if (a === 0 && !isOverwrite) {
3029
+ dIdx++;
3030
+ sIdx++;
3031
+ mIdx++;
3032
+ continue;
3033
+ }
3034
+ finalCol = (srcCol & 16777215 | a << 24) >>> 0;
3035
+ }
3036
+ const current = dst32[dIdx];
3037
+ const next = blendFn(finalCol, dst32[dIdx]);
3038
+ if (current !== next) {
3039
+ dst32[dIdx] = next;
3040
+ didChange = true;
3041
+ }
3042
+ dIdx++;
3043
+ sIdx++;
3044
+ mIdx++;
3045
+ }
3046
+ dIdx += dStride;
3047
+ sIdx += sStride;
3048
+ mIdx += mStride;
3049
+ }
3050
+ return didChange;
3051
+ }
3052
+
3053
+ // src/History/PixelMutator/mutatorBlendAlphaMask.ts
3054
+ var defaults5 = {
3055
+ blendPixelDataAlphaMask
3056
+ };
3057
+ var mutatorBlendAlphaMask = ((writer, deps = defaults5) => {
3058
+ const {
3059
+ blendPixelDataAlphaMask: blendPixelDataAlphaMask2 = defaults5.blendPixelDataAlphaMask
3060
+ } = deps;
3061
+ return {
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;
3067
+ const didChange = writer.accumulator.storeRegionBeforeState(x, y, w, h);
3068
+ return didChange(blendPixelDataAlphaMask2(writer.config.target, src, mask, opts));
3069
+ }
3070
+ };
3071
+ });
3072
+
3073
+ // src/PixelData/blendPixelDataBinaryMask.ts
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;
3086
+ if (globalAlpha === 0) return false;
3087
+ let x = targetX;
3088
+ let y = targetY;
3089
+ let sx = sourceX;
3090
+ let sy = sourceY;
3091
+ let w = width;
3092
+ let h = height;
3093
+ if (sx < 0) {
3094
+ x -= sx;
3095
+ w += sx;
3096
+ sx = 0;
3097
+ }
3098
+ if (sy < 0) {
3099
+ y -= sy;
3100
+ h += sy;
3101
+ sy = 0;
3102
+ }
3103
+ w = Math.min(w, src.width - sx);
3104
+ h = Math.min(h, src.height - sy);
3105
+ if (x < 0) {
3106
+ sx -= x;
3107
+ w += x;
3108
+ x = 0;
3109
+ }
3110
+ if (y < 0) {
3111
+ sy -= y;
3112
+ h += y;
3113
+ y = 0;
3114
+ }
3115
+ const actualW = Math.min(w, target.width - x);
3116
+ const actualH = Math.min(h, target.height - y);
3117
+ if (actualW <= 0 || actualH <= 0) return false;
3118
+ const dx = x - targetX | 0;
3119
+ const dy = y - targetY | 0;
3120
+ const dst32 = target.data32;
3121
+ const src32 = src.data32;
3122
+ const dw = target.width;
3123
+ const sw = src.width;
3124
+ const mPitch = binaryMask.w;
3125
+ const maskData = binaryMask.data;
3126
+ let dIdx = y * dw + x | 0;
3127
+ let sIdx = sy * sw + sx | 0;
3128
+ let mIdx = (my + dy) * mPitch + (mx + dx) | 0;
3129
+ const dStride = dw - actualW | 0;
3130
+ const sStride = sw - actualW | 0;
3131
+ const mStride = mPitch - actualW | 0;
3132
+ const skipVal = invertMask ? 1 : 0;
3133
+ const isOpaque = globalAlpha === 255;
3134
+ const isOverwrite = blendFn.isOverwrite || false;
3135
+ let didChange = false;
3136
+ for (let iy = 0; iy < actualH; iy++) {
3137
+ for (let ix = 0; ix < actualW; ix++) {
3138
+ if (maskData[mIdx] === skipVal) {
3139
+ dIdx++;
3140
+ sIdx++;
2642
3141
  mIdx++;
2643
3142
  continue;
2644
3143
  }
@@ -2678,953 +3177,744 @@ function blendPixelDataBinaryMask(dst, src, binaryMask, opts = {}) {
2678
3177
  return didChange;
2679
3178
  }
2680
3179
 
2681
- // src/History/PixelMutator/mutatorBlendPixelDataBinaryMask.ts
3180
+ // src/History/PixelMutator/mutatorBlendBinaryMask.ts
2682
3181
  var defaults6 = {
2683
3182
  blendPixelDataBinaryMask
2684
3183
  };
2685
- var mutatorBlendPixelDataBinaryMask = ((writer, deps = defaults6) => {
3184
+ var mutatorBlendBinaryMask = ((writer, deps = defaults6) => {
2686
3185
  const {
2687
3186
  blendPixelDataBinaryMask: blendPixelDataBinaryMask2 = defaults6.blendPixelDataBinaryMask
2688
3187
  } = deps;
2689
3188
  return {
2690
- blendPixelDataBinaryMask(src, mask, opts = {}) {
2691
- const x = opts.x ?? 0;
2692
- const y = opts.y ?? 0;
2693
- const w = opts.w ?? src.width;
2694
- 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;
2695
3194
  const didChange = writer.accumulator.storeRegionBeforeState(x, y, w, h);
2696
3195
  return didChange(blendPixelDataBinaryMask2(writer.config.target, src, mask, opts));
2697
3196
  }
2698
3197
  };
2699
3198
  });
2700
3199
 
2701
- // src/PixelData/fillPixelDataFast.ts
2702
- var SCRATCH_RECT = makeClippedRect();
2703
- function fillPixelDataFast(dst, color, _x, _y, _w, _h) {
2704
- let x;
2705
- let y;
2706
- let w;
2707
- let h;
2708
- if (typeof _x === "object") {
2709
- x = _x.x ?? 0;
2710
- y = _x.y ?? 0;
2711
- w = _x.w ?? dst.width;
2712
- h = _x.h ?? dst.height;
2713
- } else if (typeof _x === "number") {
2714
- x = _x;
2715
- y = _y;
2716
- w = _w;
2717
- h = _h;
2718
- } 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;
2719
3218
  x = 0;
3219
+ }
3220
+ if (y < 0) {
3221
+ h += y;
2720
3222
  y = 0;
2721
- w = dst.width;
2722
- h = dst.height;
2723
3223
  }
2724
- const clip = resolveRectClipping(x, y, w, h, dst.width, dst.height, SCRATCH_RECT);
2725
- if (!clip.inBounds) return;
2726
- const {
2727
- x: finalX,
2728
- y: finalY,
2729
- w: actualW,
2730
- h: actualH
2731
- } = clip;
2732
- const dst32 = dst.data32;
2733
- const dw = dst.width;
2734
- if (actualW === dw && actualH === dst.height && finalX === 0 && finalY === 0) {
2735
- dst32.fill(color);
2736
- 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;
2737
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;
2738
3238
  for (let iy = 0; iy < actualH; iy++) {
2739
- const start = (finalY + iy) * dw + finalX;
2740
- const end = start + actualW;
2741
- 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;
2742
3249
  }
3250
+ return didChange;
2743
3251
  }
2744
3252
 
2745
- // src/History/PixelMutator/mutatorClear.ts
3253
+ // src/History/PixelMutator/mutatorBlendColor.ts
2746
3254
  var defaults7 = {
2747
- fillPixelData: fillPixelDataFast
3255
+ blendColorPixelData
2748
3256
  };
2749
- var mutatorClear = ((writer, deps = defaults7) => {
3257
+ var mutatorBlendColor = ((writer, deps = defaults7) => {
2750
3258
  const {
2751
- fillPixelData: fillPixelData2 = defaults7.fillPixelData
3259
+ blendColorPixelData: blendColorPixelData2 = defaults7.blendColorPixelData
2752
3260
  } = deps;
2753
3261
  return {
2754
- clear(rect = {}) {
3262
+ blendColor(color, opts) {
2755
3263
  const target = writer.config.target;
2756
- const x = rect.x ?? 0;
2757
- const y = rect.y ?? 0;
2758
- const w = rect.w ?? target.width;
2759
- const h = rect.h ?? target.height;
2760
- writer.accumulator.storeRegionBeforeState(x, y, w, h);
2761
- 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));
2762
3270
  }
2763
3271
  };
2764
3272
  });
2765
3273
 
2766
- // src/PixelData/fillPixelData.ts
2767
- var SCRATCH_RECT2 = makeClippedRect();
2768
- function fillPixelData(dst, color, _x, _y, _w, _h) {
2769
- let x;
2770
- let y;
2771
- let w;
2772
- let h;
2773
- if (typeof _x === "object") {
2774
- x = _x.x ?? 0;
2775
- y = _x.y ?? 0;
2776
- w = _x.w ?? dst.width;
2777
- h = _x.h ?? dst.height;
2778
- } else if (typeof _x === "number") {
2779
- x = _x;
2780
- y = _y;
2781
- w = _w;
2782
- h = _h;
2783
- } 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;
2784
3295
  x = 0;
3296
+ }
3297
+ if (y < 0) {
3298
+ actualH += y;
2785
3299
  y = 0;
2786
- w = dst.width;
2787
- h = dst.height;
2788
3300
  }
2789
- const clip = resolveRectClipping(x, y, w, h, dst.width, dst.height, SCRATCH_RECT2);
2790
- if (!clip.inBounds) return false;
2791
- const {
2792
- x: finalX,
2793
- y: finalY,
2794
- w: actualW,
2795
- h: actualH
2796
- } = clip;
2797
- const dst32 = dst.data32;
2798
- const dw = dst.width;
2799
- 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;
2800
3317
  for (let iy = 0; iy < actualH; iy++) {
2801
- const rowOffset = (finalY + iy) * dw;
2802
- const start = rowOffset + finalX;
2803
- const end = start + actualW;
2804
- for (let i = start; i < end; i++) {
2805
- if (dst32[i] !== color) {
2806
- dst32[i] = color;
2807
- 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;
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;
2808
3352
  }
3353
+ dIdx++;
3354
+ mIdx++;
2809
3355
  }
3356
+ dIdx += dStride;
3357
+ mIdx += mStride;
2810
3358
  }
2811
- return hasChanged;
3359
+ return didChange;
2812
3360
  }
2813
3361
 
2814
- // src/History/PixelMutator/mutatorFill.ts
3362
+ // src/History/PixelMutator/mutatorBlendColorPaintAlphaMask.ts
2815
3363
  var defaults8 = {
2816
- fillPixelData
3364
+ blendColorPixelDataAlphaMask
2817
3365
  };
2818
- var mutatorFill = ((writer, deps = defaults8) => {
3366
+ var mutatorBlendColorPaintAlphaMask = ((writer, deps = defaults8) => {
2819
3367
  const {
2820
- fillPixelData: fillPixelData2 = defaults8.fillPixelData
3368
+ blendColorPixelDataAlphaMask: blendColorPixelDataAlphaMask2 = defaults8.blendColorPixelDataAlphaMask
2821
3369
  } = deps;
3370
+ const OPTS = {
3371
+ x: 0,
3372
+ y: 0,
3373
+ blendFn: sourceOverPerfect,
3374
+ alpha: 255
3375
+ };
2822
3376
  return {
2823
- fill(color, x = 0, y = 0, w = writer.config.target.width, h = writer.config.target.height) {
2824
- const target = writer.config.target;
2825
- const didChange = writer.accumulator.storeRegionBeforeState(x, y, w, h);
2826
- return didChange(fillPixelData2(target, color, x, y, w, 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));
2827
3386
  }
2828
3387
  };
2829
3388
  });
2830
- var mutatorFillRect = ((writer, deps = defaults8) => {
3389
+
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;
3410
+ }
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;
3456
+ }
3457
+
3458
+ // src/History/PixelMutator/mutatorBlendColorPaintBinaryMask.ts
3459
+ var defaults9 = {
3460
+ blendColorPixelDataBinaryMask
3461
+ };
3462
+ var mutatorBlendColorPaintBinaryMask = ((writer, deps = defaults9) => {
2831
3463
  const {
2832
- fillPixelData: fillPixelData2 = defaults8.fillPixelData
3464
+ blendColorPixelDataBinaryMask: blendColorPixelDataBinaryMask2 = defaults9.blendColorPixelDataBinaryMask
2833
3465
  } = deps;
3466
+ const OPTS = {
3467
+ x: 0,
3468
+ y: 0,
3469
+ blendFn: sourceOverPerfect,
3470
+ alpha: 255
3471
+ };
2834
3472
  return {
2835
- fillRect(color, rect) {
2836
- const target = writer.config.target;
2837
- const didChange = writer.accumulator.storeRegionBeforeState(rect.x, rect.y, rect.w, rect.h);
2838
- return didChange(fillPixelData2(target, color, rect.x, rect.y, rect.w, rect.h));
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));
2839
3482
  }
2840
3483
  };
2841
3484
  });
2842
3485
 
2843
- // src/PixelData/fillPixelDataBinaryMask.ts
2844
- var SCRATCH_RECT3 = makeClippedRect();
2845
- function fillPixelDataBinaryMask(dst, color, mask, alpha = 255, x = 0, y = 0) {
2846
- if (alpha === 0) return false;
2847
- const maskW = mask.w;
2848
- const maskH = mask.h;
2849
- const clip = resolveRectClipping(x, y, maskW, maskH, dst.width, dst.height, SCRATCH_RECT3);
2850
- if (!clip.inBounds) return false;
2851
- const {
2852
- x: finalX,
2853
- y: finalY,
2854
- w: actualW,
2855
- h: actualH
2856
- } = clip;
2857
- const maskData = mask.data;
2858
- const dst32 = dst.data32;
2859
- const dw = dst.width;
2860
- let finalCol = color;
2861
- if (alpha < 255) {
2862
- const baseSrcAlpha = color >>> 24;
2863
- const colorRGB = color & 16777215;
2864
- const a = baseSrcAlpha * alpha + 128 >> 8;
2865
- finalCol = (colorRGB | a << 24) >>> 0;
2866
- }
2867
- let hasChanged = false;
2868
- for (let iy = 0; iy < actualH; iy++) {
2869
- const currentY = finalY + iy;
2870
- const maskY = currentY - y;
2871
- const maskOffset = maskY * maskW;
2872
- const dstRowOffset = currentY * dw;
2873
- for (let ix = 0; ix < actualW; ix++) {
2874
- const currentX = finalX + ix;
2875
- const maskX = currentX - x;
2876
- const maskIndex = maskOffset + maskX;
2877
- if (maskData[maskIndex]) {
2878
- const current = dst32[dstRowOffset + currentX];
2879
- if (current !== finalCol) {
2880
- dst32[dstRowOffset + currentX] = finalCol;
2881
- hasChanged = true;
2882
- }
2883
- }
2884
- }
2885
- }
2886
- return hasChanged;
2887
- }
2888
-
2889
- // src/History/PixelMutator/mutatorFillBinaryMask.ts
2890
- var defaults9 = {
2891
- fillPixelDataBinaryMask
3486
+ // src/History/PixelMutator/mutatorBlendColorPaintMask.ts
3487
+ var defaults10 = {
3488
+ blendColorPixelDataAlphaMask,
3489
+ blendColorPixelDataBinaryMask
2892
3490
  };
2893
- var mutatorFillBinaryMask = ((writer, deps = defaults9) => {
3491
+ var mutatorBlendColorPaintMask = ((writer, deps = defaults10) => {
2894
3492
  const {
2895
- fillPixelDataBinaryMask: fillPixelDataBinaryMask2 = defaults9.fillPixelDataBinaryMask
3493
+ blendColorPixelDataBinaryMask: blendColorPixelDataBinaryMask2 = defaults10.blendColorPixelDataBinaryMask,
3494
+ blendColorPixelDataAlphaMask: blendColorPixelDataAlphaMask2 = defaults10.blendColorPixelDataAlphaMask
2896
3495
  } = deps;
3496
+ const OPTS = {
3497
+ x: 0,
3498
+ y: 0,
3499
+ blendFn: sourceOverPerfect,
3500
+ alpha: 255
3501
+ };
2897
3502
  return {
2898
- fillBinaryMask(color, mask, alpha = 255, x = 0, y = 0) {
2899
- const didChange = writer.accumulator.storeRegionBeforeState(x, y, mask.w, mask.h);
2900
- return didChange(fillPixelDataBinaryMask2(writer.config.target, color, mask, alpha, x, y));
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
+ }
2901
3516
  }
2902
3517
  };
2903
3518
  });
2904
3519
 
2905
- // src/PixelData/invertPixelData.ts
2906
- var SCRATCH_RECT4 = makeClippedRect();
2907
- function invertPixelData(pixelData, opts = {}) {
2908
- const dst = pixelData;
2909
- const {
2910
- x: targetX = 0,
2911
- y: targetY = 0,
2912
- w: width = pixelData.width,
2913
- h: height = pixelData.height,
2914
- mask,
2915
- mx = 0,
2916
- my = 0,
2917
- invertMask = false
2918
- } = opts;
2919
- const clip = resolveRectClipping(targetX, targetY, width, height, dst.width, dst.height, SCRATCH_RECT4);
2920
- if (!clip.inBounds) return false;
3520
+ // src/History/PixelMutator/mutatorBlendMask.ts
3521
+ var defaults11 = {
3522
+ blendPixelDataAlphaMask,
3523
+ blendPixelDataBinaryMask
3524
+ };
3525
+ var mutatorBlendMask = ((writer, deps = defaults11) => {
2921
3526
  const {
2922
- x,
2923
- y,
2924
- w: actualW,
2925
- h: actualH
2926
- } = clip;
2927
- const dst32 = dst.data32;
2928
- const dw = dst.width;
2929
- const mPitch = mask?.w ?? width;
2930
- const dx = x - targetX;
2931
- const dy = y - targetY;
2932
- let dIdx = y * dw + x;
2933
- let mIdx = (my + dy) * mPitch + (mx + dx);
2934
- const dStride = dw - actualW;
2935
- const mStride = mPitch - actualW;
2936
- if (mask) {
2937
- const maskData = mask.data;
2938
- for (let iy = 0; iy < actualH; iy++) {
2939
- for (let ix = 0; ix < actualW; ix++) {
2940
- const mVal = maskData[mIdx];
2941
- const isHit = invertMask ? mVal === 0 : mVal === 1;
2942
- if (isHit) {
2943
- dst32[dIdx] = dst32[dIdx] ^ 16777215;
2944
- }
2945
- dIdx++;
2946
- mIdx++;
2947
- }
2948
- dIdx += dStride;
2949
- mIdx += mStride;
2950
- }
2951
- } else {
2952
- for (let iy = 0; iy < actualH; iy++) {
2953
- for (let ix = 0; ix < actualW; ix++) {
2954
- dst32[dIdx] = dst32[dIdx] ^ 16777215;
2955
- dIdx++;
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));
2956
3541
  }
2957
- dIdx += dStride;
2958
3542
  }
2959
- }
2960
- return true;
2961
- }
3543
+ };
3544
+ });
2962
3545
 
2963
- // src/History/PixelMutator/mutatorInvert.ts
2964
- var defaults10 = {
2965
- invertPixelData
3546
+ // src/History/PixelMutator/mutatorBlendPaintRect.ts
3547
+ var defaults12 = {
3548
+ blendColorPixelData
2966
3549
  };
2967
- var mutatorInvert = ((writer, deps = defaults10) => {
3550
+ var mutatorBlendPaintRect = ((writer, deps = defaults12) => {
2968
3551
  const {
2969
- invertPixelData: invertPixelData2 = defaults10.invertPixelData
3552
+ blendColorPixelData: blendColorPixelData2 = defaults12.blendColorPixelData
2970
3553
  } = deps;
3554
+ const OPTS = {
3555
+ x: 0,
3556
+ y: 0,
3557
+ w: 0,
3558
+ h: 0,
3559
+ blendFn: sourceOverPerfect,
3560
+ alpha: 255
3561
+ };
2971
3562
  return {
2972
- invert(opts = {}) {
3563
+ blendPaintRect(color, centerX, centerY, brushWidth, brushHeight, alpha = 255, blendFn = sourceOverPerfect) {
2973
3564
  const target = writer.config.target;
2974
- const {
2975
- x = 0,
2976
- y = 0,
2977
- w = target.width,
2978
- h = target.height
2979
- } = opts;
2980
- const didChange = writer.accumulator.storeRegionBeforeState(x, y, w, h);
2981
- return didChange(invertPixelData2(target, opts));
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));
2982
3575
  }
2983
3576
  };
2984
3577
  });
2985
3578
 
2986
- // src/History/PixelMutator.ts
2987
- function makeFullPixelMutator(writer) {
2988
- return {
2989
- // @sort
2990
- ...mutatorBlendColor(writer),
2991
- ...mutatorBlendPixel(writer),
2992
- ...mutatorBlendPixelData(writer),
2993
- ...mutatorBlendPixelDataAlphaMask(writer),
2994
- ...mutatorBlendPixelDataBinaryMask(writer),
2995
- ...mutatorClear(writer),
2996
- ...mutatorFill(writer),
2997
- ...mutatorFillBinaryMask(writer),
2998
- ...mutatorFillRect(writer),
2999
- ...mutatorInvert(writer)
3000
- };
3001
- }
3002
-
3003
- // src/ImageData/resizeImageData.ts
3004
- function resizeImageData(target, newWidth, newHeight, offsetX = 0, offsetY = 0) {
3005
- const result = new ImageData(newWidth, newHeight);
3006
- const {
3007
- width: oldW,
3008
- height: oldH,
3009
- data: oldData
3010
- } = target;
3011
- const newData = result.data;
3012
- const x0 = Math.max(0, offsetX);
3013
- const y0 = Math.max(0, offsetY);
3014
- const x1 = Math.min(newWidth, offsetX + oldW);
3015
- const y1 = Math.min(newHeight, offsetY + oldH);
3016
- if (x1 <= x0 || y1 <= y0) {
3017
- return result;
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;
3018
3595
  }
3019
- const rowCount = y1 - y0;
3020
- const rowLen = (x1 - x0) * 4;
3021
- for (let row = 0; row < rowCount; row++) {
3022
- const dstY = y0 + row;
3023
- const srcY = dstY - offsetY;
3024
- const srcX = x0 - offsetX;
3025
- const dstStart = (dstY * newWidth + x0) * 4;
3026
- const srcStart = (srcY * oldW + srcX) * 4;
3027
- newData.set(oldData.subarray(srcStart, srcStart + rowLen), dstStart);
3596
+ let current = dst32[index];
3597
+ let next = blendFn(finalColor, current);
3598
+ if (current !== next) {
3599
+ dst32[index] = next;
3600
+ return true;
3028
3601
  }
3029
- return result;
3602
+ return false;
3030
3603
  }
3031
3604
 
3032
- // src/Rect/trimRectBounds.ts
3033
- function trimRectBounds(x, y, w, h, targetWidth, targetHeight, out) {
3034
- const res = out ?? {
3035
- x: 0,
3036
- y: 0,
3037
- w: 0,
3038
- h: 0
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
+ }
3039
3618
  };
3040
- const left = Math.max(0, x);
3041
- const top = Math.max(0, y);
3042
- const right = Math.min(targetWidth, x + w);
3043
- const bottom = Math.min(targetHeight, y + h);
3044
- res.x = left;
3045
- res.y = top;
3046
- res.w = Math.max(0, right - left);
3047
- res.h = Math.max(0, bottom - top);
3048
- return res;
3049
- }
3619
+ });
3050
3620
 
3051
- // src/Paint/PaintBuffer.ts
3052
- var PaintBuffer = class {
3053
- constructor(config, tilePool) {
3054
- this.config = config;
3055
- this.tilePool = tilePool;
3056
- this.lookup = [];
3057
- }
3058
- lookup;
3059
- scratchBounds = {
3060
- x: 0,
3061
- y: 0,
3062
- w: 0,
3063
- h: 0
3064
- };
3065
- eachTileInBounds(bounds, callback) {
3066
- const {
3067
- tileShift,
3068
- targetColumns,
3069
- targetRows,
3070
- tileSize
3071
- } = this.config;
3072
- const x1 = Math.max(0, bounds.x >> tileShift);
3073
- const y1 = Math.max(0, bounds.y >> tileShift);
3074
- const x2 = Math.min(targetColumns - 1, bounds.x + bounds.w - 1 >> tileShift);
3075
- const y2 = Math.min(targetRows - 1, bounds.y + bounds.h - 1 >> tileShift);
3076
- if (x1 > x2 || y1 > y2) return;
3077
- const lookup = this.lookup;
3078
- const tilePool = this.tilePool;
3079
- for (let ty = y1; ty <= y2; ty++) {
3080
- const rowOffset = ty * targetColumns;
3081
- const tileTop = ty << tileShift;
3082
- for (let tx = x1; tx <= x2; tx++) {
3083
- const id = rowOffset + tx;
3084
- const tile = lookup[id] ?? (lookup[id] = tilePool.getTile(id, tx, ty));
3085
- const tileLeft = tx << tileShift;
3086
- const startX = bounds.x > tileLeft ? bounds.x : tileLeft;
3087
- const startY = bounds.y > tileTop ? bounds.y : tileTop;
3088
- const maskEndX = bounds.x + bounds.w;
3089
- const tileEndX = tileLeft + tileSize;
3090
- const endX = maskEndX < tileEndX ? maskEndX : tileEndX;
3091
- const maskEndY = bounds.y + bounds.h;
3092
- const tileEndY = tileTop + tileSize;
3093
- const endY = maskEndY < tileEndY ? maskEndY : tileEndY;
3094
- callback(tile, startX, startY, endX - startX, endY - startY);
3095
- }
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));
3096
3637
  }
3097
- }
3098
- writePaintAlphaMaskStroke(color, brush, x0, y0, x1, y1) {
3099
- const cA = color >>> 24;
3100
- if (cA === 0) return false;
3101
- const {
3102
- tileShift,
3103
- tileMask,
3104
- target
3105
- } = this.config;
3106
- const {
3107
- w: bW,
3108
- h: bH,
3109
- data: bD,
3110
- centerOffsetX,
3111
- centerOffsetY
3112
- } = brush;
3113
- const cRGB = color & 16777215;
3114
- const scratch = this.scratchBounds;
3115
- let changed = false;
3116
- forEachLinePoint(x0, y0, x1, y1, (px, py) => {
3117
- const topLeftX = Math.floor(px + centerOffsetX);
3118
- const topLeftY = Math.floor(py + centerOffsetY);
3119
- trimRectBounds(topLeftX, topLeftY, bW, bH, target.width, target.height, scratch);
3120
- if (scratch.w <= 0 || scratch.h <= 0) return;
3121
- this.eachTileInBounds(scratch, (tile, bX, bY, bW_t, bH_t) => {
3122
- const d32 = tile.data32;
3123
- let tileChanged = false;
3124
- for (let i = 0; i < bH_t; i++) {
3125
- const canvasY = bY + i;
3126
- const bOff = (canvasY - topLeftY) * bW;
3127
- const tOff = (canvasY & tileMask) << tileShift;
3128
- const dS = tOff + (bX & tileMask);
3129
- for (let j = 0; j < bW_t; j++) {
3130
- const canvasX = bX + j;
3131
- const brushA = bD[bOff + (canvasX - topLeftX)];
3132
- if (brushA === 0) continue;
3133
- const t = cA * brushA + 128;
3134
- const blendedA = t + (t >> 8) >> 8;
3135
- const idx = dS + j;
3136
- const cur = d32[idx];
3137
- if (brushA > cur >>> 24) {
3138
- const next = (cRGB | blendedA << 24) >>> 0;
3139
- if (cur !== next) {
3140
- d32[idx] = next;
3141
- tileChanged = true;
3142
- }
3143
- }
3144
- }
3145
- }
3146
- if (tileChanged) changed = true;
3147
- });
3148
- });
3149
- return changed;
3150
- }
3151
- writePaintBinaryMaskStroke(color, brush, x0, y0, x1, y1) {
3152
- const alphaIsZero = color >>> 24 === 0;
3153
- if (alphaIsZero) return false;
3154
- const {
3155
- tileShift,
3156
- tileMask,
3157
- target
3158
- } = this.config;
3159
- const {
3160
- w: bW,
3161
- h: bH,
3162
- data: bD,
3163
- centerOffsetX,
3164
- centerOffsetY
3165
- } = brush;
3166
- const scratch = this.scratchBounds;
3167
- let changed = false;
3168
- forEachLinePoint(x0, y0, x1, y1, (px, py) => {
3169
- const topLeftX = Math.floor(px + centerOffsetX);
3170
- const topLeftY = Math.floor(py + centerOffsetY);
3171
- trimRectBounds(topLeftX, topLeftY, bW, bH, target.width, target.height, scratch);
3172
- if (scratch.w <= 0 || scratch.h <= 0) return;
3173
- this.eachTileInBounds(scratch, (tile, bX, bY, bW_t, bH_t) => {
3174
- const d32 = tile.data32;
3175
- let tileChanged = false;
3176
- for (let i = 0; i < bH_t; i++) {
3177
- const canvasY = bY + i;
3178
- const bOff = (canvasY - topLeftY) * bW;
3179
- const tOff = (canvasY & tileMask) << tileShift;
3180
- const dS = tOff + (bX & tileMask);
3181
- for (let j = 0; j < bW_t; j++) {
3182
- const canvasX = bX + j;
3183
- if (bD[bOff + (canvasX - topLeftX)]) {
3184
- const idx = dS + j;
3185
- if (d32[idx] !== color) {
3186
- d32[idx] = color;
3187
- tileChanged = true;
3188
- }
3189
- }
3190
- }
3191
- }
3192
- if (tileChanged) changed = true;
3193
- });
3194
- });
3195
- return changed;
3196
- }
3197
- writeRectStroke(color, brushWidth, brushHeight, x0, y0, x1, y1) {
3198
- const alphaIsZero = color >>> 24 === 0;
3199
- if (alphaIsZero) return false;
3200
- const config = this.config;
3201
- const tileShift = config.tileShift;
3202
- const tileMask = config.tileMask;
3203
- const target = config.target;
3204
- const scratch = this.scratchBounds;
3205
- const centerOffsetX = brushWidth - 1 >> 1;
3206
- const centerOffsetY = brushHeight - 1 >> 1;
3207
- let changed = false;
3208
- forEachLinePoint(x0, y0, x1, y1, (px, py) => {
3209
- const topLeftX = Math.floor(px + centerOffsetX);
3210
- const topLeftY = Math.floor(py + centerOffsetY);
3211
- trimRectBounds(topLeftX, topLeftY, brushWidth, brushHeight, target.width, target.height, scratch);
3212
- if (scratch.w <= 0 || scratch.h <= 0) return;
3213
- this.eachTileInBounds(scratch, (tile, bX, bY, bW_t, bH_t) => {
3214
- const d32 = tile.data32;
3215
- let tileChanged = false;
3216
- for (let i = 0; i < bH_t; i++) {
3217
- const canvasY = bY + i;
3218
- const tOff = (canvasY & tileMask) << tileShift;
3219
- const dS = tOff + (bX & tileMask);
3220
- for (let j = 0; j < bW_t; j++) {
3221
- const idx = dS + j;
3222
- if (d32[idx] !== color) {
3223
- d32[idx] = color;
3224
- tileChanged = true;
3225
- }
3226
- }
3227
- }
3228
- if (tileChanged) {
3229
- changed = true;
3230
- }
3231
- });
3232
- });
3233
- return changed;
3234
- }
3235
- clear() {
3236
- this.tilePool.releaseTiles(this.lookup);
3237
- }
3238
- };
3239
-
3240
- // src/PixelTile/PixelTile.ts
3241
- var PixelTile = class {
3242
- constructor(id, tx, ty, tileSize, tileArea) {
3243
- this.id = id;
3244
- this.tx = tx;
3245
- this.ty = ty;
3246
- this.width = this.height = tileSize;
3247
- this.data32 = new Uint32Array(tileArea);
3248
- const data8 = new Uint8ClampedArray(this.data32.buffer);
3249
- this.imageData = new ImageData(data8, tileSize, tileSize);
3250
- }
3251
- data32;
3252
- width;
3253
- height;
3254
- imageData;
3255
- };
3256
-
3257
- // src/PixelTile/PixelTilePool.ts
3258
- var PixelTilePool = class {
3259
- pool;
3260
- tileSize;
3261
- tileArea;
3262
- constructor(config) {
3263
- this.pool = [];
3264
- this.tileSize = config.tileSize;
3265
- this.tileArea = config.tileArea;
3266
- }
3267
- getTile(id, tx, ty) {
3268
- let tile = this.pool.pop();
3269
- if (tile) {
3270
- tile.id = id;
3271
- tile.tx = tx;
3272
- tile.ty = ty;
3273
- tile.data32.fill(0);
3274
- return tile;
3275
- }
3276
- return new PixelTile(id, tx, ty, this.tileSize, this.tileArea);
3277
- }
3278
- releaseTile(tile) {
3279
- this.pool.push(tile);
3280
- }
3281
- releaseTiles(tiles) {
3282
- let length = tiles.length;
3283
- for (let i = 0; i < length; i++) {
3284
- let tile = tiles[i];
3285
- if (tile) {
3286
- this.pool.push(tile);
3287
- }
3288
- }
3289
- tiles.length = 0;
3290
- }
3291
- };
3292
-
3293
- // src/History/PixelWriter.ts
3294
- var PixelWriter = class {
3295
- historyManager;
3296
- accumulator;
3297
- historyActionFactory;
3298
- config;
3299
- pixelTilePool;
3300
- paintBuffer;
3301
- mutator;
3302
- blendPixelDataOpts = {
3303
- alpha: 255,
3304
- blendFn: sourceOverPerfect,
3305
- x: 0,
3306
- y: 0,
3307
- w: 0,
3308
- h: 0
3309
3638
  };
3310
- _inProgress = false;
3311
- constructor(target, mutatorFactory, {
3312
- tileSize = 256,
3313
- maxHistorySteps = 50,
3314
- historyManager = new HistoryManager(maxHistorySteps),
3315
- historyActionFactory = makeHistoryAction,
3316
- pixelTilePool,
3317
- accumulator
3318
- } = {}) {
3319
- this.config = new PixelEngineConfig(tileSize, target);
3320
- this.historyManager = historyManager;
3321
- this.pixelTilePool = pixelTilePool ?? new PixelTilePool(this.config);
3322
- this.accumulator = accumulator ?? new PixelAccumulator(this.config, this.pixelTilePool);
3323
- this.historyActionFactory = historyActionFactory;
3324
- this.mutator = mutatorFactory(this);
3325
- this.paintBuffer = new PaintBuffer(this.config, this.pixelTilePool);
3326
- }
3327
- /**
3328
- * Executes `transaction` and commits the resulting pixel changes as a single
3329
- * undoable history action.
3330
- *
3331
- * - If `transaction` throws, all accumulated changes are rolled back and the error
3332
- * is re-thrown. No action is committed.
3333
- * - If `transaction` completes without modifying any pixels, no action is committed.
3334
- * - `withHistory` is not re-entrant. Calling it again from inside `transaction` will
3335
- * throw immediately to prevent silent data loss from a nested extractPatch.
3336
- *
3337
- * @param transaction Callback to be executed inside the transaction.
3338
- * @param after Called after both undo and redo — use for generic change notifications.
3339
- * @param afterUndo Called after undo only — use for dimension or state changes specific to undo.
3340
- * @param afterRedo Called after redo only.
3341
- */
3342
- withHistory(transaction, after, afterUndo, afterRedo) {
3343
- if (this._inProgress) {
3344
- throw new Error("withHistory is not re-entrant \u2014 commit or rollback the current operation first");
3345
- }
3346
- this._inProgress = true;
3347
- try {
3348
- transaction(this.mutator);
3349
- } catch (e) {
3350
- this.accumulator.rollbackAfterError();
3351
- throw e;
3352
- } finally {
3353
- this._inProgress = false;
3354
- }
3355
- if (this.accumulator.beforeTiles.length === 0) return;
3356
- const patch = this.accumulator.extractPatch();
3357
- const action = this.historyActionFactory(this, patch, after, afterUndo, afterRedo);
3358
- this.historyManager.commit(action);
3359
- }
3360
- resize(newWidth, newHeight, offsetX = 0, offsetY = 0, after, afterUndo, afterRedo, resizeImageDataFn = resizeImageData) {
3361
- if (this._inProgress) {
3362
- throw new Error("Cannot resize inside a withHistory callback");
3363
- }
3364
- if (this.accumulator.beforeTiles.length > 0) {
3365
- throw new Error("Cannot resize with an open accumulator \u2014 commit or rollback first");
3366
- }
3367
- const config = this.config;
3368
- const target = config.target;
3369
- const beforeImageData = target.imageData;
3370
- const afterImageData = resizeImageDataFn(beforeImageData, newWidth, newHeight, offsetX, offsetY);
3371
- target.set(afterImageData);
3372
- this.historyManager.commit({
3373
- undo: () => {
3374
- target.set(beforeImageData);
3375
- afterUndo?.(beforeImageData);
3376
- after?.(beforeImageData);
3377
- },
3378
- redo: () => {
3379
- target.set(afterImageData);
3380
- afterRedo?.(afterImageData);
3381
- after?.(afterImageData);
3382
- }
3383
- });
3384
- }
3385
- commitPaintBuffer(alpha = 255, blendFn = sourceOverPerfect, blendPixelDataFn = blendPixelData) {
3386
- const paintBuffer = this.paintBuffer;
3387
- const tileShift = paintBuffer.config.tileShift;
3388
- const lookup = paintBuffer.lookup;
3389
- const opts = this.blendPixelDataOpts;
3390
- opts.alpha = alpha;
3391
- opts.blendFn = blendFn;
3392
- for (let i = 0; i < lookup.length; i++) {
3393
- const tile = lookup[i];
3394
- if (tile) {
3395
- const didChange = this.accumulator.storeTileBeforeState(tile.id, tile.tx, tile.ty);
3396
- const dx = tile.tx << tileShift;
3397
- const dy = tile.ty << tileShift;
3398
- opts.x = dx;
3399
- opts.y = dy;
3400
- opts.w = tile.width;
3401
- opts.h = tile.height;
3402
- didChange(blendPixelDataFn(this.config.target, tile, opts));
3403
- }
3404
- }
3405
- paintBuffer.clear();
3406
- }
3407
- };
3639
+ });
3408
3640
 
3409
- // src/PixelData/applyAlphaMaskToPixelData.ts
3410
- function applyAlphaMaskToPixelData(dst, mask, opts = {}) {
3411
- const {
3412
- x: targetX = 0,
3413
- y: targetY = 0,
3414
- w: width = dst.width,
3415
- h: height = dst.height,
3416
- alpha: globalAlpha = 255,
3417
- mx = 0,
3418
- my = 0,
3419
- invertMask = false
3420
- } = opts;
3421
- if (globalAlpha === 0) return false;
3422
- let x = targetX;
3423
- let y = targetY;
3424
- let w = width;
3425
- let h = height;
3426
- if (x < 0) {
3427
- w += x;
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 {
3428
3659
  x = 0;
3429
- }
3430
- if (y < 0) {
3431
- h += y;
3432
3660
  y = 0;
3661
+ w = dst.width;
3662
+ h = dst.height;
3433
3663
  }
3434
- w = Math.min(w, dst.width - x);
3435
- h = Math.min(h, dst.height - y);
3436
- if (w <= 0) return false;
3437
- if (h <= 0) return false;
3438
- const mPitch = mask.w;
3439
- if (mPitch <= 0) return false;
3440
- const startX = mx + (x - targetX);
3441
- const startY = my + (y - targetY);
3442
- const sX0 = Math.max(0, startX);
3443
- const sY0 = Math.max(0, startY);
3444
- const sX1 = Math.min(mPitch, startX + w);
3445
- const sY1 = Math.min(mask.h, startY + h);
3446
- const finalW = sX1 - sX0;
3447
- const finalH = sY1 - sY0;
3448
- if (finalW <= 0) return false;
3449
- if (finalH <= 0) return false;
3450
- const xShift = sX0 - startX;
3451
- const yShift = sY0 - startY;
3664
+ const clip = resolveRectClipping(x, y, w, h, dst.width, dst.height, SCRATCH_RECT);
3665
+ if (!clip.inBounds) return false;
3666
+ const {
3667
+ x: finalX,
3668
+ y: finalY,
3669
+ w: actualW,
3670
+ h: actualH
3671
+ } = clip;
3452
3672
  const dst32 = dst.data32;
3453
3673
  const dw = dst.width;
3454
- const dStride = dw - finalW;
3455
- const mStride = mPitch - finalW;
3456
- const maskData = mask.data;
3457
- let dIdx = (y + yShift) * dw + (x + xShift);
3458
- let mIdx = sY0 * mPitch + sX0;
3459
- let didChange = false;
3460
- for (let iy = 0; iy < h; iy++) {
3461
- for (let ix = 0; ix < w; ix++) {
3462
- const mVal = maskData[mIdx];
3463
- const effectiveM = invertMask ? 255 - mVal : mVal;
3464
- let weight = 0;
3465
- if (effectiveM === 0) {
3466
- weight = 0;
3467
- } else if (effectiveM === 255) {
3468
- weight = globalAlpha;
3469
- } else if (globalAlpha === 255) {
3470
- weight = effectiveM;
3471
- } else {
3472
- weight = effectiveM * globalAlpha + 128 >> 8;
3473
- }
3474
- if (weight === 0) {
3475
- dst32[dIdx] = (dst32[dIdx] & 16777215) >>> 0;
3476
- didChange = true;
3477
- } else if (weight !== 255) {
3478
- const d = dst32[dIdx];
3479
- const da = d >>> 24;
3480
- if (da !== 0) {
3481
- const finalAlpha = da === 255 ? weight : da * weight + 128 >> 8;
3482
- const current = dst32[dIdx];
3483
- const next = (d & 16777215 | finalAlpha << 24) >>> 0;
3484
- if (current !== next) {
3485
- dst32[dIdx] = next;
3486
- didChange = true;
3487
- }
3488
- }
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;
3489
3683
  }
3490
- dIdx++;
3491
- mIdx++;
3492
3684
  }
3493
- dIdx += dStride;
3494
- mIdx += mStride;
3495
3685
  }
3496
- return didChange;
3686
+ return hasChanged;
3497
3687
  }
3498
3688
 
3499
- // src/History/PixelMutator/mutatorApplyAlphaMask.ts
3500
- var defaults11 = {
3501
- applyAlphaMaskToPixelData
3689
+ // src/History/PixelMutator/mutatorClear.ts
3690
+ var defaults15 = {
3691
+ fillPixelData
3502
3692
  };
3503
- var mutatorApplyAlphaMask = ((writer, deps = defaults11) => {
3693
+ var mutatorClear = ((writer, deps = defaults15) => {
3504
3694
  const {
3505
- applyAlphaMaskToPixelData: applyAlphaMaskToPixelData2 = defaults11.applyAlphaMaskToPixelData
3695
+ fillPixelData: fillPixelData2 = defaults15.fillPixelData
3506
3696
  } = deps;
3507
3697
  return {
3508
- applyAlphaMask(mask, opts = {}) {
3509
- let target = writer.config.target;
3510
- const {
3511
- x = 0,
3512
- y = 0,
3513
- w = target.width,
3514
- h = target.height
3515
- } = opts;
3698
+ clear(rect) {
3699
+ const target = writer.config.target;
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;
3516
3704
  const didChange = writer.accumulator.storeRegionBeforeState(x, y, w, h);
3517
- return didChange(applyAlphaMaskToPixelData2(target, mask, opts));
3705
+ return didChange(fillPixelData2(target, 0, x, y, w, h));
3518
3706
  }
3519
3707
  };
3520
3708
  });
3521
3709
 
3522
- // src/PixelData/applyBinaryMaskToPixelData.ts
3523
- function applyBinaryMaskToPixelData(dst, mask, opts = {}) {
3710
+ // src/History/PixelMutator/mutatorFill.ts
3711
+ var defaults16 = {
3712
+ fillPixelData
3713
+ };
3714
+ var mutatorFill = ((writer, deps = defaults16) => {
3524
3715
  const {
3525
- x: targetX = 0,
3526
- y: targetY = 0,
3527
- w: width = dst.width,
3528
- h: height = dst.height,
3529
- alpha: globalAlpha = 255,
3530
- mx = 0,
3531
- my = 0,
3532
- invertMask = false
3533
- } = opts;
3534
- if (globalAlpha === 0) return false;
3535
- let x = targetX;
3536
- let y = targetY;
3537
- let w = width;
3538
- let h = height;
3539
- if (x < 0) {
3540
- w += x;
3541
- x = 0;
3542
- }
3543
- if (y < 0) {
3544
- h += y;
3545
- y = 0;
3546
- }
3547
- w = Math.min(w, dst.width - x);
3548
- h = Math.min(h, dst.height - y);
3549
- if (w <= 0 || h <= 0) return false;
3550
- const mPitch = mask.w;
3551
- if (mPitch <= 0) return false;
3552
- const startX = mx + (x - targetX);
3553
- const startY = my + (y - targetY);
3554
- const sX0 = Math.max(0, startX);
3555
- const sY0 = Math.max(0, startY);
3556
- const sX1 = Math.min(mPitch, startX + w);
3557
- const sY1 = Math.min(mask.h, startY + h);
3558
- const finalW = sX1 - sX0;
3559
- const finalH = sY1 - sY0;
3560
- if (finalW <= 0 || finalH <= 0) {
3561
- return false;
3562
- }
3563
- const xShift = sX0 - startX;
3564
- const yShift = sY0 - startY;
3565
- const dst32 = dst.data32;
3566
- const dw = dst.width;
3567
- const dStride = dw - finalW;
3568
- const mStride = mPitch - finalW;
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));
3723
+ }
3724
+ };
3725
+ });
3726
+ var mutatorFillRect = ((writer, deps = defaults16) => {
3727
+ const {
3728
+ fillPixelData: fillPixelData2 = defaults16.fillPixelData
3729
+ } = deps;
3730
+ return {
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));
3735
+ }
3736
+ };
3737
+ });
3738
+
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;
3746
+ const {
3747
+ x: finalX,
3748
+ y: finalY,
3749
+ w: actualW,
3750
+ h: actualH
3751
+ } = clip;
3569
3752
  const maskData = mask.data;
3570
- let dIdx = (y + yShift) * dw + (x + xShift);
3571
- let mIdx = sY0 * mPitch + sX0;
3572
- let didChange = false;
3573
- for (let iy = 0; iy < finalH; iy++) {
3574
- for (let ix = 0; ix < finalW; ix++) {
3575
- const mVal = maskData[mIdx];
3576
- const isMaskedOut = invertMask ? mVal !== 0 : mVal === 0;
3577
- if (isMaskedOut) {
3578
- const current = dst32[dIdx];
3579
- const next = (current & 16777215) >>> 0;
3580
- if (current !== next) {
3581
- dst32[dIdx] = next;
3582
- didChange = true;
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;
3583
3770
  }
3584
- } else if (globalAlpha !== 255) {
3585
- const d = dst32[dIdx];
3586
- const da = d >>> 24;
3587
- if (da !== 0) {
3588
- const finalAlpha = da === 255 ? globalAlpha : da * globalAlpha + 128 >> 8;
3589
- const next = (d & 16777215 | finalAlpha << 24) >>> 0;
3590
- if (d !== next) {
3591
- dst32[dIdx] = next;
3592
- didChange = true;
3593
- }
3771
+ }
3772
+ }
3773
+ }
3774
+ return hasChanged;
3775
+ }
3776
+
3777
+ // src/History/PixelMutator/mutatorFillBinaryMask.ts
3778
+ var defaults17 = {
3779
+ fillPixelDataBinaryMask
3780
+ };
3781
+ var mutatorFillBinaryMask = ((writer, deps = defaults17) => {
3782
+ const {
3783
+ fillPixelDataBinaryMask: fillPixelDataBinaryMask2 = defaults17.fillPixelDataBinaryMask
3784
+ } = deps;
3785
+ return {
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));
3789
+ }
3790
+ };
3791
+ });
3792
+
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;
3594
3829
  }
3830
+ dIdx++;
3831
+ mIdx++;
3595
3832
  }
3596
- dIdx++;
3597
- mIdx++;
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++;
3841
+ }
3842
+ dIdx += dStride;
3598
3843
  }
3599
- dIdx += dStride;
3600
- mIdx += mStride;
3601
3844
  }
3602
- return didChange;
3845
+ return true;
3603
3846
  }
3604
3847
 
3605
- // src/History/PixelMutator/mutatorApplyBinaryMask.ts
3606
- var defaults12 = {
3607
- applyBinaryMaskToPixelData
3848
+ // src/History/PixelMutator/mutatorInvert.ts
3849
+ var defaults18 = {
3850
+ invertPixelData
3608
3851
  };
3609
- var mutatorApplyBinaryMask = ((writer, deps = defaults12) => {
3852
+ var mutatorInvert = ((writer, deps = defaults18) => {
3610
3853
  const {
3611
- applyBinaryMaskToPixelData: applyBinaryMaskToPixelData2 = defaults12.applyBinaryMaskToPixelData
3854
+ invertPixelData: invertPixelData2 = defaults18.invertPixelData
3612
3855
  } = deps;
3613
3856
  return {
3614
- applyBinaryMask(mask, opts = {}) {
3615
- let target = writer.config.target;
3616
- const {
3617
- x = 0,
3618
- y = 0,
3619
- w = target.width,
3620
- h = target.height
3621
- } = opts;
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;
3622
3863
  const didChange = writer.accumulator.storeRegionBeforeState(x, y, w, h);
3623
- return didChange(applyBinaryMaskToPixelData2(target, mask, opts));
3864
+ return didChange(invertPixelData2(target, opts));
3624
3865
  }
3625
3866
  };
3626
3867
  });
3627
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
+
3628
3918
  // src/ImageData/copyImageData.ts
3629
3919
  function copyImageData({
3630
3920
  data,
@@ -3645,17 +3935,6 @@ function copyImageDataLike({
3645
3935
  };
3646
3936
  }
3647
3937
 
3648
- // src/ImageData/ImageDataLike.ts
3649
- function makeImageDataLike(width, height, data) {
3650
- const size = width * height * 4;
3651
- const buffer = data ? new Uint8ClampedArray(data.buffer, data.byteOffset, size) : new Uint8ClampedArray(size);
3652
- return {
3653
- width,
3654
- height,
3655
- data: buffer
3656
- };
3657
- }
3658
-
3659
3938
  // src/ImageData/imageDataToAlphaMaskBuffer.ts
3660
3939
  function imageDataToAlphaMaskBuffer(imageData) {
3661
3940
  const {
@@ -3740,23 +4019,10 @@ function resampleImageData(source, factor) {
3740
4019
  const {
3741
4020
  data,
3742
4021
  width,
3743
- height
3744
- } = resample32(src32, source.width, source.height, factor);
3745
- const uint8ClampedArray = new Uint8ClampedArray(data.buffer);
3746
- return new ImageData(uint8ClampedArray, width, height);
3747
- }
3748
-
3749
- // src/ImageData/ReusableImageData.ts
3750
- function makeReusableImageData() {
3751
- let imageData = null;
3752
- return function getReusableImageData(width, height) {
3753
- if (imageData === null || imageData.width !== width || imageData.height !== height) {
3754
- imageData = new ImageData(width, height);
3755
- } else {
3756
- imageData.data.fill(0);
3757
- }
3758
- return imageData;
3759
- };
4022
+ height
4023
+ } = resample32(src32, source.width, source.height, factor);
4024
+ const uint8ClampedArray = new Uint8ClampedArray(data.buffer);
4025
+ return new ImageData(uint8ClampedArray, width, height);
3760
4026
  }
3761
4027
 
3762
4028
  // src/ImageData/serialization.ts
@@ -3913,18 +4179,6 @@ function writeImageDataBuffer(target, data, _x, _y, _w, _h) {
3913
4179
  }
3914
4180
  }
3915
4181
 
3916
- // src/IndexedImage/getIndexedImageColorCounts.ts
3917
- function getIndexedImageColorCounts(indexedImage) {
3918
- const data = indexedImage.data;
3919
- const palette = indexedImage.palette;
3920
- const frequencies = new Int32Array(palette.length);
3921
- for (let i = 0; i < data.length; i++) {
3922
- const colorIndex = data[i];
3923
- frequencies[colorIndex]++;
3924
- }
3925
- return frequencies;
3926
- }
3927
-
3928
4182
  // src/IndexedImage/IndexedImage.ts
3929
4183
  var IndexedImage = class _IndexedImage {
3930
4184
  /** The width of the image in pixels. */
@@ -4003,6 +4257,18 @@ var IndexedImage = class _IndexedImage {
4003
4257
  }
4004
4258
  };
4005
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
+
4006
4272
  // src/IndexedImage/indexedImageToAverageColor.ts
4007
4273
  function indexedImageToAverageColor(indexedImage, includeTransparent = false) {
4008
4274
  const {
@@ -4161,16 +4427,14 @@ function makeBinaryMask(w, h, data) {
4161
4427
  }
4162
4428
 
4163
4429
  // src/Mask/applyBinaryMaskToAlphaMask.ts
4164
- function applyBinaryMaskToAlphaMask(alphaMaskDst, binaryMaskSrc, opts = {}) {
4165
- const {
4166
- x: targetX = 0,
4167
- y: targetY = 0,
4168
- w: reqWidth = 0,
4169
- h: reqHeight = 0,
4170
- mx = 0,
4171
- my = 0,
4172
- invertMask = false
4173
- } = 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;
4174
4438
  const dstWidth = alphaMaskDst.w;
4175
4439
  if (dstWidth <= 0) return;
4176
4440
  if (binaryMaskSrc.data.length === 0) return;
@@ -4593,6 +4857,148 @@ function pushPiece(dest, r, x, y, w, h) {
4593
4857
  });
4594
4858
  }
4595
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 */,
4994
+ data,
4995
+ w: width,
4996
+ h: height,
4997
+ centerOffsetX: -(width >> 1),
4998
+ centerOffsetY: -(height >> 1)
4999
+ };
5000
+ }
5001
+
4596
5002
  // src/PixelData/PixelData.ts
4597
5003
  var PixelData = class {
4598
5004
  data32;
@@ -4614,170 +5020,96 @@ var PixelData = class {
4614
5020
  }
4615
5021
  };
4616
5022
 
4617
- // src/PixelData/blendColorPixelDataAlphaMask.ts
4618
- function blendColorPixelDataAlphaMask(dst, color, mask, opts = {}) {
4619
- const targetX = opts.x ?? 0;
4620
- const targetY = opts.y ?? 0;
4621
- const w = opts.w ?? mask.w;
4622
- const h = opts.h ?? mask.h;
4623
- const globalAlpha = opts.alpha ?? 255;
4624
- const blendFn = opts.blendFn ?? sourceOverPerfect;
4625
- const mx = opts.mx ?? 0;
4626
- const my = opts.my ?? 0;
4627
- const invertMask = opts.invertMask ?? false;
4628
- if (globalAlpha === 0) return false;
4629
- const baseSrcAlpha = color >>> 24;
4630
- const isOverwrite = blendFn.isOverwrite || false;
4631
- if (baseSrcAlpha === 0 && !isOverwrite) return false;
4632
- let x = targetX;
4633
- let y = targetY;
4634
- let actualW = w;
4635
- let actualH = h;
4636
- if (x < 0) {
4637
- actualW += x;
4638
- x = 0;
4639
- }
4640
- if (y < 0) {
4641
- actualH += y;
4642
- y = 0;
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);
4643
5029
  }
4644
- actualW = Math.min(actualW, dst.width - x);
4645
- actualH = Math.min(actualH, dst.height - y);
4646
- if (actualW <= 0 || actualH <= 0) return false;
4647
- const dx = x - targetX | 0;
4648
- const dy = y - targetY | 0;
4649
- const dst32 = dst.data32;
4650
- const dw = dst.width;
4651
- const mPitch = mask.w;
4652
- const maskData = mask.data;
4653
- let dIdx = y * dw + x | 0;
4654
- let mIdx = (my + dy) * mPitch + (mx + dx) | 0;
4655
- const dStride = dw - actualW | 0;
4656
- const mStride = mPitch - actualW | 0;
4657
- const isOpaque = globalAlpha === 255;
4658
- const colorRGB = color & 16777215;
4659
- let didChange = false;
4660
- for (let iy = 0; iy < actualH; iy++) {
4661
- for (let ix = 0; ix < actualW; ix++) {
4662
- const mVal = maskData[mIdx];
4663
- const effM = invertMask ? 255 - mVal : mVal;
4664
- if (effM === 0) {
4665
- dIdx++;
4666
- mIdx++;
4667
- continue;
4668
- }
4669
- let weight = globalAlpha;
4670
- if (isOpaque) {
4671
- weight = effM;
4672
- } else if (effM !== 255) {
4673
- weight = effM * globalAlpha + 128 >> 8;
4674
- }
4675
- if (weight === 0) {
4676
- dIdx++;
4677
- mIdx++;
4678
- continue;
4679
- }
4680
- let finalCol = color;
4681
- if (weight < 255) {
4682
- const a = baseSrcAlpha * weight + 128 >> 8;
4683
- if (a === 0 && !isOverwrite) {
4684
- dIdx++;
4685
- mIdx++;
4686
- continue;
4687
- }
4688
- finalCol = (colorRGB | a << 24) >>> 0;
4689
- }
4690
- const current = dst32[dIdx];
4691
- const next = blendFn(finalCol, current);
4692
- if (current !== next) {
4693
- dst32[dIdx] = next;
4694
- didChange = true;
4695
- }
4696
- dIdx++;
4697
- mIdx++;
4698
- }
4699
- dIdx += dStride;
4700
- mIdx += mStride;
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);
4701
5038
  }
4702
- return didChange;
4703
5039
  }
4704
5040
 
4705
- // src/PixelData/blendColorPixelDataBinaryMask.ts
4706
- function blendColorPixelDataBinaryMask(dst, color, mask, opts = {}) {
4707
- const targetX = opts.x ?? 0;
4708
- const targetY = opts.y ?? 0;
4709
- let w = opts.w ?? mask.w;
4710
- let h = opts.h ?? mask.h;
4711
- const globalAlpha = opts.alpha ?? 255;
4712
- const blendFn = opts.blendFn ?? sourceOverPerfect;
4713
- const mx = opts.mx ?? 0;
4714
- const my = opts.my ?? 0;
4715
- const invertMask = opts.invertMask ?? false;
4716
- if (globalAlpha === 0) return false;
4717
- const baseSrcAlpha = color >>> 24;
4718
- const isOverwrite = blendFn.isOverwrite || false;
4719
- if (baseSrcAlpha === 0 && !isOverwrite) return false;
4720
- let x = targetX;
4721
- let y = targetY;
4722
- if (x < 0) {
4723
- w += x;
4724
- x = 0;
4725
- }
4726
- if (y < 0) {
4727
- h += y;
4728
- y = 0;
4729
- }
4730
- const actualW = Math.min(w, dst.width - x);
4731
- const actualH = Math.min(h, dst.height - y);
4732
- if (actualW <= 0 || actualH <= 0) return false;
4733
- let baseColorWithGlobalAlpha = color;
4734
- if (globalAlpha < 255) {
4735
- const a = baseSrcAlpha * globalAlpha + 128 >> 8;
4736
- if (a === 0 && !isOverwrite) return false;
4737
- baseColorWithGlobalAlpha = (color & 16777215 | a << 24) >>> 0;
5041
+ // src/PixelData/blendColorPixelDataPaintAlphaMask.ts
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);
4738
5093
  }
4739
- const dx = x - targetX | 0;
4740
- const dy = y - targetY | 0;
4741
- const dst32 = dst.data32;
4742
- const dw = dst.width;
4743
- const mPitch = mask.w;
4744
- const maskData = mask.data;
4745
- let dIdx = y * dw + x | 0;
4746
- let mIdx = (my + dy) * mPitch + (mx + dx) | 0;
4747
- const dStride = dw - actualW | 0;
4748
- const mStride = mPitch - actualW | 0;
4749
- const skipVal = invertMask ? 1 : 0;
4750
- let didChange = false;
4751
- for (let iy = 0; iy < actualH; iy++) {
4752
- for (let ix = 0; ix < actualW; ix++) {
4753
- if (maskData[mIdx] === skipVal) {
4754
- dIdx++;
4755
- mIdx++;
4756
- continue;
4757
- }
4758
- const current = dst32[dIdx];
4759
- const next = blendFn(baseColorWithGlobalAlpha, current);
4760
- if (current !== next) {
4761
- dst32[dIdx] = next;
4762
- didChange = true;
4763
- }
4764
- dIdx++;
4765
- mIdx++;
4766
- }
4767
- dIdx += dStride;
4768
- mIdx += mStride;
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);
4769
5102
  }
4770
- return didChange;
4771
5103
  }
4772
5104
 
4773
5105
  // src/PixelData/blendPixelDataPaintBuffer.ts
4774
- var SCRATCH_OPTS = {
5106
+ var SCRATCH_OPTS4 = {
4775
5107
  x: 0,
4776
5108
  y: 0,
4777
5109
  alpha: 255,
4778
5110
  blendFn: void 0
4779
5111
  };
4780
- function blendPixelDataPaintBuffer(paintBuffer, target, alpha = 255, blendFn, blendPixelDataFn = blendPixelData) {
5112
+ function blendPixelDataPaintBuffer(target, paintBuffer, alpha = 255, blendFn, blendPixelDataFn = blendPixelData) {
4781
5113
  const tileShift = paintBuffer.config.tileShift;
4782
5114
  const lookup = paintBuffer.lookup;
4783
5115
  for (let i = 0; i < lookup.length; i++) {
@@ -4785,17 +5117,61 @@ function blendPixelDataPaintBuffer(paintBuffer, target, alpha = 255, blendFn, bl
4785
5117
  if (tile) {
4786
5118
  const x = tile.tx << tileShift;
4787
5119
  const y = tile.ty << tileShift;
4788
- SCRATCH_OPTS.x = x;
4789
- SCRATCH_OPTS.y = y;
4790
- SCRATCH_OPTS.alpha = alpha;
4791
- SCRATCH_OPTS.blendFn = blendFn;
4792
- 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);
4793
5125
  }
4794
5126
  }
4795
5127
  }
4796
5128
 
4797
- // src/PixelData/clearPixelData.ts
4798
- 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) {
4799
5175
  fillPixelDataFast(dst, 0, rect);
4800
5176
  }
4801
5177
 
@@ -4858,26 +5234,6 @@ function extractPixelData(source, _x, _y, _w, _h) {
4858
5234
  return result;
4859
5235
  }
4860
5236
 
4861
- // src/PixelData/PixelBuffer32.ts
4862
- var PixelBuffer32 = class _PixelBuffer32 {
4863
- constructor(width, height, data32) {
4864
- this.width = width;
4865
- this.height = height;
4866
- this.data32 = data32 ?? new Uint32Array(width * height);
4867
- }
4868
- data32;
4869
- set(width, height, data32) {
4870
- ;
4871
- this.data32 = data32 ?? new Uint32Array(width * height);
4872
- this.width = width;
4873
- this.height = height;
4874
- }
4875
- copy() {
4876
- const newData32 = new Uint32Array(this.data32);
4877
- return new _PixelBuffer32(this.width, this.height, newData32);
4878
- }
4879
- };
4880
-
4881
5237
  // src/PixelData/pixelDataToAlphaMask.ts
4882
5238
  function pixelDataToAlphaMask(pixelData) {
4883
5239
  const {
@@ -5029,164 +5385,17 @@ function writePaintBufferToPixelData(target, paintBuffer, writePixelDataBufferFn
5029
5385
  }
5030
5386
  }
5031
5387
  }
5032
-
5033
- // src/Paint/makeCirclePaintAlphaMask.ts
5034
- function makeCirclePaintAlphaMask(size, fallOff = (d) => d) {
5035
- const area = size * size;
5036
- const data = new Uint8Array(area);
5037
- const radius = size / 2;
5038
- const invR = 1 / radius;
5039
- const centerOffset = -Math.ceil(radius - 0.5);
5040
- for (let y = 0; y < size; y++) {
5041
- const rowOffset = y * size;
5042
- const dy = y - radius + 0.5;
5043
- const dy2 = dy * dy;
5044
- for (let x = 0; x < size; x++) {
5045
- const dx = x - radius + 0.5;
5046
- const distSqr = dx * dx + dy2;
5047
- if (distSqr <= radius * radius) {
5048
- const dist = Math.sqrt(distSqr) * invR;
5049
- const strength = fallOff(1 - dist);
5050
- if (strength > 0) {
5051
- const intensity = strength * 255 | 0;
5052
- data[rowOffset + x] = Math.max(0, Math.min(255, intensity));
5053
- }
5054
- }
5055
- }
5056
- }
5057
- return {
5058
- type: 0 /* ALPHA */,
5059
- data,
5060
- w: size,
5061
- h: size,
5062
- centerOffsetX: centerOffset,
5063
- centerOffsetY: centerOffset
5064
- };
5065
- }
5066
-
5067
- // src/Paint/makeCirclePaintBinaryMask.ts
5068
- function makeCirclePaintBinaryMask(size) {
5069
- const area = size * size;
5070
- const data = new Uint8Array(area);
5071
- const radius = size / 2;
5072
- const centerOffset = -Math.ceil(radius - 0.5);
5073
- for (let y = 0; y < size; y++) {
5074
- for (let x = 0; x < size; x++) {
5075
- const dx = x - radius + 0.5;
5076
- const dy = y - radius + 0.5;
5077
- const distSqr = dx * dx + dy * dy;
5078
- if (distSqr <= radius * radius) {
5079
- data[y * size + x] = 1;
5080
- }
5081
- }
5082
- }
5083
- return {
5084
- type: 1 /* BINARY */,
5085
- data,
5086
- w: size,
5087
- h: size,
5088
- centerOffsetX: centerOffset,
5089
- centerOffsetY: centerOffset
5090
- };
5091
- }
5092
-
5093
- // src/Internal/helpers.ts
5094
- var macro_halfAndFloor = (value) => value >> 1;
5095
-
5096
- // src/Paint/makePaintMask.ts
5097
- function makePaintBinaryMask(mask) {
5098
- return {
5099
- type: 1 /* BINARY */,
5100
- data: mask.data,
5101
- w: mask.w,
5102
- h: mask.h,
5103
- centerOffsetX: -macro_halfAndFloor(mask.w),
5104
- centerOffsetY: -macro_halfAndFloor(mask.h)
5105
- };
5106
- }
5107
- function makePaintAlphaMask(mask) {
5108
- return {
5109
- type: 0 /* ALPHA */,
5110
- data: mask.data,
5111
- w: mask.w,
5112
- h: mask.h,
5113
- centerOffsetX: -macro_halfAndFloor(mask.w),
5114
- centerOffsetY: -macro_halfAndFloor(mask.h)
5115
- };
5116
- }
5117
-
5118
- // src/Paint/makeRectFalloffPaintAlphaMask.ts
5119
- function makeRectFalloffPaintAlphaMask(width, height, fallOff = (d) => d) {
5120
- const fPx = Math.floor(width / 2);
5121
- const fPy = Math.floor(height / 2);
5122
- const invHalfW = 2 / width;
5123
- const invHalfH = 2 / height;
5124
- const offX = width % 2 === 0 ? 0.5 : 0;
5125
- const offY = height % 2 === 0 ? 0.5 : 0;
5126
- const area = width * height;
5127
- const data = new Uint8Array(area);
5128
- for (let y = 0; y < height; y++) {
5129
- const dy = Math.abs(y - fPy + offY) * invHalfH;
5130
- const rowOffset = y * width;
5131
- for (let x = 0; x < width; x++) {
5132
- const dx = Math.abs(x - fPx + offX) * invHalfW;
5133
- const dist = dx > dy ? dx : dy;
5134
- const strength = fallOff(1 - dist);
5135
- if (strength > 0) {
5136
- const intensity = strength * 255 | 0;
5137
- data[rowOffset + x] = Math.max(0, Math.min(255, intensity));
5138
- }
5139
- }
5140
- }
5141
- return {
5142
- type: 0 /* ALPHA */,
5143
- data,
5144
- w: width,
5145
- h: height,
5146
- centerOffsetX: -macro_halfAndFloor(width),
5147
- centerOffsetY: -macro_halfAndFloor(height)
5148
- };
5149
- }
5150
-
5151
- // src/Paint/PaintBufferCanvasRenderer.ts
5152
- function makePaintBufferCanvasRenderer(paintBuffer, offscreenCanvasClass = OffscreenCanvas) {
5153
- const config = paintBuffer.config;
5154
- const tileSize = config.tileSize;
5155
- const tileShift = config.tileShift;
5156
- const lookup = paintBuffer.lookup;
5157
- const canvas = new offscreenCanvasClass(tileSize, tileSize);
5158
- const ctx = canvas.getContext("2d");
5159
- if (!ctx) throw new Error(CANVAS_CTX_FAILED);
5160
- ctx.imageSmoothingEnabled = false;
5161
- return function drawPaintBuffer(targetCtx, alpha = 255, compOperation = "source-over") {
5162
- targetCtx.globalAlpha = alpha / 255;
5163
- targetCtx.globalCompositeOperation = compOperation;
5164
- for (let i = 0; i < lookup.length; i++) {
5165
- const tile = lookup[i];
5166
- if (tile) {
5167
- const dx = tile.tx << tileShift;
5168
- const dy = tile.ty << tileShift;
5169
- ctx.putImageData(tile.imageData, 0, 0);
5170
- targetCtx.drawImage(canvas, dx, dy);
5171
- }
5172
- }
5173
- targetCtx.globalAlpha = 1;
5174
- targetCtx.globalCompositeOperation = "source-over";
5175
- };
5176
- }
5177
5388
  // Annotate the CommonJS export names for ESM import in node:
5178
5389
  0 && (module.exports = {
5179
5390
  BASE_FAST_BLEND_MODE_FUNCTIONS,
5180
5391
  BASE_PERFECT_BLEND_MODE_FUNCTIONS,
5181
5392
  BaseBlendMode,
5182
- CANVAS_CTX_FAILED,
5393
+ CANVAS_COMPOSITE_MAP,
5183
5394
  HistoryManager,
5184
5395
  IndexedImage,
5185
5396
  MaskType,
5186
- OFFSCREEN_CANVAS_CTX_FAILED,
5187
5397
  PaintBuffer,
5188
5398
  PixelAccumulator,
5189
- PixelBuffer32,
5190
5399
  PixelData,
5191
5400
  PixelEngineConfig,
5192
5401
  PixelTile,
@@ -5196,18 +5405,24 @@ function makePaintBufferCanvasRenderer(paintBuffer, offscreenCanvasClass = Offsc
5196
5405
  applyAlphaMaskToPixelData,
5197
5406
  applyBinaryMaskToAlphaMask,
5198
5407
  applyBinaryMaskToPixelData,
5408
+ applyMaskToPixelData,
5199
5409
  applyPatchTiles,
5200
5410
  base64DecodeArrayBuffer,
5201
5411
  base64EncodeArrayBuffer,
5202
5412
  blendColorPixelData,
5203
5413
  blendColorPixelDataAlphaMask,
5204
5414
  blendColorPixelDataBinaryMask,
5415
+ blendColorPixelDataMask,
5416
+ blendColorPixelDataPaintAlphaMask,
5417
+ blendColorPixelDataPaintBinaryMask,
5418
+ blendColorPixelDataPaintMask,
5205
5419
  blendPixel,
5206
5420
  blendPixelData,
5207
5421
  blendPixelDataAlphaMask,
5208
5422
  blendPixelDataBinaryMask,
5423
+ blendPixelDataMask,
5209
5424
  blendPixelDataPaintBuffer,
5210
- clearPixelData,
5425
+ clearPixelDataFast,
5211
5426
  color32ToCssRGBA,
5212
5427
  color32ToHex,
5213
5428
  colorBurnFast,
@@ -5280,6 +5495,8 @@ function makePaintBufferCanvasRenderer(paintBuffer, offscreenCanvasClass = Offsc
5280
5495
  makeCanvasFrameRenderer,
5281
5496
  makeCirclePaintAlphaMask,
5282
5497
  makeCirclePaintBinaryMask,
5498
+ makeClippedBlit,
5499
+ makeClippedRect,
5283
5500
  makeFastBlendModeRegistry,
5284
5501
  makeFullPixelMutator,
5285
5502
  makeHistoryAction,
@@ -5301,11 +5518,17 @@ function makePaintBufferCanvasRenderer(paintBuffer, offscreenCanvasClass = Offsc
5301
5518
  multiplyPerfect,
5302
5519
  mutatorApplyAlphaMask,
5303
5520
  mutatorApplyBinaryMask,
5521
+ mutatorApplyMask,
5522
+ mutatorBlendAlphaMask,
5523
+ mutatorBlendBinaryMask,
5304
5524
  mutatorBlendColor,
5525
+ mutatorBlendColorPaintAlphaMask,
5526
+ mutatorBlendColorPaintBinaryMask,
5527
+ mutatorBlendColorPaintMask,
5528
+ mutatorBlendMask,
5529
+ mutatorBlendPaintRect,
5305
5530
  mutatorBlendPixel,
5306
5531
  mutatorBlendPixelData,
5307
- mutatorBlendPixelDataAlphaMask,
5308
- mutatorBlendPixelDataBinaryMask,
5309
5532
  mutatorClear,
5310
5533
  mutatorFill,
5311
5534
  mutatorFillBinaryMask,
@@ -5323,10 +5546,13 @@ function makePaintBufferCanvasRenderer(paintBuffer, offscreenCanvasClass = Offsc
5323
5546
  pixelDataToAlphaMask,
5324
5547
  reflectPixelDataHorizontal,
5325
5548
  reflectPixelDataVertical,
5549
+ resample32,
5326
5550
  resampleImageData,
5327
5551
  resampleIndexedImage,
5328
5552
  resamplePixelData,
5329
5553
  resizeImageData,
5554
+ resolveBlitClipping,
5555
+ resolveRectClipping,
5330
5556
  rotatePixelData,
5331
5557
  screenFast,
5332
5558
  screenPerfect,