pixel-data-js 0.23.0 → 0.24.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 (66) hide show
  1. package/dist/index.dev.cjs +1024 -596
  2. package/dist/index.dev.cjs.map +1 -1
  3. package/dist/index.dev.js +1010 -592
  4. package/dist/index.dev.js.map +1 -1
  5. package/dist/index.prod.cjs +1024 -596
  6. package/dist/index.prod.cjs.map +1 -1
  7. package/dist/index.prod.d.ts +280 -165
  8. package/dist/index.prod.js +1010 -592
  9. package/dist/index.prod.js.map +1 -1
  10. package/package.json +3 -2
  11. package/src/Canvas/CanvasFrameRenderer.ts +57 -0
  12. package/src/Canvas/ReusableCanvas.ts +60 -11
  13. package/src/History/HistoryAction.ts +38 -0
  14. package/src/History/HistoryManager.ts +4 -8
  15. package/src/History/PixelAccumulator.ts +95 -80
  16. package/src/History/PixelEngineConfig.ts +18 -6
  17. package/src/History/PixelMutator/mutatorApplyAlphaMask.ts +6 -6
  18. package/src/History/PixelMutator/mutatorApplyBinaryMask.ts +6 -6
  19. package/src/History/PixelMutator/mutatorApplyCircleBrushStroke.ts +6 -5
  20. package/src/History/PixelMutator/mutatorApplyCirclePencil.ts +22 -22
  21. package/src/History/PixelMutator/mutatorApplyCirclePencilStroke.ts +6 -5
  22. package/src/History/PixelMutator/mutatorApplyRectBrush.ts +19 -19
  23. package/src/History/PixelMutator/mutatorApplyRectBrushStroke.ts +6 -4
  24. package/src/History/PixelMutator/mutatorApplyRectPencil.ts +20 -20
  25. package/src/History/PixelMutator/mutatorApplyRectPencilStroke.ts +6 -4
  26. package/src/History/PixelMutator/mutatorBlendColor.ts +8 -5
  27. package/src/History/PixelMutator/mutatorBlendColorCircleMask.ts +71 -0
  28. package/src/History/PixelMutator/mutatorBlendPixel.ts +22 -26
  29. package/src/History/PixelMutator/mutatorBlendPixelData.ts +5 -3
  30. package/src/History/PixelMutator/mutatorBlendPixelDataAlphaMask.ts +5 -3
  31. package/src/History/PixelMutator/mutatorBlendPixelDataBinaryMask.ts +5 -3
  32. package/src/History/PixelMutator/mutatorClear.ts +7 -6
  33. package/src/History/PixelMutator/mutatorFill.ts +34 -9
  34. package/src/History/PixelMutator/mutatorFillBinaryMask.ts +4 -2
  35. package/src/History/PixelMutator/mutatorInvert.ts +8 -4
  36. package/src/History/PixelMutator.ts +4 -3
  37. package/src/History/PixelPatchTiles.ts +3 -15
  38. package/src/History/PixelWriter.ts +29 -33
  39. package/src/ImageData/ReusableImageData.ts +3 -5
  40. package/src/Mask/{CircleBrushAlphaMask.ts → CircleAlphaMask.ts} +2 -2
  41. package/src/Mask/{CircleBrushBinaryMask.ts → CircleBinaryMask.ts} +2 -2
  42. package/src/PixelData/PixelData.ts +1 -27
  43. package/src/PixelData/applyAlphaMaskToPixelData.ts +19 -9
  44. package/src/PixelData/applyBinaryMaskToPixelData.ts +24 -17
  45. package/src/PixelData/applyRectBrushToPixelData.ts +18 -5
  46. package/src/PixelData/blendColorPixelData.ts +31 -7
  47. package/src/PixelData/blendColorPixelDataAlphaMask.ts +16 -6
  48. package/src/PixelData/blendColorPixelDataBinaryMask.ts +16 -7
  49. package/src/PixelData/{applyCircleBrushToPixelData.ts → blendColorPixelDataCircleMask.ts} +11 -10
  50. package/src/PixelData/blendPixel.ts +47 -0
  51. package/src/PixelData/blendPixelData.ts +14 -4
  52. package/src/PixelData/blendPixelDataAlphaMask.ts +12 -4
  53. package/src/PixelData/blendPixelDataBinaryMask.ts +13 -4
  54. package/src/PixelData/blendPixelDataPaintBuffer.ts +37 -0
  55. package/src/PixelData/clearPixelData.ts +2 -2
  56. package/src/PixelData/fillPixelData.ts +26 -16
  57. package/src/PixelData/fillPixelDataBinaryMask.ts +12 -4
  58. package/src/PixelData/fillPixelDataFast.ts +94 -0
  59. package/src/PixelData/invertPixelData.ts +4 -2
  60. package/src/PixelTile/PaintBuffer.ts +122 -0
  61. package/src/PixelTile/PaintBufferRenderer.ts +40 -0
  62. package/src/PixelTile/PixelTile.ts +21 -0
  63. package/src/PixelTile/PixelTilePool.ts +63 -0
  64. package/src/_types.ts +9 -9
  65. package/src/index.ts +16 -6
  66. package/src/History/PixelMutator/mutatorApplyCircleBrush.ts +0 -78
package/dist/index.dev.js CHANGED
@@ -436,6 +436,26 @@ function floodFillSelection(img, startX, startY, {
436
436
  };
437
437
  }
438
438
 
439
+ // src/Algorithm/forEachLinePoint.ts
440
+ function forEachLinePoint(x0, y0, x1, y1, callback) {
441
+ const dx = x1 - x0;
442
+ const dy = y1 - y0;
443
+ const steps = Math.max(Math.abs(dx), Math.abs(dy));
444
+ if (steps === 0) {
445
+ callback(x0, y0);
446
+ return;
447
+ }
448
+ const xInc = dx / steps;
449
+ const yInc = dy / steps;
450
+ let curX = x0;
451
+ let curY = y0;
452
+ for (let i = 0; i <= steps; i++) {
453
+ callback(curX, curY);
454
+ curX += xInc;
455
+ curY += yInc;
456
+ }
457
+ }
458
+
439
459
  // src/BlendModes/blend-modes.ts
440
460
  var BaseBlendMode = {
441
461
  overwrite: 0,
@@ -1513,51 +1533,104 @@ var getKeyByValue = (obj, value) => {
1513
1533
  var OFFSCREEN_CANVAS_CTX_FAILED = "Failed to create OffscreenCanvas context";
1514
1534
  var CANVAS_CTX_FAILED = "Failed to create Canvas context";
1515
1535
 
1516
- // src/Canvas/PixelCanvas.ts
1517
- function makePixelCanvas(canvas) {
1518
- const ctx = canvas.getContext("2d");
1519
- if (!ctx) throw new Error(CANVAS_CTX_FAILED);
1520
- ctx.imageSmoothingEnabled = false;
1521
- return {
1522
- canvas,
1523
- ctx,
1524
- resize(w, h) {
1525
- canvas.width = w;
1526
- canvas.height = h;
1527
- ctx.imageSmoothingEnabled = false;
1528
- }
1529
- };
1530
- }
1531
-
1532
1536
  // src/Canvas/ReusableCanvas.ts
1533
1537
  function makeReusableCanvas() {
1538
+ return makeReusableCanvasMeta((w, h) => {
1539
+ const canvas = document.createElement("canvas");
1540
+ canvas.width = w;
1541
+ canvas.height = h;
1542
+ return canvas;
1543
+ });
1544
+ }
1545
+ function makeReusableOffscreenCanvas() {
1546
+ return makeReusableCanvasMeta((w, h) => new OffscreenCanvas(w, h));
1547
+ }
1548
+ function makeReusableCanvasMeta(factory) {
1534
1549
  let canvas = null;
1535
1550
  let ctx = null;
1551
+ const result = {
1552
+ canvas: null,
1553
+ ctx: null
1554
+ };
1536
1555
  function get2(width, height) {
1537
1556
  if (canvas === null) {
1538
- canvas = document.createElement("canvas");
1557
+ canvas = factory(width, height);
1539
1558
  ctx = canvas.getContext("2d");
1540
- if (!ctx) throw new Error(CANVAS_CTX_FAILED);
1559
+ if (!ctx) {
1560
+ throw new Error(CANVAS_CTX_FAILED);
1561
+ }
1562
+ ctx.imageSmoothingEnabled = false;
1563
+ result.canvas = canvas;
1564
+ result.ctx = ctx;
1565
+ return result;
1541
1566
  }
1542
1567
  if (canvas.width !== width || canvas.height !== height) {
1543
1568
  canvas.width = width;
1544
1569
  canvas.height = height;
1545
1570
  ctx.imageSmoothingEnabled = false;
1546
1571
  } else {
1572
+ ctx.setTransform(1, 0, 0, 1, 0, 0);
1547
1573
  ctx.clearRect(0, 0, width, height);
1548
1574
  }
1549
- return {
1550
- canvas,
1551
- ctx
1552
- };
1575
+ return result;
1553
1576
  }
1554
1577
  get2.reset = () => {
1555
1578
  canvas = null;
1556
1579
  ctx = null;
1580
+ result.canvas = null;
1581
+ result.ctx = null;
1557
1582
  };
1558
1583
  return get2;
1559
1584
  }
1560
1585
 
1586
+ // src/Canvas/CanvasFrameRenderer.ts
1587
+ var defaults = {
1588
+ makeReusableCanvas
1589
+ };
1590
+ function makeCanvasFrameRenderer(deps = defaults) {
1591
+ const {
1592
+ makeReusableCanvas: makeReusableCanvas2 = defaults.makeReusableCanvas
1593
+ } = deps;
1594
+ const bufferCanvas = makeReusableCanvas2();
1595
+ return function renderCanvasFrame(pixelCanvas, scale, getImageData, drawPixelLayer, drawScreenLayer) {
1596
+ const {
1597
+ canvas,
1598
+ ctx
1599
+ } = pixelCanvas;
1600
+ const {
1601
+ ctx: pxCtx,
1602
+ canvas: pxCanvas
1603
+ } = bufferCanvas(canvas.width, canvas.height);
1604
+ const img = getImageData();
1605
+ if (img) {
1606
+ pxCtx.putImageData(img, 0, 0);
1607
+ }
1608
+ drawPixelLayer?.(pxCtx);
1609
+ ctx.setTransform(1, 0, 0, 1, 0, 0);
1610
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
1611
+ ctx.setTransform(scale, 0, 0, scale, 0, 0);
1612
+ ctx.drawImage(pxCanvas, 0, 0);
1613
+ ctx.setTransform(1, 0, 0, 1, 0, 0);
1614
+ drawScreenLayer?.(ctx, scale);
1615
+ };
1616
+ }
1617
+
1618
+ // src/Canvas/PixelCanvas.ts
1619
+ function makePixelCanvas(canvas) {
1620
+ const ctx = canvas.getContext("2d");
1621
+ if (!ctx) throw new Error(CANVAS_CTX_FAILED);
1622
+ ctx.imageSmoothingEnabled = false;
1623
+ return {
1624
+ canvas,
1625
+ ctx,
1626
+ resize(w, h) {
1627
+ canvas.width = w;
1628
+ canvas.height = h;
1629
+ ctx.imageSmoothingEnabled = false;
1630
+ }
1631
+ };
1632
+ }
1633
+
1561
1634
  // src/ImageData/imgBlobToImageData.ts
1562
1635
  async function imgBlobToImageData(blob) {
1563
1636
  let bitmap = null;
@@ -1617,6 +1690,50 @@ async function writeImageDataToClipboard(imageData) {
1617
1690
  return writeImgBlobToClipboard(blob);
1618
1691
  }
1619
1692
 
1693
+ // src/History/PixelPatchTiles.ts
1694
+ function applyPatchTiles(target, tiles, tileSize) {
1695
+ for (let i = 0; i < tiles.length; i++) {
1696
+ const tile = tiles[i];
1697
+ if (!tile) continue;
1698
+ const dst = target.data32;
1699
+ const src = tile.data32;
1700
+ const dstWidth = target.width;
1701
+ const dstHeight = target.height;
1702
+ const startX = tile.tx * tileSize;
1703
+ const startY = tile.ty * tileSize;
1704
+ const copyWidth = Math.max(0, Math.min(tileSize, dstWidth - startX));
1705
+ if (copyWidth <= 0) continue;
1706
+ for (let ly = 0; ly < tileSize; ly++) {
1707
+ const globalY = startY + ly;
1708
+ if (globalY >= dstHeight) break;
1709
+ const dstIndex = globalY * dstWidth + startX;
1710
+ const srcIndex = ly * tileSize;
1711
+ const rowData = src.subarray(srcIndex, srcIndex + copyWidth);
1712
+ dst.set(rowData, dstIndex);
1713
+ }
1714
+ }
1715
+ }
1716
+
1717
+ // src/History/HistoryAction.ts
1718
+ function makeHistoryAction(writer, patch, after, afterUndo, afterRedo, applyPatchTilesFn = applyPatchTiles) {
1719
+ const target = writer.config.target;
1720
+ const tileSize = writer.config.tileSize;
1721
+ const accumulator = writer.accumulator;
1722
+ return {
1723
+ undo: () => {
1724
+ applyPatchTilesFn(target, patch.beforeTiles, tileSize);
1725
+ afterUndo?.();
1726
+ after?.();
1727
+ },
1728
+ redo: () => {
1729
+ applyPatchTilesFn(target, patch.afterTiles, tileSize);
1730
+ afterRedo?.();
1731
+ after?.();
1732
+ },
1733
+ dispose: () => accumulator.recyclePatch(patch)
1734
+ };
1735
+ }
1736
+
1620
1737
  // src/History/HistoryManager.ts
1621
1738
  var HistoryManager = class {
1622
1739
  constructor(maxSteps = 50) {
@@ -1675,126 +1792,91 @@ var HistoryManager = class {
1675
1792
  }
1676
1793
  };
1677
1794
 
1678
- // src/History/PixelPatchTiles.ts
1679
- var PixelTile = class {
1680
- constructor(id, tx, ty, tileArea) {
1681
- this.id = id;
1682
- this.tx = tx;
1683
- this.ty = ty;
1684
- this.data32 = new Uint32Array(tileArea);
1685
- }
1686
- data32;
1687
- };
1688
- function applyPatchTiles(target, tiles, tileSize = 256) {
1689
- for (let i = 0; i < tiles.length; i++) {
1690
- const tile = tiles[i];
1691
- if (!tile) continue;
1692
- const dst = target.data32;
1693
- const src = tile.data32;
1694
- const dstWidth = target.width;
1695
- const dstHeight = target.height;
1696
- const startX = tile.tx * tileSize;
1697
- const startY = tile.ty * tileSize;
1698
- const copyWidth = Math.max(0, Math.min(tileSize, dstWidth - startX));
1699
- if (copyWidth <= 0) return;
1700
- for (let ly = 0; ly < tileSize; ly++) {
1701
- const globalY = startY + ly;
1702
- if (globalY >= dstHeight) break;
1703
- const dstIndex = globalY * dstWidth + startX;
1704
- const srcIndex = ly * tileSize;
1705
- const rowData = src.subarray(srcIndex, srcIndex + copyWidth);
1706
- dst.set(rowData, dstIndex);
1707
- }
1708
- }
1709
- }
1710
-
1711
1795
  // src/History/PixelAccumulator.ts
1712
1796
  var PixelAccumulator = class {
1713
- constructor(target, config) {
1714
- this.target = target;
1797
+ constructor(config, tilePool) {
1715
1798
  this.config = config;
1799
+ this.tilePool = tilePool;
1716
1800
  this.lookup = [];
1717
1801
  this.beforeTiles = [];
1718
- this.pool = [];
1719
1802
  }
1720
1803
  lookup;
1721
1804
  beforeTiles;
1722
- pool;
1723
- getTile(id, tx, ty) {
1724
- let tile = this.pool.pop();
1725
- if (tile) {
1726
- tile.id = id;
1727
- tile.tx = tx;
1728
- tile.ty = ty;
1729
- return tile;
1730
- }
1731
- return new PixelTile(id, tx, ty, this.config.tileArea);
1732
- }
1733
1805
  recyclePatch(patch) {
1734
- const before = patch.beforeTiles;
1735
- for (let i = 0; i < before.length; i++) {
1736
- let tile = before[i];
1737
- if (tile) {
1738
- this.pool.push(tile);
1739
- }
1740
- }
1741
- const after = patch.afterTiles;
1742
- for (let i = 0; i < after.length; i++) {
1743
- let tile = after[i];
1744
- if (tile) {
1745
- this.pool.push(tile);
1746
- }
1747
- }
1806
+ this.tilePool.releaseTiles(patch.beforeTiles);
1807
+ this.tilePool.releaseTiles(patch.afterTiles);
1748
1808
  }
1749
1809
  /**
1750
1810
  * @param x pixel x coordinate
1751
1811
  * @param y pixel y coordinate
1752
1812
  */
1753
- storeTileBeforeState(x, y) {
1754
- let target = this.target;
1813
+ storePixelBeforeState(x, y) {
1755
1814
  let shift = this.config.tileShift;
1756
- let columns = target.width + this.config.tileMask >> shift;
1815
+ let columns = this.config.targetColumns;
1757
1816
  let tx = x >> shift;
1758
1817
  let ty = y >> shift;
1759
1818
  let id = ty * columns + tx;
1760
1819
  let tile = this.lookup[id];
1820
+ let added = false;
1761
1821
  if (!tile) {
1762
- tile = this.getTile(id, tx, ty);
1822
+ tile = this.tilePool.getTile(id, tx, ty);
1763
1823
  this.extractState(tile);
1764
1824
  this.lookup[id] = tile;
1765
1825
  this.beforeTiles.push(tile);
1826
+ added = true;
1766
1827
  }
1828
+ return (didChange) => {
1829
+ if (!didChange && added) {
1830
+ this.beforeTiles.pop();
1831
+ this.lookup[id] = void 0;
1832
+ this.tilePool.releaseTile(tile);
1833
+ }
1834
+ return didChange;
1835
+ };
1767
1836
  }
1768
1837
  /**
1769
- *
1770
1838
  * @param x pixel x coordinate
1771
1839
  * @param y pixel y coordinate
1772
1840
  * @param w pixel width
1773
1841
  * @param h pixel height
1774
1842
  */
1775
1843
  storeRegionBeforeState(x, y, w, h) {
1776
- let target = this.target;
1777
1844
  let shift = this.config.tileShift;
1778
- let columns = target.width + this.config.tileMask >> shift;
1845
+ let columns = this.config.targetColumns;
1779
1846
  let startX = x >> shift;
1780
1847
  let startY = y >> shift;
1781
1848
  let endX = x + w - 1 >> shift;
1782
1849
  let endY = y + h - 1 >> shift;
1850
+ let startIndex = this.beforeTiles.length;
1783
1851
  for (let ty = startY; ty <= endY; ty++) {
1784
1852
  for (let tx = startX; tx <= endX; tx++) {
1785
1853
  let id = ty * columns + tx;
1786
1854
  let tile = this.lookup[id];
1787
1855
  if (!tile) {
1788
- tile = this.getTile(id, tx, ty);
1856
+ tile = this.tilePool.getTile(id, tx, ty);
1789
1857
  this.extractState(tile);
1790
1858
  this.lookup[id] = tile;
1791
1859
  this.beforeTiles.push(tile);
1792
1860
  }
1793
1861
  }
1794
1862
  }
1863
+ return (didChange) => {
1864
+ if (!didChange) {
1865
+ let length = this.beforeTiles.length;
1866
+ for (let i = startIndex; i < length; i++) {
1867
+ let t = this.beforeTiles[i];
1868
+ if (t) {
1869
+ this.lookup[t.id] = void 0;
1870
+ this.tilePool.releaseTile(t);
1871
+ }
1872
+ }
1873
+ this.beforeTiles.length = startIndex;
1874
+ }
1875
+ return didChange;
1876
+ };
1795
1877
  }
1796
1878
  extractState(tile) {
1797
- let target = this.target;
1879
+ let target = this.config.target;
1798
1880
  let TILE_SIZE = this.config.tileSize;
1799
1881
  let dst = tile.data32;
1800
1882
  let src = target.data32;
@@ -1802,7 +1884,12 @@ var PixelAccumulator = class {
1802
1884
  let startY = tile.ty * TILE_SIZE;
1803
1885
  let targetWidth = target.width;
1804
1886
  let targetHeight = target.height;
1805
- let copyWidth = Math.max(0, Math.min(TILE_SIZE, targetWidth - startX));
1887
+ if (startX >= targetWidth || startX + TILE_SIZE <= 0 || startY >= targetHeight || startY + TILE_SIZE <= 0) {
1888
+ dst.fill(0);
1889
+ return;
1890
+ }
1891
+ let srcOffsetX = Math.max(0, -startX);
1892
+ let copyWidth = Math.max(0, Math.min(TILE_SIZE - srcOffsetX, targetWidth - Math.max(0, startX)));
1806
1893
  for (let ly = 0; ly < TILE_SIZE; ly++) {
1807
1894
  let globalY = startY + ly;
1808
1895
  let dstIndex = ly * TILE_SIZE;
@@ -1810,47 +1897,77 @@ var PixelAccumulator = class {
1810
1897
  dst.fill(0, dstIndex, dstIndex + TILE_SIZE);
1811
1898
  continue;
1812
1899
  }
1813
- let srcIndex = globalY * targetWidth + startX;
1900
+ let srcIndex = globalY * targetWidth + Math.max(0, startX);
1814
1901
  let rowData = src.subarray(srcIndex, srcIndex + copyWidth);
1815
- dst.set(rowData, dstIndex);
1816
- if (copyWidth < TILE_SIZE) {
1817
- dst.fill(0, dstIndex + copyWidth, dstIndex + TILE_SIZE);
1902
+ dst.set(rowData, dstIndex + srcOffsetX);
1903
+ if (srcOffsetX > 0) {
1904
+ dst.fill(0, dstIndex, dstIndex + srcOffsetX);
1905
+ }
1906
+ if (srcOffsetX + copyWidth < TILE_SIZE) {
1907
+ dst.fill(0, dstIndex + srcOffsetX + copyWidth, dstIndex + TILE_SIZE);
1818
1908
  }
1819
1909
  }
1820
1910
  }
1821
- extractAfterTiles() {
1911
+ extractPatch() {
1822
1912
  let afterTiles = [];
1823
1913
  let length = this.beforeTiles.length;
1824
1914
  for (let i = 0; i < length; i++) {
1825
1915
  let beforeTile = this.beforeTiles[i];
1826
1916
  if (beforeTile) {
1827
- let afterTile = this.getTile(beforeTile.id, beforeTile.tx, beforeTile.ty);
1917
+ let afterTile = this.tilePool.getTile(beforeTile.id, beforeTile.tx, beforeTile.ty);
1828
1918
  this.extractState(afterTile);
1829
1919
  afterTiles.push(afterTile);
1830
1920
  }
1831
1921
  }
1832
- return afterTiles;
1833
- }
1834
- reset() {
1835
- this.lookup = [];
1922
+ let beforeTiles = this.beforeTiles;
1836
1923
  this.beforeTiles = [];
1924
+ this.lookup.length = 0;
1925
+ return {
1926
+ beforeTiles,
1927
+ afterTiles
1928
+ };
1929
+ }
1930
+ rollback() {
1931
+ let target = this.config.target;
1932
+ let tileSize = this.config.tileSize;
1933
+ let length = this.beforeTiles.length;
1934
+ applyPatchTiles(target, this.beforeTiles, tileSize);
1935
+ for (let i = 0; i < length; i++) {
1936
+ let tile = this.beforeTiles[i];
1937
+ if (tile) {
1938
+ this.lookup[tile.id] = void 0;
1939
+ this.tilePool.releaseTile(tile);
1940
+ }
1941
+ }
1942
+ this.beforeTiles.length = 0;
1943
+ this.lookup.length = 0;
1837
1944
  }
1838
1945
  };
1839
1946
 
1840
1947
  // src/History/PixelEngineConfig.ts
1841
1948
  var PixelEngineConfig = class {
1842
1949
  tileSize;
1950
+ // pixelX = tileX << tileShift
1951
+ // pixelY = tileY << tileShift
1843
1952
  tileShift;
1844
1953
  tileMask;
1845
1954
  tileArea;
1846
- constructor(tileSize = 256) {
1955
+ target;
1956
+ targetColumns = 0;
1957
+ constructor(tileSize, target) {
1847
1958
  if ((tileSize & tileSize - 1) !== 0) {
1848
1959
  throw new Error("tileSize must be a power of 2");
1849
1960
  }
1850
1961
  this.tileSize = tileSize;
1851
- this.tileShift = Math.log2(tileSize);
1962
+ this.tileShift = 31 - Math.clz32(tileSize);
1852
1963
  this.tileMask = tileSize - 1;
1853
1964
  this.tileArea = tileSize * tileSize;
1965
+ this.setTarget(target);
1966
+ }
1967
+ setTarget(target) {
1968
+ ;
1969
+ this.target = target;
1970
+ this.targetColumns = target.width + this.tileMask >> this.tileShift;
1854
1971
  }
1855
1972
  };
1856
1973
 
@@ -1866,7 +1983,7 @@ function applyAlphaMaskToPixelData(dst, mask, opts = {}) {
1866
1983
  my = 0,
1867
1984
  invertMask = false
1868
1985
  } = opts;
1869
- if (globalAlpha === 0) return;
1986
+ if (globalAlpha === 0) return false;
1870
1987
  let x = targetX;
1871
1988
  let y = targetY;
1872
1989
  let w = width;
@@ -1881,10 +1998,10 @@ function applyAlphaMaskToPixelData(dst, mask, opts = {}) {
1881
1998
  }
1882
1999
  w = Math.min(w, dst.width - x);
1883
2000
  h = Math.min(h, dst.height - y);
1884
- if (w <= 0) return;
1885
- if (h <= 0) return;
2001
+ if (w <= 0) return false;
2002
+ if (h <= 0) return false;
1886
2003
  const mPitch = mask.w;
1887
- if (mPitch <= 0) return;
2004
+ if (mPitch <= 0) return false;
1888
2005
  const startX = mx + (x - targetX);
1889
2006
  const startY = my + (y - targetY);
1890
2007
  const sX0 = Math.max(0, startX);
@@ -1893,8 +2010,8 @@ function applyAlphaMaskToPixelData(dst, mask, opts = {}) {
1893
2010
  const sY1 = Math.min(mask.h, startY + h);
1894
2011
  const finalW = sX1 - sX0;
1895
2012
  const finalH = sY1 - sY0;
1896
- if (finalW <= 0) return;
1897
- if (finalH <= 0) return;
2013
+ if (finalW <= 0) return false;
2014
+ if (finalH <= 0) return false;
1898
2015
  const xShift = sX0 - startX;
1899
2016
  const yShift = sY0 - startY;
1900
2017
  const dst32 = dst.data32;
@@ -1904,6 +2021,7 @@ function applyAlphaMaskToPixelData(dst, mask, opts = {}) {
1904
2021
  const maskData = mask.data;
1905
2022
  let dIdx = (y + yShift) * dw + (x + xShift);
1906
2023
  let mIdx = sY0 * mPitch + sX0;
2024
+ let didChange = false;
1907
2025
  for (let iy = 0; iy < h; iy++) {
1908
2026
  for (let ix = 0; ix < w; ix++) {
1909
2027
  const mVal = maskData[mIdx];
@@ -1920,12 +2038,18 @@ function applyAlphaMaskToPixelData(dst, mask, opts = {}) {
1920
2038
  }
1921
2039
  if (weight === 0) {
1922
2040
  dst32[dIdx] = (dst32[dIdx] & 16777215) >>> 0;
2041
+ didChange = true;
1923
2042
  } else if (weight !== 255) {
1924
2043
  const d = dst32[dIdx];
1925
2044
  const da = d >>> 24;
1926
2045
  if (da !== 0) {
1927
2046
  const finalAlpha = da === 255 ? weight : da * weight + 128 >> 8;
1928
- dst32[dIdx] = (d & 16777215 | finalAlpha << 24) >>> 0;
2047
+ const current = dst32[dIdx];
2048
+ const next = (d & 16777215 | finalAlpha << 24) >>> 0;
2049
+ if (current !== next) {
2050
+ dst32[dIdx] = next;
2051
+ didChange = true;
2052
+ }
1929
2053
  }
1930
2054
  }
1931
2055
  dIdx++;
@@ -1934,27 +2058,28 @@ function applyAlphaMaskToPixelData(dst, mask, opts = {}) {
1934
2058
  dIdx += dStride;
1935
2059
  mIdx += mStride;
1936
2060
  }
2061
+ return didChange;
1937
2062
  }
1938
2063
 
1939
2064
  // src/History/PixelMutator/mutatorApplyAlphaMask.ts
1940
- var defaults = {
2065
+ var defaults2 = {
1941
2066
  applyAlphaMaskToPixelData
1942
2067
  };
1943
- var mutatorApplyAlphaMask = ((writer, deps = defaults) => {
2068
+ var mutatorApplyAlphaMask = ((writer, deps = defaults2) => {
1944
2069
  const {
1945
- applyAlphaMaskToPixelData: applyAlphaMaskToPixelData2 = defaults.applyAlphaMaskToPixelData
2070
+ applyAlphaMaskToPixelData: applyAlphaMaskToPixelData2 = defaults2.applyAlphaMaskToPixelData
1946
2071
  } = deps;
1947
2072
  return {
1948
- applyAlphaMask: (mask, opts = {}) => {
1949
- let target = writer.target;
2073
+ applyAlphaMask(mask, opts = {}) {
2074
+ let target = writer.config.target;
1950
2075
  const {
1951
2076
  x = 0,
1952
2077
  y = 0,
1953
- w = writer.target.width,
1954
- h = writer.target.height
2078
+ w = target.width,
2079
+ h = target.height
1955
2080
  } = opts;
1956
- writer.accumulator.storeRegionBeforeState(x, y, w, h);
1957
- applyAlphaMaskToPixelData2(target, mask, opts);
2081
+ const didChange = writer.accumulator.storeRegionBeforeState(x, y, w, h);
2082
+ return didChange(applyAlphaMaskToPixelData2(target, mask, opts));
1958
2083
  }
1959
2084
  };
1960
2085
  });
@@ -1971,7 +2096,7 @@ function applyBinaryMaskToPixelData(dst, mask, opts = {}) {
1971
2096
  my = 0,
1972
2097
  invertMask = false
1973
2098
  } = opts;
1974
- if (globalAlpha === 0) return;
2099
+ if (globalAlpha === 0) return false;
1975
2100
  let x = targetX;
1976
2101
  let y = targetY;
1977
2102
  let w = width;
@@ -1986,10 +2111,9 @@ function applyBinaryMaskToPixelData(dst, mask, opts = {}) {
1986
2111
  }
1987
2112
  w = Math.min(w, dst.width - x);
1988
2113
  h = Math.min(h, dst.height - y);
1989
- if (w <= 0) return;
1990
- if (h <= 0) return;
2114
+ if (w <= 0 || h <= 0) return false;
1991
2115
  const mPitch = mask.w;
1992
- if (mPitch <= 0) return;
2116
+ if (mPitch <= 0) return false;
1993
2117
  const startX = mx + (x - targetX);
1994
2118
  const startY = my + (y - targetY);
1995
2119
  const sX0 = Math.max(0, startX);
@@ -1998,8 +2122,9 @@ function applyBinaryMaskToPixelData(dst, mask, opts = {}) {
1998
2122
  const sY1 = Math.min(mask.h, startY + h);
1999
2123
  const finalW = sX1 - sX0;
2000
2124
  const finalH = sY1 - sY0;
2001
- if (finalW <= 0) return;
2002
- if (finalH <= 0) return;
2125
+ if (finalW <= 0 || finalH <= 0) {
2126
+ return false;
2127
+ }
2003
2128
  const xShift = sX0 - startX;
2004
2129
  const yShift = sY0 - startY;
2005
2130
  const dst32 = dst.data32;
@@ -2009,18 +2134,28 @@ function applyBinaryMaskToPixelData(dst, mask, opts = {}) {
2009
2134
  const maskData = mask.data;
2010
2135
  let dIdx = (y + yShift) * dw + (x + xShift);
2011
2136
  let mIdx = sY0 * mPitch + sX0;
2012
- for (let iy = 0; iy < h; iy++) {
2013
- for (let ix = 0; ix < w; ix++) {
2137
+ let didChange = false;
2138
+ for (let iy = 0; iy < finalH; iy++) {
2139
+ for (let ix = 0; ix < finalW; ix++) {
2014
2140
  const mVal = maskData[mIdx];
2015
2141
  const isMaskedOut = invertMask ? mVal !== 0 : mVal === 0;
2016
2142
  if (isMaskedOut) {
2017
- dst32[dIdx] = (dst32[dIdx] & 16777215) >>> 0;
2143
+ const current = dst32[dIdx];
2144
+ const next = (current & 16777215) >>> 0;
2145
+ if (current !== next) {
2146
+ dst32[dIdx] = next;
2147
+ didChange = true;
2148
+ }
2018
2149
  } else if (globalAlpha !== 255) {
2019
2150
  const d = dst32[dIdx];
2020
2151
  const da = d >>> 24;
2021
2152
  if (da !== 0) {
2022
2153
  const finalAlpha = da === 255 ? globalAlpha : da * globalAlpha + 128 >> 8;
2023
- dst32[dIdx] = (d & 16777215 | finalAlpha << 24) >>> 0;
2154
+ const next = (d & 16777215 | finalAlpha << 24) >>> 0;
2155
+ if (d !== next) {
2156
+ dst32[dIdx] = next;
2157
+ didChange = true;
2158
+ }
2024
2159
  }
2025
2160
  }
2026
2161
  dIdx++;
@@ -2029,59 +2164,32 @@ function applyBinaryMaskToPixelData(dst, mask, opts = {}) {
2029
2164
  dIdx += dStride;
2030
2165
  mIdx += mStride;
2031
2166
  }
2167
+ return didChange;
2032
2168
  }
2033
2169
 
2034
2170
  // src/History/PixelMutator/mutatorApplyBinaryMask.ts
2035
- var defaults2 = {
2171
+ var defaults3 = {
2036
2172
  applyBinaryMaskToPixelData
2037
2173
  };
2038
- var mutatorApplyBinaryMask = ((writer, deps = defaults2) => {
2174
+ var mutatorApplyBinaryMask = ((writer, deps = defaults3) => {
2039
2175
  const {
2040
- applyBinaryMaskToPixelData: applyBinaryMaskToPixelData2 = defaults2.applyBinaryMaskToPixelData
2176
+ applyBinaryMaskToPixelData: applyBinaryMaskToPixelData2 = defaults3.applyBinaryMaskToPixelData
2041
2177
  } = deps;
2042
2178
  return {
2043
- applyBinaryMask: (mask, opts = {}) => {
2044
- let target = writer.target;
2179
+ applyBinaryMask(mask, opts = {}) {
2180
+ let target = writer.config.target;
2045
2181
  const {
2046
2182
  x = 0,
2047
2183
  y = 0,
2048
- w = writer.target.width,
2049
- h = writer.target.height
2184
+ w = target.width,
2185
+ h = target.height
2050
2186
  } = opts;
2051
- writer.accumulator.storeRegionBeforeState(x, y, w, h);
2052
- applyBinaryMaskToPixelData2(target, mask, opts);
2187
+ const didChange = writer.accumulator.storeRegionBeforeState(x, y, w, h);
2188
+ return didChange(applyBinaryMaskToPixelData2(target, mask, opts));
2053
2189
  }
2054
2190
  };
2055
2191
  });
2056
2192
 
2057
- // src/Rect/getCircleBrushOrPencilBounds.ts
2058
- function getCircleBrushOrPencilBounds(centerX, centerY, brushSize, targetWidth, targetHeight, out) {
2059
- const r = brushSize / 2;
2060
- const minOffset = -Math.ceil(r - 0.5);
2061
- const maxOffset = Math.floor(r - 0.5);
2062
- const startX = Math.floor(centerX + minOffset);
2063
- const startY = Math.floor(centerY + minOffset);
2064
- const endX = Math.floor(centerX + maxOffset) + 1;
2065
- const endY = Math.floor(centerY + maxOffset) + 1;
2066
- const res = out ?? {
2067
- x: 0,
2068
- y: 0,
2069
- w: 0,
2070
- h: 0
2071
- };
2072
- const cStartX = Math.max(0, startX);
2073
- const cStartY = Math.max(0, startY);
2074
- const cEndX = Math.min(targetWidth, endX);
2075
- const cEndY = Math.min(targetHeight, endY);
2076
- const w = cEndX - cStartX;
2077
- const h = cEndY - cStartY;
2078
- res.x = cStartX;
2079
- res.y = cStartY;
2080
- res.w = w < 0 ? 0 : w;
2081
- res.h = h < 0 ? 0 : h;
2082
- return res;
2083
- }
2084
-
2085
2193
  // src/PixelData/blendColorPixelDataAlphaMask.ts
2086
2194
  function blendColorPixelDataAlphaMask(dst, color, mask, opts = {}) {
2087
2195
  const targetX = opts.x ?? 0;
@@ -2093,10 +2201,10 @@ function blendColorPixelDataAlphaMask(dst, color, mask, opts = {}) {
2093
2201
  const mx = opts.mx ?? 0;
2094
2202
  const my = opts.my ?? 0;
2095
2203
  const invertMask = opts.invertMask ?? false;
2096
- if (globalAlpha === 0) return;
2204
+ if (globalAlpha === 0) return false;
2097
2205
  const baseSrcAlpha = color >>> 24;
2098
2206
  const isOverwrite = blendFn.isOverwrite || false;
2099
- if (baseSrcAlpha === 0 && !isOverwrite) return;
2207
+ if (baseSrcAlpha === 0 && !isOverwrite) return false;
2100
2208
  let x = targetX;
2101
2209
  let y = targetY;
2102
2210
  let actualW = w;
@@ -2111,7 +2219,7 @@ function blendColorPixelDataAlphaMask(dst, color, mask, opts = {}) {
2111
2219
  }
2112
2220
  actualW = Math.min(actualW, dst.width - x);
2113
2221
  actualH = Math.min(actualH, dst.height - y);
2114
- if (actualW <= 0 || actualH <= 0) return;
2222
+ if (actualW <= 0 || actualH <= 0) return false;
2115
2223
  const dx = x - targetX | 0;
2116
2224
  const dy = y - targetY | 0;
2117
2225
  const dst32 = dst.data32;
@@ -2121,9 +2229,10 @@ function blendColorPixelDataAlphaMask(dst, color, mask, opts = {}) {
2121
2229
  let dIdx = y * dw + x | 0;
2122
2230
  let mIdx = (my + dy) * mPitch + (mx + dx) | 0;
2123
2231
  const dStride = dw - actualW | 0;
2124
- let mStride = mPitch - actualW | 0;
2232
+ const mStride = mPitch - actualW | 0;
2125
2233
  const isOpaque = globalAlpha === 255;
2126
2234
  const colorRGB = color & 16777215;
2235
+ let didChange = false;
2127
2236
  for (let iy = 0; iy < actualH; iy++) {
2128
2237
  for (let ix = 0; ix < actualW; ix++) {
2129
2238
  const mVal = maskData[mIdx];
@@ -2154,162 +2263,47 @@ function blendColorPixelDataAlphaMask(dst, color, mask, opts = {}) {
2154
2263
  }
2155
2264
  finalCol = (colorRGB | a << 24) >>> 0;
2156
2265
  }
2157
- dst32[dIdx] = blendFn(finalCol, dst32[dIdx]);
2266
+ const current = dst32[dIdx];
2267
+ const next = blendFn(finalCol, current);
2268
+ if (current !== next) {
2269
+ dst32[dIdx] = next;
2270
+ didChange = true;
2271
+ }
2158
2272
  dIdx++;
2159
2273
  mIdx++;
2160
2274
  }
2161
2275
  dIdx += dStride;
2162
2276
  mIdx += mStride;
2163
2277
  }
2278
+ return didChange;
2164
2279
  }
2165
2280
 
2166
- // src/PixelData/blendColorPixelDataBinaryMask.ts
2167
- function blendColorPixelDataBinaryMask(dst, color, mask, opts = {}) {
2168
- const targetX = opts.x ?? 0;
2169
- const targetY = opts.y ?? 0;
2170
- let w = opts.w ?? mask.w;
2171
- let h = opts.h ?? mask.h;
2172
- const globalAlpha = opts.alpha ?? 255;
2173
- const blendFn = opts.blendFn ?? sourceOverPerfect;
2174
- const mx = opts.mx ?? 0;
2175
- const my = opts.my ?? 0;
2176
- const invertMask = opts.invertMask ?? false;
2177
- if (globalAlpha === 0) return;
2178
- const baseSrcAlpha = color >>> 24;
2179
- const isOverwrite = blendFn.isOverwrite || false;
2180
- if (baseSrcAlpha === 0 && !isOverwrite) return;
2181
- let x = targetX;
2182
- let y = targetY;
2183
- if (x < 0) {
2184
- w += x;
2185
- x = 0;
2186
- }
2187
- if (y < 0) {
2188
- h += y;
2189
- y = 0;
2190
- }
2191
- const actualW = Math.min(w, dst.width - x);
2192
- const actualH = Math.min(h, dst.height - y);
2193
- if (actualW <= 0 || actualH <= 0) return;
2194
- let baseColorWithGlobalAlpha = color;
2195
- if (globalAlpha < 255) {
2196
- const a = baseSrcAlpha * globalAlpha + 128 >> 8;
2197
- if (a === 0 && !isOverwrite) return;
2198
- baseColorWithGlobalAlpha = (color & 16777215 | a << 24) >>> 0;
2199
- }
2200
- const dx = x - targetX | 0;
2201
- const dy = y - targetY | 0;
2202
- const dst32 = dst.data32;
2203
- const dw = dst.width;
2204
- const mPitch = mask.w;
2205
- const maskData = mask.data;
2206
- let dIdx = y * dw + x | 0;
2207
- let mIdx = (my + dy) * mPitch + (mx + dx) | 0;
2208
- const dStride = dw - actualW | 0;
2209
- const mStride = mPitch - actualW | 0;
2210
- const skipVal = invertMask ? 1 : 0;
2211
- for (let iy = 0; iy < actualH; iy++) {
2212
- for (let ix = 0; ix < actualW; ix++) {
2213
- if (maskData[mIdx] === skipVal) {
2214
- dIdx++;
2215
- mIdx++;
2216
- continue;
2217
- }
2218
- dst32[dIdx] = blendFn(baseColorWithGlobalAlpha, dst32[dIdx]);
2219
- dIdx++;
2220
- mIdx++;
2221
- }
2222
- dIdx += dStride;
2223
- mIdx += mStride;
2224
- }
2225
- }
2226
-
2227
- // src/PixelData/applyCircleBrushToPixelData.ts
2228
- function applyCircleBrushToPixelData(target, color, centerX, centerY, brush, alpha = 255, blendFn = sourceOverPerfect, scratchOptions = {}, bounds) {
2229
- const b = bounds ?? getCircleBrushOrPencilBounds(centerX, centerY, brush.size, target.width, target.height);
2230
- if (b.w <= 0 || b.h <= 0) return;
2231
- const unclippedStartX = Math.floor(centerX + brush.minOffset);
2232
- const unclippedStartY = Math.floor(centerY + brush.minOffset);
2233
- const ix = Math.max(unclippedStartX, b.x);
2234
- const iy = Math.max(unclippedStartY, b.y);
2235
- const ir = Math.min(unclippedStartX + brush.w, b.x + b.w);
2236
- const ib = Math.min(unclippedStartY + brush.h, b.y + b.h);
2237
- const iw = ir - ix;
2238
- const ih = ib - iy;
2239
- if (iw <= 0 || ih <= 0) return;
2240
- scratchOptions.x = ix;
2241
- scratchOptions.y = iy;
2242
- scratchOptions.w = iw;
2243
- scratchOptions.h = ih;
2244
- scratchOptions.mx = ix - unclippedStartX;
2245
- scratchOptions.my = iy - unclippedStartY;
2246
- scratchOptions.alpha = alpha;
2247
- scratchOptions.blendFn = blendFn;
2248
- if (brush.type === 0 /* ALPHA */) {
2249
- blendColorPixelDataAlphaMask(target, color, brush, scratchOptions);
2250
- }
2251
- if (brush.type === 1 /* BINARY */) {
2252
- blendColorPixelDataBinaryMask(target, color, brush, scratchOptions);
2253
- }
2254
- }
2255
-
2256
- // src/History/PixelMutator/mutatorApplyCircleBrush.ts
2257
- var defaults3 = {
2258
- applyCircleBrushToPixelData,
2259
- getCircleBrushOrPencilBounds
2260
- };
2261
- var mutatorApplyCircleBrush = ((writer, deps = defaults3) => {
2262
- const {
2263
- applyCircleBrushToPixelData: applyCircleBrushToPixelData2 = defaults3.applyCircleBrushToPixelData,
2264
- getCircleBrushOrPencilBounds: getCircleBrushOrPencilBounds2 = defaults3.getCircleBrushOrPencilBounds
2265
- } = deps;
2266
- const boundsOut = {
2267
- x: 0,
2268
- y: 0,
2269
- w: 0,
2270
- h: 0
2271
- };
2272
- const blendColorPixelOptions = {
2273
- alpha: 255,
2274
- blendFn: sourceOverPerfect,
2281
+ // src/Rect/getCircleBrushOrPencilBounds.ts
2282
+ function getCircleBrushOrPencilBounds(centerX, centerY, brushSize, targetWidth, targetHeight, out) {
2283
+ const r = brushSize / 2;
2284
+ const minOffset = -Math.ceil(r - 0.5);
2285
+ const maxOffset = Math.floor(r - 0.5);
2286
+ const startX = Math.floor(centerX + minOffset);
2287
+ const startY = Math.floor(centerY + minOffset);
2288
+ const endX = Math.floor(centerX + maxOffset) + 1;
2289
+ const endY = Math.floor(centerY + maxOffset) + 1;
2290
+ const res = out ?? {
2275
2291
  x: 0,
2276
2292
  y: 0,
2277
2293
  w: 0,
2278
2294
  h: 0
2279
2295
  };
2280
- return {
2281
- applyCircleBrush(color, centerX, centerY, brush, alpha = 255, blendFn) {
2282
- const bounds = getCircleBrushOrPencilBounds2(centerX, centerY, brush.size, writer.target.width, writer.target.height, boundsOut);
2283
- const {
2284
- x,
2285
- y,
2286
- w,
2287
- h
2288
- } = bounds;
2289
- writer.accumulator.storeRegionBeforeState(x, y, w, h);
2290
- applyCircleBrushToPixelData2(writer.target, color, centerX, centerY, brush, alpha, blendFn, blendColorPixelOptions, bounds);
2291
- }
2292
- };
2293
- });
2294
-
2295
- // src/Algorithm/forEachLinePoint.ts
2296
- function forEachLinePoint(x0, y0, x1, y1, callback) {
2297
- const dx = x1 - x0;
2298
- const dy = y1 - y0;
2299
- const steps = Math.max(Math.abs(dx), Math.abs(dy));
2300
- if (steps === 0) {
2301
- callback(x0, y0);
2302
- return;
2303
- }
2304
- const xInc = dx / steps;
2305
- const yInc = dy / steps;
2306
- let curX = x0;
2307
- let curY = y0;
2308
- for (let i = 0; i <= steps; i++) {
2309
- callback(curX, curY);
2310
- curX += xInc;
2311
- curY += yInc;
2312
- }
2296
+ const cStartX = Math.max(0, startX);
2297
+ const cStartY = Math.max(0, startY);
2298
+ const cEndX = Math.min(targetWidth, endX);
2299
+ const cEndY = Math.min(targetHeight, endY);
2300
+ const w = cEndX - cStartX;
2301
+ const h = cEndY - cStartY;
2302
+ res.x = cStartX;
2303
+ res.y = cStartY;
2304
+ res.w = w < 0 ? 0 : w;
2305
+ res.h = h < 0 ? 0 : h;
2306
+ return res;
2313
2307
  }
2314
2308
 
2315
2309
  // src/Rect/getCircleBrushOrPencilStrokeBounds.ts
@@ -2382,8 +2376,9 @@ var mutatorApplyCircleBrushStroke = ((writer, deps = defaults4) => {
2382
2376
  const maskData = mask.data;
2383
2377
  const brushData = brush.data;
2384
2378
  const minOffset = brush.minOffset;
2385
- const targetWidth = writer.target.width;
2386
- const targetHeight = writer.target.height;
2379
+ const target = writer.config.target;
2380
+ const targetWidth = target.width;
2381
+ const targetHeight = target.height;
2387
2382
  forEachLinePoint2(x0, y0, x1, y1, (px, py) => {
2388
2383
  const {
2389
2384
  x: cbx,
@@ -2421,19 +2416,117 @@ var mutatorApplyCircleBrushStroke = ((writer, deps = defaults4) => {
2421
2416
  blendColorPixelOptions.y = by;
2422
2417
  blendColorPixelOptions.w = bw;
2423
2418
  blendColorPixelOptions.h = bh;
2424
- blendColorPixelDataAlphaMask2(writer.target, color, mask, blendColorPixelOptions);
2419
+ blendColorPixelDataAlphaMask2(target, color, mask, blendColorPixelOptions);
2425
2420
  }
2426
2421
  };
2427
2422
  });
2428
2423
 
2429
- // src/History/PixelMutator/mutatorApplyCirclePencil.ts
2424
+ // src/PixelData/blendColorPixelDataBinaryMask.ts
2425
+ function blendColorPixelDataBinaryMask(dst, color, mask, opts = {}) {
2426
+ const targetX = opts.x ?? 0;
2427
+ const targetY = opts.y ?? 0;
2428
+ let w = opts.w ?? mask.w;
2429
+ let h = opts.h ?? mask.h;
2430
+ const globalAlpha = opts.alpha ?? 255;
2431
+ const blendFn = opts.blendFn ?? sourceOverPerfect;
2432
+ const mx = opts.mx ?? 0;
2433
+ const my = opts.my ?? 0;
2434
+ const invertMask = opts.invertMask ?? false;
2435
+ if (globalAlpha === 0) return false;
2436
+ const baseSrcAlpha = color >>> 24;
2437
+ const isOverwrite = blendFn.isOverwrite || false;
2438
+ if (baseSrcAlpha === 0 && !isOverwrite) return false;
2439
+ let x = targetX;
2440
+ let y = targetY;
2441
+ if (x < 0) {
2442
+ w += x;
2443
+ x = 0;
2444
+ }
2445
+ if (y < 0) {
2446
+ h += y;
2447
+ y = 0;
2448
+ }
2449
+ const actualW = Math.min(w, dst.width - x);
2450
+ const actualH = Math.min(h, dst.height - y);
2451
+ if (actualW <= 0 || actualH <= 0) return false;
2452
+ let baseColorWithGlobalAlpha = color;
2453
+ if (globalAlpha < 255) {
2454
+ const a = baseSrcAlpha * globalAlpha + 128 >> 8;
2455
+ if (a === 0 && !isOverwrite) return false;
2456
+ baseColorWithGlobalAlpha = (color & 16777215 | a << 24) >>> 0;
2457
+ }
2458
+ const dx = x - targetX | 0;
2459
+ const dy = y - targetY | 0;
2460
+ const dst32 = dst.data32;
2461
+ const dw = dst.width;
2462
+ const mPitch = mask.w;
2463
+ const maskData = mask.data;
2464
+ let dIdx = y * dw + x | 0;
2465
+ let mIdx = (my + dy) * mPitch + (mx + dx) | 0;
2466
+ const dStride = dw - actualW | 0;
2467
+ const mStride = mPitch - actualW | 0;
2468
+ const skipVal = invertMask ? 1 : 0;
2469
+ let didChange = false;
2470
+ for (let iy = 0; iy < actualH; iy++) {
2471
+ for (let ix = 0; ix < actualW; ix++) {
2472
+ if (maskData[mIdx] === skipVal) {
2473
+ dIdx++;
2474
+ mIdx++;
2475
+ continue;
2476
+ }
2477
+ const current = dst32[dIdx];
2478
+ const next = blendFn(baseColorWithGlobalAlpha, current);
2479
+ if (current !== next) {
2480
+ dst32[dIdx] = next;
2481
+ didChange = true;
2482
+ }
2483
+ dIdx++;
2484
+ mIdx++;
2485
+ }
2486
+ dIdx += dStride;
2487
+ mIdx += mStride;
2488
+ }
2489
+ return didChange;
2490
+ }
2491
+
2492
+ // src/PixelData/blendColorPixelDataCircleMask.ts
2493
+ function blendColorPixelDataCircleMask(target, color, centerX, centerY, brush, alpha = 255, blendFn = sourceOverPerfect, scratchOptions = {}, bounds) {
2494
+ const b = bounds ?? getCircleBrushOrPencilBounds(centerX, centerY, brush.size, target.width, target.height);
2495
+ if (b.w <= 0 || b.h <= 0) return false;
2496
+ const unclippedStartX = Math.floor(centerX + brush.minOffset);
2497
+ const unclippedStartY = Math.floor(centerY + brush.minOffset);
2498
+ const ix = Math.max(unclippedStartX, b.x);
2499
+ const iy = Math.max(unclippedStartY, b.y);
2500
+ const ir = Math.min(unclippedStartX + brush.w, b.x + b.w);
2501
+ const ib = Math.min(unclippedStartY + brush.h, b.y + b.h);
2502
+ const iw = ir - ix;
2503
+ const ih = ib - iy;
2504
+ if (iw <= 0 || ih <= 0) return false;
2505
+ scratchOptions.x = ix;
2506
+ scratchOptions.y = iy;
2507
+ scratchOptions.w = iw;
2508
+ scratchOptions.h = ih;
2509
+ scratchOptions.mx = ix - unclippedStartX;
2510
+ scratchOptions.my = iy - unclippedStartY;
2511
+ scratchOptions.alpha = alpha;
2512
+ scratchOptions.blendFn = blendFn;
2513
+ if (brush.type === 0 /* ALPHA */) {
2514
+ return blendColorPixelDataAlphaMask(target, color, brush, scratchOptions);
2515
+ }
2516
+ if (brush.type === 1 /* BINARY */) {
2517
+ return blendColorPixelDataBinaryMask(target, color, brush, scratchOptions);
2518
+ }
2519
+ return false;
2520
+ }
2521
+
2522
+ // src/History/PixelMutator/mutatorBlendColorCircleMask.ts
2430
2523
  var defaults5 = {
2431
- applyCircleBrushToPixelData,
2524
+ blendColorPixelDataCircleMask,
2432
2525
  getCircleBrushOrPencilBounds
2433
2526
  };
2434
- var mutatorApplyCirclePencil = ((writer, deps = defaults5) => {
2527
+ var mutatorBlendColorCircleMask = ((writer, deps = defaults5) => {
2435
2528
  const {
2436
- applyCircleBrushToPixelData: applyCircleBrushToPixelData2 = defaults5.applyCircleBrushToPixelData,
2529
+ blendColorPixelDataCircleMask: blendColorPixelDataCircleMask2 = defaults5.blendColorPixelDataCircleMask,
2437
2530
  getCircleBrushOrPencilBounds: getCircleBrushOrPencilBounds2 = defaults5.getCircleBrushOrPencilBounds
2438
2531
  } = deps;
2439
2532
  const boundsOut = {
@@ -2442,34 +2535,63 @@ var mutatorApplyCirclePencil = ((writer, deps = defaults5) => {
2442
2535
  w: 0,
2443
2536
  h: 0
2444
2537
  };
2538
+ const blendColorPixelOptions = {
2539
+ alpha: 255,
2540
+ blendFn: sourceOverPerfect,
2541
+ x: 0,
2542
+ y: 0,
2543
+ w: 0,
2544
+ h: 0
2545
+ };
2546
+ return {
2547
+ applyCircleMask(color, centerX, centerY, brush, alpha = 255, blendFn) {
2548
+ const target = writer.config.target;
2549
+ const b = getCircleBrushOrPencilBounds2(centerX, centerY, brush.size, target.width, target.height, boundsOut);
2550
+ const didChange = writer.accumulator.storeRegionBeforeState(b.x, b.y, b.w, b.h);
2551
+ return didChange(blendColorPixelDataCircleMask2(target, color, centerX, centerY, brush, alpha, blendFn, blendColorPixelOptions, b));
2552
+ }
2553
+ };
2554
+ });
2555
+
2556
+ // src/History/PixelMutator/mutatorApplyCirclePencil.ts
2557
+ var defaults6 = {
2558
+ applyCircleMaskToPixelData: blendColorPixelDataCircleMask,
2559
+ getCircleBrushOrPencilBounds
2560
+ };
2561
+ var mutatorApplyCirclePencil = ((writer, deps = defaults6) => {
2562
+ const {
2563
+ applyCircleMaskToPixelData = defaults6.applyCircleMaskToPixelData,
2564
+ getCircleBrushOrPencilBounds: getCircleBrushOrPencilBounds2 = defaults6.getCircleBrushOrPencilBounds
2565
+ } = deps;
2566
+ const boundsOut = {
2567
+ x: 0,
2568
+ y: 0,
2569
+ w: 0,
2570
+ h: 0
2571
+ };
2445
2572
  return {
2446
2573
  applyCirclePencil(color, centerX, centerY, brush, alpha = 255, blendFn) {
2447
- const bounds = getCircleBrushOrPencilBounds2(centerX, centerY, brush.size, writer.target.width, writer.target.height, boundsOut);
2448
- const {
2449
- x,
2450
- y,
2451
- w,
2452
- h
2453
- } = bounds;
2454
- writer.accumulator.storeRegionBeforeState(x, y, w, h);
2455
- applyCircleBrushToPixelData2(writer.target, color, centerX, centerY, brush, alpha, blendFn, bounds);
2574
+ const target = writer.config.target;
2575
+ const b = getCircleBrushOrPencilBounds2(centerX, centerY, brush.size, target.width, target.height, boundsOut);
2576
+ const didChange = writer.accumulator.storeRegionBeforeState(b.x, b.y, b.w, b.h);
2577
+ return didChange(applyCircleMaskToPixelData(target, color, centerX, centerY, brush, alpha, blendFn, b));
2456
2578
  }
2457
2579
  };
2458
2580
  });
2459
2581
 
2460
2582
  // src/History/PixelMutator/mutatorApplyCirclePencilStroke.ts
2461
- var defaults6 = {
2583
+ var defaults7 = {
2462
2584
  forEachLinePoint,
2463
2585
  blendColorPixelDataBinaryMask,
2464
2586
  getCircleBrushOrPencilBounds,
2465
2587
  getCircleBrushOrPencilStrokeBounds
2466
2588
  };
2467
- var mutatorApplyCirclePencilStroke = ((writer, deps = defaults6) => {
2589
+ var mutatorApplyCirclePencilStroke = ((writer, deps = defaults7) => {
2468
2590
  const {
2469
- forEachLinePoint: forEachLinePoint2 = defaults6.forEachLinePoint,
2470
- blendColorPixelDataBinaryMask: blendColorPixelDataBinaryMask2 = defaults6.blendColorPixelDataBinaryMask,
2471
- getCircleBrushOrPencilStrokeBounds: getCircleBrushOrPencilStrokeBounds2 = defaults6.getCircleBrushOrPencilStrokeBounds,
2472
- getCircleBrushOrPencilBounds: getCircleBrushOrPencilBounds2 = defaults6.getCircleBrushOrPencilBounds
2591
+ forEachLinePoint: forEachLinePoint2 = defaults7.forEachLinePoint,
2592
+ blendColorPixelDataBinaryMask: blendColorPixelDataBinaryMask2 = defaults7.blendColorPixelDataBinaryMask,
2593
+ getCircleBrushOrPencilStrokeBounds: getCircleBrushOrPencilStrokeBounds2 = defaults7.getCircleBrushOrPencilStrokeBounds,
2594
+ getCircleBrushOrPencilBounds: getCircleBrushOrPencilBounds2 = defaults7.getCircleBrushOrPencilBounds
2473
2595
  } = deps;
2474
2596
  const strokeBoundsOut = {
2475
2597
  x: 0,
@@ -2510,8 +2632,9 @@ var mutatorApplyCirclePencilStroke = ((writer, deps = defaults6) => {
2510
2632
  mask.w = bw;
2511
2633
  mask.h = bh;
2512
2634
  const maskData = mask.data;
2513
- const targetWidth = writer.target.width;
2514
- const targetHeight = writer.target.height;
2635
+ const target = writer.config.target;
2636
+ const targetWidth = target.width;
2637
+ const targetHeight = target.height;
2515
2638
  forEachLinePoint2(x0, y0, x1, y1, (px, py) => {
2516
2639
  const {
2517
2640
  x: cbx,
@@ -2546,7 +2669,7 @@ var mutatorApplyCirclePencilStroke = ((writer, deps = defaults6) => {
2546
2669
  blendColorPixelOptions.y = by;
2547
2670
  blendColorPixelOptions.w = bw;
2548
2671
  blendColorPixelOptions.h = bh;
2549
- blendColorPixelDataBinaryMask2(writer.target, color, mask, blendColorPixelOptions);
2672
+ blendColorPixelDataBinaryMask2(target, color, mask, blendColorPixelOptions);
2550
2673
  }
2551
2674
  };
2552
2675
  });
@@ -2581,7 +2704,9 @@ function applyRectBrushToPixelData(target, color, centerX, centerY, brushWidth,
2581
2704
  const targetWidth = target.width;
2582
2705
  const targetHeight = target.height;
2583
2706
  const b = bounds ?? getRectBrushOrPencilBounds(centerX, centerY, brushWidth, brushHeight, targetWidth, targetHeight);
2584
- if (b.w <= 0 || b.h <= 0) return;
2707
+ if (b.w <= 0 || b.h <= 0) {
2708
+ return false;
2709
+ }
2585
2710
  const data32 = target.data32;
2586
2711
  const baseColor = color & 16777215;
2587
2712
  const baseSrcAlpha = color >>> 24;
@@ -2595,6 +2720,7 @@ function applyRectBrushToPixelData(target, color, centerX, centerY, brushWidth,
2595
2720
  const endX = b.x + b.w;
2596
2721
  const endY = b.y + b.h;
2597
2722
  const isOverwrite = blendFn.isOverwrite;
2723
+ let didChange = false;
2598
2724
  for (let py = b.y; py < endY; py++) {
2599
2725
  const rowOffset = py * targetWidth;
2600
2726
  const dy = Math.abs(py - fCenterY + centerOffsetY) * invHalfH;
@@ -2617,20 +2743,26 @@ function applyRectBrushToPixelData(target, color, centerX, centerY, brushWidth,
2617
2743
  if (a === 0 && !isOverwrite) continue;
2618
2744
  finalCol = (a << 24 | baseColor) >>> 0;
2619
2745
  }
2620
- data32[idx] = blendFn(finalCol, data32[idx]);
2746
+ const current = data32[idx];
2747
+ const next = blendFn(finalCol, current);
2748
+ if (current !== next) {
2749
+ data32[idx] = next;
2750
+ didChange = true;
2751
+ }
2621
2752
  }
2622
2753
  }
2754
+ return didChange;
2623
2755
  }
2624
2756
 
2625
2757
  // src/History/PixelMutator/mutatorApplyRectBrush.ts
2626
- var defaults7 = {
2758
+ var defaults8 = {
2627
2759
  applyRectBrushToPixelData,
2628
2760
  getRectBrushOrPencilBounds
2629
2761
  };
2630
- var mutatorApplyRectBrush = ((writer, deps = defaults7) => {
2762
+ var mutatorApplyRectBrush = ((writer, deps = defaults8) => {
2631
2763
  const {
2632
- applyRectBrushToPixelData: applyRectBrushToPixelData2 = defaults7.applyRectBrushToPixelData,
2633
- getRectBrushOrPencilBounds: getRectBrushOrPencilBounds2 = defaults7.getRectBrushOrPencilBounds
2764
+ applyRectBrushToPixelData: applyRectBrushToPixelData2 = defaults8.applyRectBrushToPixelData,
2765
+ getRectBrushOrPencilBounds: getRectBrushOrPencilBounds2 = defaults8.getRectBrushOrPencilBounds
2634
2766
  } = deps;
2635
2767
  const boundsOut = {
2636
2768
  x: 0,
@@ -2640,15 +2772,10 @@ var mutatorApplyRectBrush = ((writer, deps = defaults7) => {
2640
2772
  };
2641
2773
  return {
2642
2774
  applyRectBrush(color, centerX, centerY, brushWidth, brushHeight, alpha = 255, fallOff, blendFn) {
2643
- const bounds = getRectBrushOrPencilBounds2(centerX, centerY, brushWidth, brushHeight, writer.target.width, writer.target.height, boundsOut);
2644
- const {
2645
- x,
2646
- y,
2647
- w,
2648
- h
2649
- } = bounds;
2650
- writer.accumulator.storeRegionBeforeState(x, y, w, h);
2651
- applyRectBrushToPixelData2(writer.target, color, centerX, centerY, brushWidth, brushHeight, alpha, fallOff, blendFn, bounds);
2775
+ const target = writer.config.target;
2776
+ const b = getRectBrushOrPencilBounds2(centerX, centerY, brushWidth, brushHeight, target.width, target.height, boundsOut);
2777
+ const didChange = writer.accumulator.storeRegionBeforeState(b.x, b.y, b.w, b.h);
2778
+ return didChange(applyRectBrushToPixelData2(target, color, centerX, centerY, brushWidth, brushHeight, alpha, fallOff, blendFn, b));
2652
2779
  }
2653
2780
  };
2654
2781
  });
@@ -2669,18 +2796,18 @@ function getRectBrushOrPencilStrokeBounds(x0, y0, x1, y1, brushWidth, brushHeigh
2669
2796
  }
2670
2797
 
2671
2798
  // src/History/PixelMutator/mutatorApplyRectBrushStroke.ts
2672
- var defaults8 = {
2799
+ var defaults9 = {
2673
2800
  forEachLinePoint,
2674
2801
  blendColorPixelDataAlphaMask,
2675
2802
  getRectBrushOrPencilBounds,
2676
2803
  getRectBrushOrPencilStrokeBounds
2677
2804
  };
2678
- var mutatorApplyRectBrushStroke = ((writer, deps = defaults8) => {
2805
+ var mutatorApplyRectBrushStroke = ((writer, deps = defaults9) => {
2679
2806
  const {
2680
- forEachLinePoint: forEachLinePoint2 = defaults8.forEachLinePoint,
2681
- blendColorPixelDataAlphaMask: blendColorPixelDataAlphaMask2 = defaults8.blendColorPixelDataAlphaMask,
2682
- getRectBrushOrPencilBounds: getRectBrushOrPencilBounds2 = defaults8.getRectBrushOrPencilBounds,
2683
- getRectBrushOrPencilStrokeBounds: getRectBrushOrPencilStrokeBounds2 = defaults8.getRectBrushOrPencilStrokeBounds
2807
+ forEachLinePoint: forEachLinePoint2 = defaults9.forEachLinePoint,
2808
+ blendColorPixelDataAlphaMask: blendColorPixelDataAlphaMask2 = defaults9.blendColorPixelDataAlphaMask,
2809
+ getRectBrushOrPencilBounds: getRectBrushOrPencilBounds2 = defaults9.getRectBrushOrPencilBounds,
2810
+ getRectBrushOrPencilStrokeBounds: getRectBrushOrPencilStrokeBounds2 = defaults9.getRectBrushOrPencilStrokeBounds
2684
2811
  } = deps;
2685
2812
  const strokeBoundsOut = {
2686
2813
  x: 0,
@@ -2727,8 +2854,9 @@ var mutatorApplyRectBrushStroke = ((writer, deps = defaults8) => {
2727
2854
  const invHalfH = 1 / halfH;
2728
2855
  const centerOffsetX = brushWidth % 2 === 0 ? 0.5 : 0;
2729
2856
  const centerOffsetY = brushHeight % 2 === 0 ? 0.5 : 0;
2730
- const targetWidth = writer.target.width;
2731
- const targetHeight = writer.target.height;
2857
+ const target = writer.config.target;
2858
+ const targetWidth = target.width;
2859
+ const targetHeight = target.height;
2732
2860
  forEachLinePoint2(x0, y0, x1, y1, (px, py) => {
2733
2861
  const {
2734
2862
  x: rbx,
@@ -2766,22 +2894,22 @@ var mutatorApplyRectBrushStroke = ((writer, deps = defaults8) => {
2766
2894
  blendColorPixelOptions.y = by;
2767
2895
  blendColorPixelOptions.w = bw;
2768
2896
  blendColorPixelOptions.h = bh;
2769
- blendColorPixelDataAlphaMask2(writer.target, color, mask, blendColorPixelOptions);
2897
+ blendColorPixelDataAlphaMask2(target, color, mask, blendColorPixelOptions);
2770
2898
  }
2771
2899
  };
2772
2900
  });
2773
2901
 
2774
2902
  // src/History/PixelMutator/mutatorApplyRectPencil.ts
2775
- var defaults9 = {
2903
+ var defaults10 = {
2776
2904
  applyRectBrushToPixelData,
2777
2905
  getRectBrushOrPencilBounds,
2778
2906
  fallOff: () => 1
2779
2907
  };
2780
- var mutatorApplyRectPencil = ((writer, deps = defaults9) => {
2908
+ var mutatorApplyRectPencil = ((writer, deps = defaults10) => {
2781
2909
  const {
2782
- applyRectBrushToPixelData: applyRectBrushToPixelData2 = defaults9.applyRectBrushToPixelData,
2783
- getRectBrushOrPencilBounds: getRectBrushOrPencilBounds2 = defaults9.getRectBrushOrPencilBounds,
2784
- fallOff = defaults9.fallOff
2910
+ applyRectBrushToPixelData: applyRectBrushToPixelData2 = defaults10.applyRectBrushToPixelData,
2911
+ getRectBrushOrPencilBounds: getRectBrushOrPencilBounds2 = defaults10.getRectBrushOrPencilBounds,
2912
+ fallOff = defaults10.fallOff
2785
2913
  } = deps;
2786
2914
  const boundsOut = {
2787
2915
  x: 0,
@@ -2791,32 +2919,27 @@ var mutatorApplyRectPencil = ((writer, deps = defaults9) => {
2791
2919
  };
2792
2920
  return {
2793
2921
  applyRectPencil(color, centerX, centerY, brushWidth, brushHeight, alpha = 255, blendFn) {
2794
- const bounds = getRectBrushOrPencilBounds2(centerX, centerY, brushWidth, brushHeight, writer.target.width, writer.target.height, boundsOut);
2795
- const {
2796
- x,
2797
- y,
2798
- w,
2799
- h
2800
- } = bounds;
2801
- writer.accumulator.storeRegionBeforeState(x, y, w, h);
2802
- applyRectBrushToPixelData2(writer.target, color, centerX, centerY, brushWidth, brushHeight, alpha, fallOff, blendFn, bounds);
2922
+ const target = writer.config.target;
2923
+ const b = getRectBrushOrPencilBounds2(centerX, centerY, brushWidth, brushHeight, target.width, target.height, boundsOut);
2924
+ const didChange = writer.accumulator.storeRegionBeforeState(b.x, b.y, b.w, b.h);
2925
+ return didChange(applyRectBrushToPixelData2(target, color, centerX, centerY, brushWidth, brushHeight, alpha, fallOff, blendFn, b));
2803
2926
  }
2804
2927
  };
2805
2928
  });
2806
2929
 
2807
2930
  // src/History/PixelMutator/mutatorApplyRectPencilStroke.ts
2808
- var defaults10 = {
2931
+ var defaults11 = {
2809
2932
  forEachLinePoint,
2810
2933
  getRectBrushOrPencilBounds,
2811
2934
  getRectBrushOrPencilStrokeBounds,
2812
2935
  blendColorPixelDataBinaryMask
2813
2936
  };
2814
- var mutatorApplyRectPencilStroke = ((writer, deps = defaults10) => {
2937
+ var mutatorApplyRectPencilStroke = ((writer, deps = defaults11) => {
2815
2938
  const {
2816
- forEachLinePoint: forEachLinePoint2 = defaults10.forEachLinePoint,
2817
- blendColorPixelDataBinaryMask: blendColorPixelDataBinaryMask2 = defaults10.blendColorPixelDataBinaryMask,
2818
- getRectBrushOrPencilBounds: getRectBrushOrPencilBounds2 = defaults10.getRectBrushOrPencilBounds,
2819
- getRectBrushOrPencilStrokeBounds: getRectBrushOrPencilStrokeBounds2 = defaults10.getRectBrushOrPencilStrokeBounds
2939
+ forEachLinePoint: forEachLinePoint2 = defaults11.forEachLinePoint,
2940
+ blendColorPixelDataBinaryMask: blendColorPixelDataBinaryMask2 = defaults11.blendColorPixelDataBinaryMask,
2941
+ getRectBrushOrPencilBounds: getRectBrushOrPencilBounds2 = defaults11.getRectBrushOrPencilBounds,
2942
+ getRectBrushOrPencilStrokeBounds: getRectBrushOrPencilStrokeBounds2 = defaults11.getRectBrushOrPencilStrokeBounds
2820
2943
  } = deps;
2821
2944
  const strokeBoundsOut = {
2822
2945
  x: 0,
@@ -2860,8 +2983,9 @@ var mutatorApplyRectPencilStroke = ((writer, deps = defaults10) => {
2860
2983
  const halfW = brushWidth / 2;
2861
2984
  const halfH = brushHeight / 2;
2862
2985
  const centerOffset = brushWidth % 2 === 0 ? 0.5 : 0;
2863
- const targetWidth = writer.target.width;
2864
- const targetHeight = writer.target.height;
2986
+ const target = writer.config.target;
2987
+ const targetWidth = target.width;
2988
+ const targetHeight = target.height;
2865
2989
  forEachLinePoint2(x0, y0, x1, y1, (px, py) => {
2866
2990
  const {
2867
2991
  x: rbx,
@@ -2894,7 +3018,7 @@ var mutatorApplyRectPencilStroke = ((writer, deps = defaults10) => {
2894
3018
  blendColorPixelOptions.y = by;
2895
3019
  blendColorPixelOptions.w = bw;
2896
3020
  blendColorPixelOptions.h = bh;
2897
- blendColorPixelDataBinaryMask2(writer.target, color, mask, blendColorPixelOptions);
3021
+ blendColorPixelDataBinaryMask2(target, color, mask, blendColorPixelOptions);
2898
3022
  }
2899
3023
  };
2900
3024
  });
@@ -2909,11 +3033,14 @@ function blendColorPixelData(dst, color, opts = {}) {
2909
3033
  alpha: globalAlpha = 255,
2910
3034
  blendFn = sourceOverPerfect
2911
3035
  } = opts;
2912
- if (globalAlpha === 0) return;
3036
+ if (globalAlpha === 0) return false;
2913
3037
  const baseSrcAlpha = color >>> 24;
2914
3038
  const isOverwrite = blendFn.isOverwrite || false;
2915
- if (baseSrcAlpha === 0 && !isOverwrite) return;
2916
- let x = targetX, y = targetY, w = width, h = height;
3039
+ if (baseSrcAlpha === 0 && !isOverwrite) return false;
3040
+ let x = targetX;
3041
+ let y = targetY;
3042
+ let w = width;
3043
+ let h = height;
2917
3044
  if (x < 0) {
2918
3045
  w += x;
2919
3046
  x = 0;
@@ -2924,69 +3051,97 @@ function blendColorPixelData(dst, color, opts = {}) {
2924
3051
  }
2925
3052
  const actualW = Math.min(w, dst.width - x);
2926
3053
  const actualH = Math.min(h, dst.height - y);
2927
- if (actualW <= 0 || actualH <= 0) return;
3054
+ if (actualW <= 0 || actualH <= 0) return false;
2928
3055
  let finalSrcColor = color;
2929
3056
  if (globalAlpha < 255) {
2930
3057
  const a = baseSrcAlpha * globalAlpha + 128 >> 8;
2931
- if (a === 0 && !isOverwrite) return;
3058
+ if (a === 0 && !isOverwrite) return false;
2932
3059
  finalSrcColor = (color & 16777215 | a << 24) >>> 0;
2933
3060
  }
2934
3061
  const dst32 = dst.data32;
2935
3062
  const dw = dst.width;
2936
3063
  let dIdx = y * dw + x | 0;
2937
3064
  const dStride = dw - actualW | 0;
3065
+ let didChange = false;
2938
3066
  for (let iy = 0; iy < actualH; iy++) {
2939
3067
  for (let ix = 0; ix < actualW; ix++) {
2940
- dst32[dIdx] = blendFn(finalSrcColor, dst32[dIdx]);
3068
+ const current = dst32[dIdx];
3069
+ const next = blendFn(finalSrcColor, current);
3070
+ if (current !== next) {
3071
+ dst32[dIdx] = next;
3072
+ didChange = true;
3073
+ }
2941
3074
  dIdx++;
2942
3075
  }
2943
3076
  dIdx += dStride;
2944
3077
  }
3078
+ return didChange;
2945
3079
  }
2946
3080
 
2947
3081
  // src/History/PixelMutator/mutatorBlendColor.ts
2948
- var defaults11 = {
3082
+ var defaults12 = {
2949
3083
  blendColorPixelData
2950
3084
  };
2951
- var mutatorBlendColor = ((writer, deps = defaults11) => {
3085
+ var mutatorBlendColor = ((writer, deps = defaults12) => {
2952
3086
  const {
2953
- blendColorPixelData: blendColorPixelData2 = defaults11.blendColorPixelData
3087
+ blendColorPixelData: blendColorPixelData2 = defaults12.blendColorPixelData
2954
3088
  } = deps;
2955
3089
  return {
2956
3090
  blendColor(color, opts = {}) {
3091
+ const target = writer.config.target;
2957
3092
  const {
2958
3093
  x = 0,
2959
3094
  y = 0,
2960
- w = writer.target.width,
2961
- h = writer.target.height
3095
+ w = target.width,
3096
+ h = target.height
2962
3097
  } = opts;
2963
- writer.accumulator.storeRegionBeforeState(x, y, w, h);
2964
- blendColorPixelData2(writer.target, color, opts);
3098
+ const didChange = writer.accumulator.storeRegionBeforeState(x, y, w, h);
3099
+ return didChange(blendColorPixelData2(target, color, opts));
2965
3100
  }
2966
3101
  };
2967
3102
  });
2968
3103
 
3104
+ // src/PixelData/blendPixel.ts
3105
+ function blendPixel(target, x, y, color, alpha = 255, blendFn = sourceOverPerfect) {
3106
+ if (alpha === 0) return false;
3107
+ let width = target.width;
3108
+ let height = target.height;
3109
+ if (x < 0 || x >= width || y < 0 || y >= height) return false;
3110
+ let srcAlpha = color >>> 24;
3111
+ let isOverwrite = blendFn.isOverwrite;
3112
+ if (srcAlpha === 0 && !isOverwrite) return false;
3113
+ let dst32 = target.data32;
3114
+ let index = y * width + x;
3115
+ let finalColor = color;
3116
+ if (alpha !== 255) {
3117
+ let finalAlpha = srcAlpha * alpha + 128 >> 8;
3118
+ if (finalAlpha === 0 && !isOverwrite) return false;
3119
+ finalColor = (color & 16777215 | finalAlpha << 24) >>> 0;
3120
+ }
3121
+ let current = dst32[index];
3122
+ let next = blendFn(finalColor, current);
3123
+ if (current !== next) {
3124
+ dst32[index] = next;
3125
+ return true;
3126
+ }
3127
+ return false;
3128
+ }
3129
+
2969
3130
  // src/History/PixelMutator/mutatorBlendPixel.ts
2970
- function mutatorBlendPixel(writer) {
3131
+ var defaults13 = {
3132
+ blendPixel
3133
+ };
3134
+ var mutatorBlendPixel = ((writer, deps = defaults13) => {
3135
+ const {
3136
+ blendPixel: blendPixel2 = defaults13.blendPixel
3137
+ } = deps;
2971
3138
  return {
2972
- blendPixel(x, y, color, alpha = 255, blendFn = overwriteFast) {
2973
- let target = writer.target;
2974
- let width = target.width;
2975
- let height = target.height;
2976
- if (x < 0 || x >= width || y < 0 || y >= height) return;
2977
- writer.accumulator.storeTileBeforeState(x, y);
2978
- let index = y * width + x;
2979
- let bg = target.data32[index];
2980
- let finalColor = color;
2981
- if (alpha < 255) {
2982
- let baseSrcAlpha = color >>> 24;
2983
- let finalAlpha = baseSrcAlpha * alpha + 128 >> 8;
2984
- finalColor = (color & 16777215 | finalAlpha << 24) >>> 0;
2985
- }
2986
- target.data32[index] = blendFn(finalColor, bg);
3139
+ blendPixel(x, y, color, alpha, blendFn) {
3140
+ const didChange = writer.accumulator.storePixelBeforeState(x, y);
3141
+ return didChange(blendPixel2(writer.config.target, x, y, color, alpha, blendFn));
2987
3142
  }
2988
3143
  };
2989
- }
3144
+ });
2990
3145
 
2991
3146
  // src/PixelData/blendPixelData.ts
2992
3147
  function blendPixelData(dst, src, opts = {}) {
@@ -3000,7 +3155,7 @@ function blendPixelData(dst, src, opts = {}) {
3000
3155
  alpha: globalAlpha = 255,
3001
3156
  blendFn = sourceOverPerfect
3002
3157
  } = opts;
3003
- if (globalAlpha === 0) return;
3158
+ if (globalAlpha === 0) return false;
3004
3159
  let x = targetX;
3005
3160
  let y = targetY;
3006
3161
  let sx = sourceX;
@@ -3031,7 +3186,7 @@ function blendPixelData(dst, src, opts = {}) {
3031
3186
  }
3032
3187
  const actualW = Math.min(w, dst.width - x);
3033
3188
  const actualH = Math.min(h, dst.height - y);
3034
- if (actualW <= 0 || actualH <= 0) return;
3189
+ if (actualW <= 0 || actualH <= 0) return false;
3035
3190
  const dst32 = dst.data32;
3036
3191
  const src32 = src.data32;
3037
3192
  const dw = dst.width;
@@ -3042,6 +3197,7 @@ function blendPixelData(dst, src, opts = {}) {
3042
3197
  const sStride = sw - actualW | 0;
3043
3198
  const isOpaque = globalAlpha === 255;
3044
3199
  const isOverwrite = blendFn.isOverwrite;
3200
+ let didChange = false;
3045
3201
  for (let iy = 0; iy < actualH; iy++) {
3046
3202
  for (let ix = 0; ix < actualW; ix++) {
3047
3203
  const srcCol = src32[sIdx];
@@ -3061,22 +3217,28 @@ function blendPixelData(dst, src, opts = {}) {
3061
3217
  }
3062
3218
  finalCol = (srcCol & 16777215 | a << 24) >>> 0;
3063
3219
  }
3064
- dst32[dIdx] = blendFn(finalCol, dst32[dIdx]);
3220
+ const current = dst32[dIdx];
3221
+ const next = blendFn(finalCol, dst32[dIdx]);
3222
+ if (current !== next) {
3223
+ dst32[dIdx] = next;
3224
+ didChange = true;
3225
+ }
3065
3226
  dIdx++;
3066
3227
  sIdx++;
3067
3228
  }
3068
3229
  dIdx += dStride;
3069
3230
  sIdx += sStride;
3070
3231
  }
3232
+ return didChange;
3071
3233
  }
3072
3234
 
3073
3235
  // src/History/PixelMutator/mutatorBlendPixelData.ts
3074
- var defaults12 = {
3236
+ var defaults14 = {
3075
3237
  blendPixelData
3076
3238
  };
3077
- var mutatorBlendPixelData = ((writer, deps = defaults12) => {
3239
+ var mutatorBlendPixelData = ((writer, deps = defaults14) => {
3078
3240
  const {
3079
- blendPixelData: blendPixelData2 = defaults12.blendPixelData
3241
+ blendPixelData: blendPixelData2 = defaults14.blendPixelData
3080
3242
  } = deps;
3081
3243
  return {
3082
3244
  blendPixelData(src, opts = {}) {
@@ -3086,8 +3248,8 @@ var mutatorBlendPixelData = ((writer, deps = defaults12) => {
3086
3248
  w = src.width,
3087
3249
  h = src.height
3088
3250
  } = opts;
3089
- writer.accumulator.storeRegionBeforeState(x, y, w, h);
3090
- blendPixelData2(writer.target, src, opts);
3251
+ const didChange = writer.accumulator.storeRegionBeforeState(x, y, w, h);
3252
+ return didChange(blendPixelData2(writer.config.target, src, opts));
3091
3253
  }
3092
3254
  };
3093
3255
  });
@@ -3107,7 +3269,7 @@ function blendPixelDataAlphaMask(dst, src, alphaMask, opts = {}) {
3107
3269
  my = 0,
3108
3270
  invertMask = false
3109
3271
  } = opts;
3110
- if (globalAlpha === 0) return;
3272
+ if (globalAlpha === 0) return false;
3111
3273
  let x = targetX;
3112
3274
  let y = targetY;
3113
3275
  let sx = sourceX;
@@ -3138,7 +3300,7 @@ function blendPixelDataAlphaMask(dst, src, alphaMask, opts = {}) {
3138
3300
  }
3139
3301
  const actualW = Math.min(w, dst.width - x);
3140
3302
  const actualH = Math.min(h, dst.height - y);
3141
- if (actualW <= 0 || actualH <= 0) return;
3303
+ if (actualW <= 0 || actualH <= 0) return false;
3142
3304
  const dw = dst.width;
3143
3305
  const sw = src.width;
3144
3306
  const mPitch = alphaMask.w;
@@ -3155,6 +3317,7 @@ function blendPixelDataAlphaMask(dst, src, alphaMask, opts = {}) {
3155
3317
  const mStride = mPitch - actualW | 0;
3156
3318
  const isOpaque = globalAlpha === 255;
3157
3319
  const isOverwrite = blendFn.isOverwrite || false;
3320
+ let didChange = false;
3158
3321
  for (let iy = 0; iy < actualH; iy++) {
3159
3322
  for (let ix = 0; ix < actualW; ix++) {
3160
3323
  const mVal = maskData[mIdx];
@@ -3196,7 +3359,12 @@ function blendPixelDataAlphaMask(dst, src, alphaMask, opts = {}) {
3196
3359
  }
3197
3360
  finalCol = (srcCol & 16777215 | a << 24) >>> 0;
3198
3361
  }
3199
- dst32[dIdx] = blendFn(finalCol, dst32[dIdx]);
3362
+ const current = dst32[dIdx];
3363
+ const next = blendFn(finalCol, dst32[dIdx]);
3364
+ if (current !== next) {
3365
+ dst32[dIdx] = next;
3366
+ didChange = true;
3367
+ }
3200
3368
  dIdx++;
3201
3369
  sIdx++;
3202
3370
  mIdx++;
@@ -3205,15 +3373,16 @@ function blendPixelDataAlphaMask(dst, src, alphaMask, opts = {}) {
3205
3373
  sIdx += sStride;
3206
3374
  mIdx += mStride;
3207
3375
  }
3376
+ return didChange;
3208
3377
  }
3209
3378
 
3210
3379
  // src/History/PixelMutator/mutatorBlendPixelDataAlphaMask.ts
3211
- var defaults13 = {
3380
+ var defaults15 = {
3212
3381
  blendPixelDataAlphaMask
3213
3382
  };
3214
- var mutatorBlendPixelDataAlphaMask = ((writer, deps = defaults13) => {
3383
+ var mutatorBlendPixelDataAlphaMask = ((writer, deps = defaults15) => {
3215
3384
  const {
3216
- blendPixelDataAlphaMask: blendPixelDataAlphaMask2 = defaults13.blendPixelDataAlphaMask
3385
+ blendPixelDataAlphaMask: blendPixelDataAlphaMask2 = defaults15.blendPixelDataAlphaMask
3217
3386
  } = deps;
3218
3387
  return {
3219
3388
  blendPixelDataAlphaMask(src, mask, opts = {}) {
@@ -3221,8 +3390,8 @@ var mutatorBlendPixelDataAlphaMask = ((writer, deps = defaults13) => {
3221
3390
  const y = opts.y ?? 0;
3222
3391
  const w = opts.w ?? src.width;
3223
3392
  const h = opts.h ?? src.height;
3224
- writer.accumulator.storeRegionBeforeState(x, y, w, h);
3225
- blendPixelDataAlphaMask2(writer.target, src, mask, opts);
3393
+ const didChange = writer.accumulator.storeRegionBeforeState(x, y, w, h);
3394
+ return didChange(blendPixelDataAlphaMask2(writer.config.target, src, mask, opts));
3226
3395
  }
3227
3396
  };
3228
3397
  });
@@ -3242,7 +3411,7 @@ function blendPixelDataBinaryMask(dst, src, binaryMask, opts = {}) {
3242
3411
  my = 0,
3243
3412
  invertMask = false
3244
3413
  } = opts;
3245
- if (globalAlpha === 0) return;
3414
+ if (globalAlpha === 0) return false;
3246
3415
  let x = targetX;
3247
3416
  let y = targetY;
3248
3417
  let sx = sourceX;
@@ -3273,7 +3442,7 @@ function blendPixelDataBinaryMask(dst, src, binaryMask, opts = {}) {
3273
3442
  }
3274
3443
  const actualW = Math.min(w, dst.width - x);
3275
3444
  const actualH = Math.min(h, dst.height - y);
3276
- if (actualW <= 0 || actualH <= 0) return;
3445
+ if (actualW <= 0 || actualH <= 0) return false;
3277
3446
  const dx = x - targetX | 0;
3278
3447
  const dy = y - targetY | 0;
3279
3448
  const dst32 = dst.data32;
@@ -3291,6 +3460,7 @@ function blendPixelDataBinaryMask(dst, src, binaryMask, opts = {}) {
3291
3460
  const skipVal = invertMask ? 1 : 0;
3292
3461
  const isOpaque = globalAlpha === 255;
3293
3462
  const isOverwrite = blendFn.isOverwrite || false;
3463
+ let didChange = false;
3294
3464
  for (let iy = 0; iy < actualH; iy++) {
3295
3465
  for (let ix = 0; ix < actualW; ix++) {
3296
3466
  if (maskData[mIdx] === skipVal) {
@@ -3318,7 +3488,12 @@ function blendPixelDataBinaryMask(dst, src, binaryMask, opts = {}) {
3318
3488
  }
3319
3489
  finalCol = (srcCol & 16777215 | a << 24) >>> 0;
3320
3490
  }
3321
- dst32[dIdx] = blendFn(finalCol, dst32[dIdx]);
3491
+ const current = dst32[dIdx];
3492
+ const next = blendFn(finalCol, dst32[dIdx]);
3493
+ if (current !== next) {
3494
+ dst32[dIdx] = next;
3495
+ didChange = true;
3496
+ }
3322
3497
  dIdx++;
3323
3498
  sIdx++;
3324
3499
  mIdx++;
@@ -3327,15 +3502,16 @@ function blendPixelDataBinaryMask(dst, src, binaryMask, opts = {}) {
3327
3502
  sIdx += sStride;
3328
3503
  mIdx += mStride;
3329
3504
  }
3505
+ return didChange;
3330
3506
  }
3331
3507
 
3332
3508
  // src/History/PixelMutator/mutatorBlendPixelDataBinaryMask.ts
3333
- var defaults14 = {
3509
+ var defaults16 = {
3334
3510
  blendPixelDataBinaryMask
3335
3511
  };
3336
- var mutatorBlendPixelDataBinaryMask = ((writer, deps = defaults14) => {
3512
+ var mutatorBlendPixelDataBinaryMask = ((writer, deps = defaults16) => {
3337
3513
  const {
3338
- blendPixelDataBinaryMask: blendPixelDataBinaryMask2 = defaults14.blendPixelDataBinaryMask
3514
+ blendPixelDataBinaryMask: blendPixelDataBinaryMask2 = defaults16.blendPixelDataBinaryMask
3339
3515
  } = deps;
3340
3516
  return {
3341
3517
  blendPixelDataBinaryMask(src, mask, opts = {}) {
@@ -3343,15 +3519,15 @@ var mutatorBlendPixelDataBinaryMask = ((writer, deps = defaults14) => {
3343
3519
  const y = opts.y ?? 0;
3344
3520
  const w = opts.w ?? src.width;
3345
3521
  const h = opts.h ?? src.height;
3346
- writer.accumulator.storeRegionBeforeState(x, y, w, h);
3347
- blendPixelDataBinaryMask2(writer.target, src, mask, opts);
3522
+ const didChange = writer.accumulator.storeRegionBeforeState(x, y, w, h);
3523
+ return didChange(blendPixelDataBinaryMask2(writer.config.target, src, mask, opts));
3348
3524
  }
3349
3525
  };
3350
3526
  });
3351
3527
 
3352
- // src/PixelData/fillPixelData.ts
3528
+ // src/PixelData/fillPixelDataFast.ts
3353
3529
  var SCRATCH_RECT = makeClippedRect();
3354
- function fillPixelData(dst, color, _x, _y, _w, _h) {
3530
+ function fillPixelDataFast(dst, color, _x, _y, _w, _h) {
3355
3531
  let x;
3356
3532
  let y;
3357
3533
  let w;
@@ -3394,55 +3570,111 @@ function fillPixelData(dst, color, _x, _y, _w, _h) {
3394
3570
  }
3395
3571
 
3396
3572
  // src/History/PixelMutator/mutatorClear.ts
3397
- var defaults15 = {
3398
- fillPixelData
3573
+ var defaults17 = {
3574
+ fillPixelData: fillPixelDataFast
3399
3575
  };
3400
- var mutatorClear = ((writer, deps = defaults15) => {
3576
+ var mutatorClear = ((writer, deps = defaults17) => {
3401
3577
  const {
3402
- fillPixelData: fillPixelData2 = defaults15.fillPixelData
3578
+ fillPixelData: fillPixelData2 = defaults17.fillPixelData
3403
3579
  } = deps;
3404
3580
  return {
3405
3581
  clear(rect = {}) {
3582
+ const target = writer.config.target;
3406
3583
  const x = rect.x ?? 0;
3407
3584
  const y = rect.y ?? 0;
3408
- const w = rect.w ?? writer.target.width;
3409
- const h = rect.h ?? writer.target.height;
3585
+ const w = rect.w ?? target.width;
3586
+ const h = rect.h ?? target.height;
3410
3587
  writer.accumulator.storeRegionBeforeState(x, y, w, h);
3411
- fillPixelData2(writer.target, 0, x, y, w, h);
3588
+ fillPixelData2(target, 0, x, y, w, h);
3412
3589
  }
3413
3590
  };
3414
3591
  });
3415
3592
 
3593
+ // src/PixelData/fillPixelData.ts
3594
+ var SCRATCH_RECT2 = makeClippedRect();
3595
+ function fillPixelData(dst, color, _x, _y, _w, _h) {
3596
+ let x;
3597
+ let y;
3598
+ let w;
3599
+ let h;
3600
+ if (typeof _x === "object") {
3601
+ x = _x.x ?? 0;
3602
+ y = _x.y ?? 0;
3603
+ w = _x.w ?? dst.width;
3604
+ h = _x.h ?? dst.height;
3605
+ } else if (typeof _x === "number") {
3606
+ x = _x;
3607
+ y = _y;
3608
+ w = _w;
3609
+ h = _h;
3610
+ } else {
3611
+ x = 0;
3612
+ y = 0;
3613
+ w = dst.width;
3614
+ h = dst.height;
3615
+ }
3616
+ const clip = resolveRectClipping(x, y, w, h, dst.width, dst.height, SCRATCH_RECT2);
3617
+ if (!clip.inBounds) return false;
3618
+ const {
3619
+ x: finalX,
3620
+ y: finalY,
3621
+ w: actualW,
3622
+ h: actualH
3623
+ } = clip;
3624
+ const dst32 = dst.data32;
3625
+ const dw = dst.width;
3626
+ let hasChanged = false;
3627
+ for (let iy = 0; iy < actualH; iy++) {
3628
+ const rowOffset = (finalY + iy) * dw;
3629
+ const start = rowOffset + finalX;
3630
+ const end = start + actualW;
3631
+ for (let i = start; i < end; i++) {
3632
+ if (dst32[i] !== color) {
3633
+ dst32[i] = color;
3634
+ hasChanged = true;
3635
+ }
3636
+ }
3637
+ }
3638
+ return hasChanged;
3639
+ }
3640
+
3416
3641
  // src/History/PixelMutator/mutatorFill.ts
3417
- var defaults16 = {
3642
+ var defaults18 = {
3418
3643
  fillPixelData
3419
3644
  };
3420
- var mutatorFill = ((writer, deps = defaults16) => {
3645
+ var mutatorFill = ((writer, deps = defaults18) => {
3421
3646
  const {
3422
- fillPixelData: fillPixelData2 = defaults16.fillPixelData
3647
+ fillPixelData: fillPixelData2 = defaults18.fillPixelData
3423
3648
  } = deps;
3424
3649
  return {
3425
- fill(color, rect = {}) {
3426
- const {
3427
- x = 0,
3428
- y = 0,
3429
- w = writer.target.width,
3430
- h = writer.target.height
3431
- } = rect;
3432
- writer.accumulator.storeRegionBeforeState(x, y, w, h);
3433
- fillPixelData2(writer.target, color, x, y, w, h);
3650
+ fill(color, x = 0, y = 0, w = writer.config.target.width, h = writer.config.target.height) {
3651
+ const target = writer.config.target;
3652
+ const didChange = writer.accumulator.storeRegionBeforeState(x, y, w, h);
3653
+ return didChange(fillPixelData2(target, color, x, y, w, h));
3654
+ }
3655
+ };
3656
+ });
3657
+ var mutatorFillRect = ((writer, deps = defaults18) => {
3658
+ const {
3659
+ fillPixelData: fillPixelData2 = defaults18.fillPixelData
3660
+ } = deps;
3661
+ return {
3662
+ fillRect(color, rect) {
3663
+ const target = writer.config.target;
3664
+ const didChange = writer.accumulator.storeRegionBeforeState(rect.x, rect.y, rect.w, rect.h);
3665
+ return didChange(fillPixelData2(target, color, rect.x, rect.y, rect.w, rect.h));
3434
3666
  }
3435
3667
  };
3436
3668
  });
3437
3669
 
3438
3670
  // src/PixelData/fillPixelDataBinaryMask.ts
3439
- var SCRATCH_RECT2 = makeClippedRect();
3671
+ var SCRATCH_RECT3 = makeClippedRect();
3440
3672
  function fillPixelDataBinaryMask(dst, color, mask, alpha = 255, x = 0, y = 0) {
3441
- if (alpha === 0) return;
3673
+ if (alpha === 0) return false;
3442
3674
  const maskW = mask.w;
3443
3675
  const maskH = mask.h;
3444
- const clip = resolveRectClipping(x, y, maskW, maskH, dst.width, dst.height, SCRATCH_RECT2);
3445
- if (!clip.inBounds) return;
3676
+ const clip = resolveRectClipping(x, y, maskW, maskH, dst.width, dst.height, SCRATCH_RECT3);
3677
+ if (!clip.inBounds) return false;
3446
3678
  const {
3447
3679
  x: finalX,
3448
3680
  y: finalY,
@@ -3459,6 +3691,7 @@ function fillPixelDataBinaryMask(dst, color, mask, alpha = 255, x = 0, y = 0) {
3459
3691
  const a = baseSrcAlpha * alpha + 128 >> 8;
3460
3692
  finalCol = (colorRGB | a << 24) >>> 0;
3461
3693
  }
3694
+ let hasChanged = false;
3462
3695
  for (let iy = 0; iy < actualH; iy++) {
3463
3696
  const currentY = finalY + iy;
3464
3697
  const maskY = currentY - y;
@@ -3469,30 +3702,35 @@ function fillPixelDataBinaryMask(dst, color, mask, alpha = 255, x = 0, y = 0) {
3469
3702
  const maskX = currentX - x;
3470
3703
  const maskIndex = maskOffset + maskX;
3471
3704
  if (maskData[maskIndex]) {
3472
- dst32[dstRowOffset + currentX] = finalCol;
3705
+ const current = dst32[dstRowOffset + currentX];
3706
+ if (current !== finalCol) {
3707
+ dst32[dstRowOffset + currentX] = finalCol;
3708
+ hasChanged = true;
3709
+ }
3473
3710
  }
3474
3711
  }
3475
3712
  }
3713
+ return hasChanged;
3476
3714
  }
3477
3715
 
3478
3716
  // src/History/PixelMutator/mutatorFillBinaryMask.ts
3479
- var defaults17 = {
3717
+ var defaults19 = {
3480
3718
  fillPixelDataBinaryMask
3481
3719
  };
3482
- var mutatorFillBinaryMask = ((writer, deps = defaults17) => {
3720
+ var mutatorFillBinaryMask = ((writer, deps = defaults19) => {
3483
3721
  const {
3484
- fillPixelDataBinaryMask: fillPixelDataBinaryMask2 = defaults17.fillPixelDataBinaryMask
3722
+ fillPixelDataBinaryMask: fillPixelDataBinaryMask2 = defaults19.fillPixelDataBinaryMask
3485
3723
  } = deps;
3486
3724
  return {
3487
3725
  fillBinaryMask(color, mask, alpha = 255, x = 0, y = 0) {
3488
- writer.accumulator.storeRegionBeforeState(x, y, mask.w, mask.h);
3489
- fillPixelDataBinaryMask2(writer.target, color, mask, alpha, x, y);
3726
+ const didChange = writer.accumulator.storeRegionBeforeState(x, y, mask.w, mask.h);
3727
+ return didChange(fillPixelDataBinaryMask2(writer.config.target, color, mask, alpha, x, y));
3490
3728
  }
3491
3729
  };
3492
3730
  });
3493
3731
 
3494
3732
  // src/PixelData/invertPixelData.ts
3495
- var SCRATCH_RECT3 = makeClippedRect();
3733
+ var SCRATCH_RECT4 = makeClippedRect();
3496
3734
  function invertPixelData(pixelData, opts = {}) {
3497
3735
  const dst = pixelData;
3498
3736
  const {
@@ -3505,8 +3743,8 @@ function invertPixelData(pixelData, opts = {}) {
3505
3743
  my = 0,
3506
3744
  invertMask = false
3507
3745
  } = opts;
3508
- const clip = resolveRectClipping(targetX, targetY, width, height, dst.width, dst.height, SCRATCH_RECT3);
3509
- if (!clip.inBounds) return;
3746
+ const clip = resolveRectClipping(targetX, targetY, width, height, dst.width, dst.height, SCRATCH_RECT4);
3747
+ if (!clip.inBounds) return false;
3510
3748
  const {
3511
3749
  x,
3512
3750
  y,
@@ -3546,26 +3784,28 @@ function invertPixelData(pixelData, opts = {}) {
3546
3784
  dIdx += dStride;
3547
3785
  }
3548
3786
  }
3787
+ return true;
3549
3788
  }
3550
3789
 
3551
3790
  // src/History/PixelMutator/mutatorInvert.ts
3552
- var defaults18 = {
3791
+ var defaults20 = {
3553
3792
  invertPixelData
3554
3793
  };
3555
- var mutatorInvert = ((writer, deps = defaults18) => {
3794
+ var mutatorInvert = ((writer, deps = defaults20) => {
3556
3795
  const {
3557
- invertPixelData: invertPixelData2 = defaults18.invertPixelData
3796
+ invertPixelData: invertPixelData2 = defaults20.invertPixelData
3558
3797
  } = deps;
3559
3798
  return {
3560
3799
  invert(opts = {}) {
3800
+ const target = writer.config.target;
3561
3801
  const {
3562
3802
  x = 0,
3563
3803
  y = 0,
3564
- w = writer.target.width,
3565
- h = writer.target.height
3804
+ w = target.width,
3805
+ h = target.height
3566
3806
  } = opts;
3567
- writer.accumulator.storeRegionBeforeState(x, y, w, h);
3568
- invertPixelData2(writer.target, opts);
3807
+ const didChange = writer.accumulator.storeRegionBeforeState(x, y, w, h);
3808
+ return didChange(invertPixelData2(target, opts));
3569
3809
  }
3570
3810
  };
3571
3811
  });
@@ -3576,7 +3816,6 @@ function makeFullPixelMutator(writer) {
3576
3816
  // @sort
3577
3817
  ...mutatorApplyAlphaMask(writer),
3578
3818
  ...mutatorApplyBinaryMask(writer),
3579
- ...mutatorApplyCircleBrush(writer),
3580
3819
  ...mutatorApplyCircleBrushStroke(writer),
3581
3820
  ...mutatorApplyCirclePencil(writer),
3582
3821
  ...mutatorApplyCirclePencilStroke(writer),
@@ -3585,6 +3824,7 @@ function makeFullPixelMutator(writer) {
3585
3824
  ...mutatorApplyRectPencil(writer),
3586
3825
  ...mutatorApplyRectPencilStroke(writer),
3587
3826
  ...mutatorBlendColor(writer),
3827
+ ...mutatorBlendColorCircleMask(writer),
3588
3828
  ...mutatorBlendPixel(writer),
3589
3829
  ...mutatorBlendPixelData(writer),
3590
3830
  ...mutatorBlendPixelDataAlphaMask(writer),
@@ -3592,50 +3832,96 @@ function makeFullPixelMutator(writer) {
3592
3832
  ...mutatorClear(writer),
3593
3833
  ...mutatorFill(writer),
3594
3834
  ...mutatorFillBinaryMask(writer),
3835
+ ...mutatorFillRect(writer),
3595
3836
  ...mutatorInvert(writer)
3596
3837
  };
3597
3838
  }
3598
3839
 
3840
+ // src/PixelTile/PixelTile.ts
3841
+ var PixelTile = class {
3842
+ constructor(id, tx, ty, tileSize, tileArea) {
3843
+ this.id = id;
3844
+ this.tx = tx;
3845
+ this.ty = ty;
3846
+ this.width = this.height = tileSize;
3847
+ this.data32 = new Uint32Array(tileArea);
3848
+ const data8 = new Uint8ClampedArray(this.data32.buffer);
3849
+ this.imageData = new ImageData(data8, tileSize, tileSize);
3850
+ }
3851
+ data32;
3852
+ width;
3853
+ height;
3854
+ imageData;
3855
+ };
3856
+
3857
+ // src/PixelTile/PixelTilePool.ts
3858
+ var PixelTilePool = class {
3859
+ pool;
3860
+ tileSize;
3861
+ tileArea;
3862
+ constructor(config) {
3863
+ this.pool = [];
3864
+ this.tileSize = config.tileSize;
3865
+ this.tileArea = config.tileArea;
3866
+ }
3867
+ getTile(id, tx, ty) {
3868
+ let tile = this.pool.pop();
3869
+ if (tile) {
3870
+ tile.id = id;
3871
+ tile.tx = tx;
3872
+ tile.ty = ty;
3873
+ tile.data32.fill(0);
3874
+ return tile;
3875
+ }
3876
+ return new PixelTile(id, tx, ty, this.tileSize, this.tileArea);
3877
+ }
3878
+ releaseTile(tile) {
3879
+ this.pool.push(tile);
3880
+ }
3881
+ releaseTiles(tiles) {
3882
+ let length = tiles.length;
3883
+ for (let i = 0; i < length; i++) {
3884
+ let tile = tiles[i];
3885
+ if (tile) {
3886
+ this.pool.push(tile);
3887
+ }
3888
+ }
3889
+ tiles.length = 0;
3890
+ }
3891
+ };
3892
+
3599
3893
  // src/History/PixelWriter.ts
3600
3894
  var PixelWriter = class {
3601
- target;
3602
3895
  historyManager;
3603
3896
  accumulator;
3897
+ historyActionFactory;
3604
3898
  config;
3605
3899
  mutator;
3606
3900
  constructor(target, mutatorFactory, {
3607
3901
  tileSize = 256,
3608
3902
  maxHistorySteps = 50,
3609
- historyManager = new HistoryManager(maxHistorySteps)
3903
+ historyManager = new HistoryManager(maxHistorySteps),
3904
+ historyActionFactory = makeHistoryAction,
3905
+ pixelTilePool
3610
3906
  } = {}) {
3611
- this.target = target;
3612
- this.config = new PixelEngineConfig(tileSize);
3907
+ this.config = new PixelEngineConfig(tileSize, target);
3613
3908
  this.historyManager = historyManager;
3614
- this.accumulator = new PixelAccumulator(target, this.config);
3909
+ pixelTilePool ??= new PixelTilePool(this.config);
3910
+ this.accumulator = new PixelAccumulator(this.config, pixelTilePool);
3911
+ this.historyActionFactory = historyActionFactory;
3615
3912
  this.mutator = mutatorFactory(this);
3616
3913
  }
3617
- withHistory(cb) {
3618
- cb(this.mutator);
3619
- this.captureHistory();
3620
- }
3621
- captureHistory() {
3622
- const beforeTiles = this.accumulator.beforeTiles;
3623
- if (beforeTiles.length === 0) return;
3624
- const afterTiles = this.accumulator.extractAfterTiles();
3625
- const patch = {
3626
- beforeTiles,
3627
- afterTiles
3628
- };
3629
- const target = this.target;
3630
- const tileSize = this.config.tileSize;
3631
- const accumulator = this.accumulator;
3632
- const action = {
3633
- undo: () => applyPatchTiles(target, patch.beforeTiles, tileSize),
3634
- redo: () => applyPatchTiles(target, patch.afterTiles, tileSize),
3635
- dispose: () => accumulator.recyclePatch(patch)
3636
- };
3914
+ withHistory(cb, after, afterUndo, afterRedo) {
3915
+ try {
3916
+ cb(this.mutator);
3917
+ } catch (e) {
3918
+ this.accumulator.rollback();
3919
+ throw e;
3920
+ }
3921
+ if (this.accumulator.beforeTiles.length === 0) return;
3922
+ const patch = this.accumulator.extractPatch();
3923
+ const action = this.historyActionFactory(this, patch, after, afterUndo, afterRedo);
3637
3924
  this.historyManager.commit(action);
3638
- this.accumulator.reset();
3639
3925
  }
3640
3926
  };
3641
3927
 
@@ -3793,11 +4079,10 @@ function resizeImageData(target, newWidth, newHeight, offsetX = 0, offsetY = 0)
3793
4079
  function makeReusableImageData() {
3794
4080
  let imageData = null;
3795
4081
  return function getReusableImageData(width, height) {
3796
- const hasInstance = !!imageData;
3797
- const widthMatches = hasInstance && imageData.width === width;
3798
- const heightMatches = hasInstance && imageData.height === height;
3799
- if (!widthMatches || !heightMatches) {
4082
+ if (imageData === null || imageData.width !== width || imageData.height !== height) {
3800
4083
  imageData = new ImageData(width, height);
4084
+ } else {
4085
+ imageData.data.fill(0);
3801
4086
  }
3802
4087
  return imageData;
3803
4088
  };
@@ -4204,8 +4489,8 @@ function makeBinaryMask(w, h, data) {
4204
4489
  };
4205
4490
  }
4206
4491
 
4207
- // src/Mask/CircleBrushAlphaMask.ts
4208
- function makeCircleBrushAlphaMask(size, fallOff = () => 1) {
4492
+ // src/Mask/CircleAlphaMask.ts
4493
+ function makeCircleAlphaMask(size, fallOff = () => 1) {
4209
4494
  const area = size * size;
4210
4495
  const data = new Uint8Array(area);
4211
4496
  const radius = size / 2;
@@ -4233,8 +4518,8 @@ function makeCircleBrushAlphaMask(size, fallOff = () => 1) {
4233
4518
  };
4234
4519
  }
4235
4520
 
4236
- // src/Mask/CircleBrushBinaryMask.ts
4237
- function makeCircleBrushBinaryMask(size) {
4521
+ // src/Mask/CircleBinaryMask.ts
4522
+ function makeCircleBinaryMask(size) {
4238
4523
  const area = size * size;
4239
4524
  const data = new Uint8Array(area);
4240
4525
  const radius = size / 2;
@@ -4530,57 +4815,6 @@ function setMaskData(mask, width, height, data) {
4530
4815
  mask.data = data;
4531
4816
  }
4532
4817
 
4533
- // src/MaskRect/subtractBinaryMaskRects.ts
4534
- function subtractBinaryMaskRects(current, subtracting) {
4535
- let result = [...current];
4536
- for (const sub of subtracting) {
4537
- const next = [];
4538
- for (const r of result) {
4539
- const ix = Math.max(r.x, sub.x);
4540
- const iy = Math.max(r.y, sub.y);
4541
- const ix2 = Math.min(r.x + r.w, sub.x + sub.w);
4542
- const iy2 = Math.min(r.y + r.h, sub.y + sub.h);
4543
- if (ix >= ix2 || iy >= iy2) {
4544
- next.push(r);
4545
- continue;
4546
- }
4547
- if (r.y < iy) pushPiece(next, r, r.x, r.y, r.w, iy - r.y);
4548
- if (iy2 < r.y + r.h) pushPiece(next, r, r.x, iy2, r.w, r.y + r.h - iy2);
4549
- if (r.x < ix) pushPiece(next, r, r.x, iy, ix - r.x, iy2 - iy);
4550
- if (ix2 < r.x + r.w) pushPiece(next, r, ix2, iy, r.x + r.w - ix2, iy2 - iy);
4551
- }
4552
- result = next;
4553
- }
4554
- return result;
4555
- }
4556
- function pushPiece(dest, r, x, y, w, h) {
4557
- if (r.data === null || r.data === void 0) {
4558
- dest.push({
4559
- x,
4560
- y,
4561
- w,
4562
- h,
4563
- data: null,
4564
- type: null
4565
- });
4566
- return;
4567
- }
4568
- const lx = x - r.x;
4569
- const ly = y - r.y;
4570
- const data = new Uint8Array(w * h);
4571
- for (let row = 0; row < h; row++) {
4572
- data.set(r.data.subarray((ly + row) * r.w + lx, (ly + row) * r.w + lx + w), row * w);
4573
- }
4574
- dest.push({
4575
- x,
4576
- y,
4577
- w,
4578
- h,
4579
- data,
4580
- type: 1 /* BINARY */
4581
- });
4582
- }
4583
-
4584
4818
  // src/Rect/getRectsBounds.ts
4585
4819
  function getRectsBounds(rects) {
4586
4820
  if (rects.length === 1) return {
@@ -4693,8 +4927,59 @@ function mergeBinaryMaskRects(current, adding) {
4693
4927
  return rects;
4694
4928
  }
4695
4929
 
4930
+ // src/MaskRect/subtractBinaryMaskRects.ts
4931
+ function subtractBinaryMaskRects(current, subtracting) {
4932
+ let result = [...current];
4933
+ for (const sub of subtracting) {
4934
+ const next = [];
4935
+ for (const r of result) {
4936
+ const ix = Math.max(r.x, sub.x);
4937
+ const iy = Math.max(r.y, sub.y);
4938
+ const ix2 = Math.min(r.x + r.w, sub.x + sub.w);
4939
+ const iy2 = Math.min(r.y + r.h, sub.y + sub.h);
4940
+ if (ix >= ix2 || iy >= iy2) {
4941
+ next.push(r);
4942
+ continue;
4943
+ }
4944
+ if (r.y < iy) pushPiece(next, r, r.x, r.y, r.w, iy - r.y);
4945
+ if (iy2 < r.y + r.h) pushPiece(next, r, r.x, iy2, r.w, r.y + r.h - iy2);
4946
+ if (r.x < ix) pushPiece(next, r, r.x, iy, ix - r.x, iy2 - iy);
4947
+ if (ix2 < r.x + r.w) pushPiece(next, r, ix2, iy, r.x + r.w - ix2, iy2 - iy);
4948
+ }
4949
+ result = next;
4950
+ }
4951
+ return result;
4952
+ }
4953
+ function pushPiece(dest, r, x, y, w, h) {
4954
+ if (r.data === null || r.data === void 0) {
4955
+ dest.push({
4956
+ x,
4957
+ y,
4958
+ w,
4959
+ h,
4960
+ data: null,
4961
+ type: null
4962
+ });
4963
+ return;
4964
+ }
4965
+ const lx = x - r.x;
4966
+ const ly = y - r.y;
4967
+ const data = new Uint8Array(w * h);
4968
+ for (let row = 0; row < h; row++) {
4969
+ data.set(r.data.subarray((ly + row) * r.w + lx, (ly + row) * r.w + lx + w), row * w);
4970
+ }
4971
+ dest.push({
4972
+ x,
4973
+ y,
4974
+ w,
4975
+ h,
4976
+ data,
4977
+ type: 1 /* BINARY */
4978
+ });
4979
+ }
4980
+
4696
4981
  // src/PixelData/PixelData.ts
4697
- var PixelData = class _PixelData {
4982
+ var PixelData = class {
4698
4983
  data32;
4699
4984
  imageData;
4700
4985
  width;
@@ -4712,30 +4997,35 @@ var PixelData = class _PixelData {
4712
4997
  this.width = imageData.width;
4713
4998
  this.height = imageData.height;
4714
4999
  }
4715
- // should only be used for debug and testing
4716
- copy() {
4717
- const data = this.imageData.data;
4718
- const buffer = new Uint8ClampedArray(data);
4719
- const Ctor = this.imageData.constructor;
4720
- const isCtorValid = typeof Ctor === "function";
4721
- let newImageData;
4722
- if (isCtorValid && Ctor !== Object) {
4723
- const ImageConstructor = Ctor;
4724
- newImageData = new ImageConstructor(buffer, this.width, this.height);
4725
- } else {
4726
- newImageData = {
4727
- width: this.width,
4728
- height: this.height,
4729
- data: buffer
4730
- };
5000
+ };
5001
+
5002
+ // src/PixelData/blendPixelDataPaintBuffer.ts
5003
+ var SCRATCH_OPTS = {
5004
+ x: 0,
5005
+ y: 0,
5006
+ alpha: 255,
5007
+ blendFn: void 0
5008
+ };
5009
+ function blendPixelDataPaintBuffer(paintBuffer, target, alpha = 255, blendFn, blendPixelDataFn = blendPixelData) {
5010
+ const tileShift = paintBuffer.config.tileShift;
5011
+ const lookup = paintBuffer.lookup;
5012
+ for (let i = 0; i < lookup.length; i++) {
5013
+ const tile = lookup[i];
5014
+ if (tile) {
5015
+ const x = tile.tx << tileShift;
5016
+ const y = tile.ty << tileShift;
5017
+ SCRATCH_OPTS.x = x;
5018
+ SCRATCH_OPTS.y = y;
5019
+ SCRATCH_OPTS.alpha = alpha;
5020
+ SCRATCH_OPTS.blendFn = blendFn;
5021
+ blendPixelDataFn(target, tile, SCRATCH_OPTS);
4731
5022
  }
4732
- return new _PixelData(newImageData);
4733
5023
  }
4734
- };
5024
+ }
4735
5025
 
4736
5026
  // src/PixelData/clearPixelData.ts
4737
5027
  function clearPixelData(dst, rect) {
4738
- fillPixelData(dst, 0, rect);
5028
+ fillPixelDataFast(dst, 0, rect);
4739
5029
  }
4740
5030
 
4741
5031
  // src/PixelData/extractPixelDataBuffer.ts
@@ -4954,6 +5244,124 @@ function writePixelDataBuffer(target, data, _x, _y, _w, _h) {
4954
5244
  dstData.set(data.subarray(srcStart, srcStart + copyW), dstStart);
4955
5245
  }
4956
5246
  }
5247
+
5248
+ // src/PixelTile/PaintBuffer.ts
5249
+ var PaintBuffer = class {
5250
+ constructor(config, tilePool) {
5251
+ this.config = config;
5252
+ this.tilePool = tilePool;
5253
+ this.lookup = [];
5254
+ }
5255
+ lookup;
5256
+ processMaskTiles(mask, callback) {
5257
+ const {
5258
+ tileShift,
5259
+ targetColumns
5260
+ } = this.config;
5261
+ const x1 = mask.x >> tileShift;
5262
+ const y1 = mask.y >> tileShift;
5263
+ const x2 = mask.x + mask.w - 1 >> tileShift;
5264
+ const y2 = mask.y + mask.h - 1 >> tileShift;
5265
+ for (let ty = y1; ty <= y2; ty++) {
5266
+ const tileRowIndex = ty * targetColumns;
5267
+ const tileTop = ty << tileShift;
5268
+ for (let tx = x1; tx <= x2; tx++) {
5269
+ const id = tileRowIndex + tx;
5270
+ let tile = this.lookup[id];
5271
+ if (!tile) {
5272
+ tile = this.tilePool.getTile(id, tx, ty);
5273
+ this.lookup[id] = tile;
5274
+ }
5275
+ const tileLeft = tx << tileShift;
5276
+ const startX = Math.max(mask.x, tileLeft);
5277
+ const endX = Math.min(mask.x + mask.w, tileLeft + this.config.tileSize);
5278
+ const startY = Math.max(mask.y, tileTop);
5279
+ const endY = Math.min(mask.y + mask.h, tileTop + this.config.tileSize);
5280
+ callback(tile, startX, startY, endX - startX, endY - startY, startX - mask.x, startY - mask.y);
5281
+ }
5282
+ }
5283
+ }
5284
+ writeColorBinaryMaskRect(color, mask) {
5285
+ const {
5286
+ tileShift,
5287
+ tileMask
5288
+ } = this.config;
5289
+ const maskData = mask.data;
5290
+ const maskW = mask.w;
5291
+ this.processMaskTiles(mask, (tile, bX, bY, bW, bH, mX, mY) => {
5292
+ const data32 = tile.data32;
5293
+ const startTileX = bX & tileMask;
5294
+ for (let i = 0; i < bH; i++) {
5295
+ const tileY = bY + i & tileMask;
5296
+ const maskY = mY + i;
5297
+ const tileRowOffset = tileY << tileShift;
5298
+ const maskRowOffset = maskY * maskW;
5299
+ const destStart = tileRowOffset + startTileX;
5300
+ const maskStart = maskRowOffset + mX;
5301
+ for (let j = 0; j < bW; j++) {
5302
+ if (maskData[maskStart + j]) {
5303
+ data32[destStart + j] = color;
5304
+ }
5305
+ }
5306
+ }
5307
+ });
5308
+ }
5309
+ writeColorAlphaMaskRect(color, mask) {
5310
+ const {
5311
+ tileShift,
5312
+ tileMask
5313
+ } = this.config;
5314
+ const maskData = mask.data;
5315
+ const maskW = mask.w;
5316
+ const colorRGB = color & 16777215;
5317
+ const colorA = color >>> 24;
5318
+ this.processMaskTiles(mask, (tile, bX, bY, bW, bH, mX, mY) => {
5319
+ const data32 = tile.data32;
5320
+ const startTileX = bX & tileMask;
5321
+ for (let i = 0; i < bH; i++) {
5322
+ const tileY = bY + i & tileMask;
5323
+ const maskY = mY + i;
5324
+ const tileRowOffset = tileY << tileShift;
5325
+ const maskRowOffset = maskY * maskW;
5326
+ const destStart = tileRowOffset + startTileX;
5327
+ const maskStart = maskRowOffset + mX;
5328
+ for (let j = 0; j < bW; j++) {
5329
+ const maskA = maskData[maskStart + j];
5330
+ if (maskA > 0) {
5331
+ const finalA = colorA * maskA + 128 >> 8;
5332
+ data32[destStart + j] = (colorRGB | finalA << 24) >>> 0;
5333
+ }
5334
+ }
5335
+ }
5336
+ });
5337
+ }
5338
+ clear() {
5339
+ this.tilePool.releaseTiles(this.lookup);
5340
+ }
5341
+ };
5342
+
5343
+ // src/PixelTile/PaintBufferRenderer.ts
5344
+ function makePaintBufferRenderer(paintBuffer, offscreenCanvasClass = OffscreenCanvas) {
5345
+ const config = paintBuffer.config;
5346
+ const tileSize = config.tileSize;
5347
+ const tileShift = config.tileShift;
5348
+ const lookup = paintBuffer.lookup;
5349
+ const canvas = new offscreenCanvasClass(tileSize, tileSize);
5350
+ const ctx = canvas.getContext("2d");
5351
+ if (!ctx) throw new Error(CANVAS_CTX_FAILED);
5352
+ ctx.imageSmoothingEnabled = false;
5353
+ return function drawPaintBuffer(target) {
5354
+ for (let i = 0; i < lookup.length; i++) {
5355
+ const tile = lookup[i];
5356
+ if (tile) {
5357
+ const dx = tile.tx << tileShift;
5358
+ const dy = tile.ty << tileShift;
5359
+ ctx.putImageData(tile.imageData, 0, 0);
5360
+ target.drawImage(canvas, dx, dy);
5361
+ }
5362
+ }
5363
+ };
5364
+ }
4957
5365
  export {
4958
5366
  BASE_FAST_BLEND_MODE_FUNCTIONS,
4959
5367
  BASE_PERFECT_BLEND_MODE_FUNCTIONS,
@@ -4963,17 +5371,18 @@ export {
4963
5371
  IndexedImage,
4964
5372
  MaskType,
4965
5373
  OFFSCREEN_CANVAS_CTX_FAILED,
5374
+ PaintBuffer,
4966
5375
  PixelAccumulator,
4967
5376
  PixelBuffer32,
4968
5377
  PixelData,
4969
5378
  PixelEngineConfig,
4970
5379
  PixelTile,
5380
+ PixelTilePool,
4971
5381
  PixelWriter,
4972
5382
  UnsupportedFormatError,
4973
5383
  applyAlphaMaskToPixelData,
4974
5384
  applyBinaryMaskToAlphaMask,
4975
5385
  applyBinaryMaskToPixelData,
4976
- applyCircleBrushToPixelData,
4977
5386
  applyPatchTiles,
4978
5387
  applyRectBrushToPixelData,
4979
5388
  base64DecodeArrayBuffer,
@@ -4981,9 +5390,12 @@ export {
4981
5390
  blendColorPixelData,
4982
5391
  blendColorPixelDataAlphaMask,
4983
5392
  blendColorPixelDataBinaryMask,
5393
+ blendColorPixelDataCircleMask,
5394
+ blendPixel,
4984
5395
  blendPixelData,
4985
5396
  blendPixelDataAlphaMask,
4986
5397
  blendPixelDataBinaryMask,
5398
+ blendPixelDataPaintBuffer,
4987
5399
  clearPixelData,
4988
5400
  color32ToCssRGBA,
4989
5401
  color32ToHex,
@@ -5017,6 +5429,7 @@ export {
5017
5429
  fileToImageData,
5018
5430
  fillPixelData,
5019
5431
  fillPixelDataBinaryMask,
5432
+ fillPixelDataFast,
5020
5433
  floodFillSelection,
5021
5434
  forEachLinePoint,
5022
5435
  getCircleBrushOrPencilBounds,
@@ -5057,15 +5470,19 @@ export {
5057
5470
  makeAlphaMask,
5058
5471
  makeBinaryMask,
5059
5472
  makeBlendModeRegistry,
5060
- makeCircleBrushAlphaMask,
5061
- makeCircleBrushBinaryMask,
5473
+ makeCanvasFrameRenderer,
5474
+ makeCircleAlphaMask,
5475
+ makeCircleBinaryMask,
5062
5476
  makeFastBlendModeRegistry,
5063
5477
  makeFullPixelMutator,
5478
+ makeHistoryAction,
5064
5479
  makeImageDataLike,
5480
+ makePaintBufferRenderer,
5065
5481
  makePerfectBlendModeRegistry,
5066
5482
  makePixelCanvas,
5067
5483
  makeReusableCanvas,
5068
5484
  makeReusableImageData,
5485
+ makeReusableOffscreenCanvas,
5069
5486
  merge2BinaryMaskRects,
5070
5487
  mergeAlphaMasks,
5071
5488
  mergeBinaryMaskRects,
@@ -5074,7 +5491,6 @@ export {
5074
5491
  multiplyPerfect,
5075
5492
  mutatorApplyAlphaMask,
5076
5493
  mutatorApplyBinaryMask,
5077
- mutatorApplyCircleBrush,
5078
5494
  mutatorApplyCircleBrushStroke,
5079
5495
  mutatorApplyCirclePencil,
5080
5496
  mutatorApplyCirclePencilStroke,
@@ -5083,6 +5499,7 @@ export {
5083
5499
  mutatorApplyRectPencil,
5084
5500
  mutatorApplyRectPencilStroke,
5085
5501
  mutatorBlendColor,
5502
+ mutatorBlendColorCircleMask,
5086
5503
  mutatorBlendPixel,
5087
5504
  mutatorBlendPixelData,
5088
5505
  mutatorBlendPixelDataAlphaMask,
@@ -5090,6 +5507,7 @@ export {
5090
5507
  mutatorClear,
5091
5508
  mutatorFill,
5092
5509
  mutatorFillBinaryMask,
5510
+ mutatorFillRect,
5093
5511
  mutatorInvert,
5094
5512
  overlayFast,
5095
5513
  overlayPerfect,