pixel-data-js 0.24.0 → 0.25.2

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 (72) hide show
  1. package/dist/index.dev.cjs +1476 -1834
  2. package/dist/index.dev.cjs.map +1 -1
  3. package/dist/index.dev.js +1465 -1816
  4. package/dist/index.dev.js.map +1 -1
  5. package/dist/index.prod.cjs +1475 -1833
  6. package/dist/index.prod.cjs.map +1 -1
  7. package/dist/index.prod.d.ts +233 -310
  8. package/dist/index.prod.js +1465 -1816
  9. package/dist/index.prod.js.map +1 -1
  10. package/package.json +1 -1
  11. package/src/Algorithm/floodFillSelection.ts +2 -2
  12. package/src/Canvas/canvas-blend-modes.ts +28 -0
  13. package/src/History/PixelAccumulator.ts +52 -29
  14. package/src/History/PixelEngineConfig.ts +7 -9
  15. package/src/History/PixelMutator/mutatorBlendPaintMask.ts +60 -0
  16. package/src/History/PixelMutator/mutatorBlendPixelData.ts +2 -2
  17. package/src/History/PixelMutator/mutatorBlendPixelDataAlphaMask.ts +2 -2
  18. package/src/History/PixelMutator/mutatorBlendPixelDataBinaryMask.ts +2 -2
  19. package/src/History/PixelMutator.ts +0 -20
  20. package/src/History/PixelPatchTiles.ts +2 -2
  21. package/src/History/PixelWriter.ts +132 -9
  22. package/src/Internal/helpers.ts +2 -0
  23. package/src/Paint/PaintBuffer.ts +269 -0
  24. package/src/{PixelTile/PaintBufferRenderer.ts → Paint/PaintBufferCanvasRenderer.ts} +13 -5
  25. package/src/Paint/makeCirclePaintAlphaMask.ts +41 -0
  26. package/src/{Mask/CircleBinaryMask.ts → Paint/makeCirclePaintBinaryMask.ts} +5 -6
  27. package/src/Paint/makePaintMask.ts +28 -0
  28. package/src/Paint/makeRectFalloffPaintAlphaMask.ts +47 -0
  29. package/src/PixelData/PixelBuffer32.ts +2 -2
  30. package/src/PixelData/PixelData.ts +1 -1
  31. package/src/PixelData/applyAlphaMaskToPixelData.ts +2 -2
  32. package/src/PixelData/applyBinaryMaskToPixelData.ts +2 -2
  33. package/src/PixelData/blendColorPixelData.ts +2 -2
  34. package/src/PixelData/blendColorPixelDataAlphaMask.ts +3 -3
  35. package/src/PixelData/blendColorPixelDataBinaryMask.ts +3 -3
  36. package/src/PixelData/blendPixel.ts +2 -2
  37. package/src/PixelData/blendPixelData.ts +3 -3
  38. package/src/PixelData/blendPixelDataAlphaMask.ts +3 -3
  39. package/src/PixelData/blendPixelDataBinaryMask.ts +3 -3
  40. package/src/PixelData/blendPixelDataPaintBuffer.ts +3 -3
  41. package/src/PixelData/clearPixelData.ts +2 -2
  42. package/src/PixelData/extractPixelData.ts +4 -4
  43. package/src/PixelData/extractPixelDataBuffer.ts +4 -4
  44. package/src/PixelData/fillPixelData.ts +5 -5
  45. package/src/PixelData/fillPixelDataBinaryMask.ts +3 -3
  46. package/src/PixelData/fillPixelDataFast.ts +5 -5
  47. package/src/PixelData/invertPixelData.ts +2 -2
  48. package/src/PixelData/pixelDataToAlphaMask.ts +2 -2
  49. package/src/PixelData/reflectPixelData.ts +3 -3
  50. package/src/PixelData/resamplePixelData.ts +2 -2
  51. package/src/PixelData/writePaintBufferToPixelData.ts +26 -0
  52. package/src/PixelData/writePixelDataBuffer.ts +5 -5
  53. package/src/Rect/trimMaskRectBounds.ts +121 -0
  54. package/src/Rect/trimRectBounds.ts +25 -116
  55. package/src/_types.ts +16 -15
  56. package/src/index.ts +11 -24
  57. package/src/History/PixelMutator/mutatorApplyCircleBrushStroke.ts +0 -182
  58. package/src/History/PixelMutator/mutatorApplyCirclePencil.ts +0 -59
  59. package/src/History/PixelMutator/mutatorApplyCirclePencilStroke.ts +0 -172
  60. package/src/History/PixelMutator/mutatorApplyRectBrush.ts +0 -64
  61. package/src/History/PixelMutator/mutatorApplyRectBrushStroke.ts +0 -184
  62. package/src/History/PixelMutator/mutatorApplyRectPencil.ts +0 -65
  63. package/src/History/PixelMutator/mutatorApplyRectPencilStroke.ts +0 -166
  64. package/src/History/PixelMutator/mutatorBlendColorCircleMask.ts +0 -71
  65. package/src/Mask/CircleAlphaMask.ts +0 -32
  66. package/src/PixelData/applyRectBrushToPixelData.ts +0 -98
  67. package/src/PixelData/blendColorPixelDataCircleMask.ts +0 -92
  68. package/src/PixelTile/PaintBuffer.ts +0 -122
  69. package/src/Rect/getCircleBrushOrPencilBounds.ts +0 -43
  70. package/src/Rect/getCircleBrushOrPencilStrokeBounds.ts +0 -24
  71. package/src/Rect/getRectBrushOrPencilBounds.ts +0 -38
  72. package/src/Rect/getRectBrushOrPencilStrokeBounds.ts +0 -26
@@ -232,8 +232,8 @@ function extractMaskBuffer(maskBuffer, maskWidth, xOrRect, y, w, h) {
232
232
  return out;
233
233
  }
234
234
 
235
- // src/Rect/trimRectBounds.ts
236
- function trimRectBounds(target, bounds) {
235
+ // src/Rect/trimMaskRectBounds.ts
236
+ function trimMaskRectBounds(target, bounds) {
237
237
  const originalX = target.x;
238
238
  const originalY = target.y;
239
239
  const originalW = target.w;
@@ -421,7 +421,7 @@ function floodFillSelection(img, startX, startY, {
421
421
  finalMask[my * sw + mx] = 1;
422
422
  }
423
423
  }
424
- trimRectBounds(selectionRect, {
424
+ trimMaskRectBounds(selectionRect, {
425
425
  x: 0,
426
426
  y: 0,
427
427
  w: width,
@@ -1533,6 +1533,24 @@ var getKeyByValue = (obj, value) => {
1533
1533
  var OFFSCREEN_CANVAS_CTX_FAILED = "Failed to create OffscreenCanvas context";
1534
1534
  var CANVAS_CTX_FAILED = "Failed to create Canvas context";
1535
1535
 
1536
+ // src/Canvas/canvas-blend-modes.ts
1537
+ var CANVAS_COMPOSITE_MAP = {
1538
+ [BaseBlendMode.overwrite]: "copy",
1539
+ [BaseBlendMode.sourceOver]: "source-over",
1540
+ [BaseBlendMode.darken]: "darken",
1541
+ [BaseBlendMode.multiply]: "multiply",
1542
+ [BaseBlendMode.colorBurn]: "color-burn",
1543
+ [BaseBlendMode.lighten]: "lighten",
1544
+ [BaseBlendMode.screen]: "screen",
1545
+ [BaseBlendMode.colorDodge]: "color-dodge",
1546
+ [BaseBlendMode.linearDodge]: "lighter",
1547
+ [BaseBlendMode.overlay]: "overlay",
1548
+ [BaseBlendMode.softLight]: "soft-light",
1549
+ [BaseBlendMode.hardLight]: "hard-light",
1550
+ [BaseBlendMode.difference]: "difference",
1551
+ [BaseBlendMode.exclusion]: "exclusion"
1552
+ };
1553
+
1536
1554
  // src/Canvas/ReusableCanvas.ts
1537
1555
  function makeReusableCanvas() {
1538
1556
  return makeReusableCanvasMeta((w, h) => {
@@ -1811,11 +1829,11 @@ var PixelAccumulator = class {
1811
1829
  * @param y pixel y coordinate
1812
1830
  */
1813
1831
  storePixelBeforeState(x, y) {
1814
- let shift = this.config.tileShift;
1815
- let columns = this.config.targetColumns;
1816
- let tx = x >> shift;
1817
- let ty = y >> shift;
1818
- let id = ty * columns + tx;
1832
+ const shift = this.config.tileShift;
1833
+ const columns = this.config.targetColumns;
1834
+ const tx = x >> shift;
1835
+ const ty = y >> shift;
1836
+ const id = ty * columns + tx;
1819
1837
  let tile = this.lookup[id];
1820
1838
  let added = false;
1821
1839
  if (!tile) {
@@ -1841,16 +1859,16 @@ var PixelAccumulator = class {
1841
1859
  * @param h pixel height
1842
1860
  */
1843
1861
  storeRegionBeforeState(x, y, w, h) {
1844
- let shift = this.config.tileShift;
1845
- let columns = this.config.targetColumns;
1846
- let startX = x >> shift;
1847
- let startY = y >> shift;
1848
- let endX = x + w - 1 >> shift;
1849
- let endY = y + h - 1 >> shift;
1850
- let startIndex = this.beforeTiles.length;
1862
+ const shift = this.config.tileShift;
1863
+ const columns = this.config.targetColumns;
1864
+ const startX = x >> shift;
1865
+ const startY = y >> shift;
1866
+ const endX = x + w - 1 >> shift;
1867
+ const endY = y + h - 1 >> shift;
1868
+ const startIndex = this.beforeTiles.length;
1851
1869
  for (let ty = startY; ty <= endY; ty++) {
1852
1870
  for (let tx = startX; tx <= endX; tx++) {
1853
- let id = ty * columns + tx;
1871
+ const id = ty * columns + tx;
1854
1872
  let tile = this.lookup[id];
1855
1873
  if (!tile) {
1856
1874
  tile = this.tilePool.getTile(id, tx, ty);
@@ -1862,7 +1880,7 @@ var PixelAccumulator = class {
1862
1880
  }
1863
1881
  return (didChange) => {
1864
1882
  if (!didChange) {
1865
- let length = this.beforeTiles.length;
1883
+ const length = this.beforeTiles.length;
1866
1884
  for (let i = startIndex; i < length; i++) {
1867
1885
  let t = this.beforeTiles[i];
1868
1886
  if (t) {
@@ -1875,15 +1893,34 @@ var PixelAccumulator = class {
1875
1893
  return didChange;
1876
1894
  };
1877
1895
  }
1896
+ storeTileBeforeState(id, tx, ty) {
1897
+ let tile = this.lookup[id];
1898
+ let added = false;
1899
+ if (!tile) {
1900
+ tile = this.tilePool.getTile(id, tx, ty);
1901
+ this.extractState(tile);
1902
+ this.lookup[id] = tile;
1903
+ this.beforeTiles.push(tile);
1904
+ added = true;
1905
+ }
1906
+ return (didChange) => {
1907
+ if (!didChange && added) {
1908
+ this.beforeTiles.pop();
1909
+ this.lookup[id] = void 0;
1910
+ this.tilePool.releaseTile(tile);
1911
+ }
1912
+ return didChange;
1913
+ };
1914
+ }
1878
1915
  extractState(tile) {
1879
- let target = this.config.target;
1880
- let TILE_SIZE = this.config.tileSize;
1881
- let dst = tile.data32;
1882
- let src = target.data32;
1883
- let startX = tile.tx * TILE_SIZE;
1884
- let startY = tile.ty * TILE_SIZE;
1885
- let targetWidth = target.width;
1886
- let targetHeight = target.height;
1916
+ const target = this.config.target;
1917
+ const TILE_SIZE = this.config.tileSize;
1918
+ const dst = tile.data32;
1919
+ const src = target.data32;
1920
+ const startX = tile.tx * TILE_SIZE;
1921
+ const startY = tile.ty * TILE_SIZE;
1922
+ const targetWidth = target.width;
1923
+ const targetHeight = target.height;
1887
1924
  if (startX >= targetWidth || startX + TILE_SIZE <= 0 || startY >= targetHeight || startY + TILE_SIZE <= 0) {
1888
1925
  dst.fill(0);
1889
1926
  return;
@@ -1909,8 +1946,8 @@ var PixelAccumulator = class {
1909
1946
  }
1910
1947
  }
1911
1948
  extractPatch() {
1912
- let afterTiles = [];
1913
- let length = this.beforeTiles.length;
1949
+ const afterTiles = [];
1950
+ const length = this.beforeTiles.length;
1914
1951
  for (let i = 0; i < length; i++) {
1915
1952
  let beforeTile = this.beforeTiles[i];
1916
1953
  if (beforeTile) {
@@ -1919,7 +1956,7 @@ var PixelAccumulator = class {
1919
1956
  afterTiles.push(afterTile);
1920
1957
  }
1921
1958
  }
1922
- let beforeTiles = this.beforeTiles;
1959
+ const beforeTiles = this.beforeTiles;
1923
1960
  this.beforeTiles = [];
1924
1961
  this.lookup.length = 0;
1925
1962
  return {
@@ -1927,10 +1964,10 @@ var PixelAccumulator = class {
1927
1964
  afterTiles
1928
1965
  };
1929
1966
  }
1930
- rollback() {
1931
- let target = this.config.target;
1932
- let tileSize = this.config.tileSize;
1933
- let length = this.beforeTiles.length;
1967
+ rollbackAfterError() {
1968
+ const target = this.config.target;
1969
+ const tileSize = this.config.tileSize;
1970
+ const length = this.beforeTiles.length;
1934
1971
  applyPatchTiles(target, this.beforeTiles, tileSize);
1935
1972
  for (let i = 0; i < length; i++) {
1936
1973
  let tile = this.beforeTiles[i];
@@ -1954,6 +1991,7 @@ var PixelEngineConfig = class {
1954
1991
  tileArea;
1955
1992
  target;
1956
1993
  targetColumns = 0;
1994
+ targetRows = 0;
1957
1995
  constructor(tileSize, target) {
1958
1996
  if ((tileSize & tileSize - 1) !== 0) {
1959
1997
  throw new Error("tileSize must be a power of 2");
@@ -1962,28 +2000,26 @@ var PixelEngineConfig = class {
1962
2000
  this.tileShift = 31 - Math.clz32(tileSize);
1963
2001
  this.tileMask = tileSize - 1;
1964
2002
  this.tileArea = tileSize * tileSize;
1965
- this.setTarget(target);
1966
- }
1967
- setTarget(target) {
1968
- ;
1969
2003
  this.target = target;
1970
2004
  this.targetColumns = target.width + this.tileMask >> this.tileShift;
2005
+ this.targetRows = target.height + this.tileMask >> this.tileShift;
1971
2006
  }
1972
2007
  };
1973
2008
 
1974
- // src/PixelData/applyAlphaMaskToPixelData.ts
1975
- function applyAlphaMaskToPixelData(dst, mask, opts = {}) {
2009
+ // src/PixelData/blendColorPixelData.ts
2010
+ function blendColorPixelData(dst, color, opts = {}) {
1976
2011
  const {
1977
2012
  x: targetX = 0,
1978
2013
  y: targetY = 0,
1979
2014
  w: width = dst.width,
1980
2015
  h: height = dst.height,
1981
2016
  alpha: globalAlpha = 255,
1982
- mx = 0,
1983
- my = 0,
1984
- invertMask = false
2017
+ blendFn = sourceOverPerfect
1985
2018
  } = opts;
1986
2019
  if (globalAlpha === 0) return false;
2020
+ const baseSrcAlpha = color >>> 24;
2021
+ const isOverwrite = blendFn.isOverwrite || false;
2022
+ if (baseSrcAlpha === 0 && !isOverwrite) return false;
1987
2023
  let x = targetX;
1988
2024
  let y = targetY;
1989
2025
  let w = width;
@@ -1996,82 +2032,46 @@ function applyAlphaMaskToPixelData(dst, mask, opts = {}) {
1996
2032
  h += y;
1997
2033
  y = 0;
1998
2034
  }
1999
- w = Math.min(w, dst.width - x);
2000
- h = Math.min(h, dst.height - y);
2001
- if (w <= 0) return false;
2002
- if (h <= 0) return false;
2003
- const mPitch = mask.w;
2004
- if (mPitch <= 0) return false;
2005
- const startX = mx + (x - targetX);
2006
- const startY = my + (y - targetY);
2007
- const sX0 = Math.max(0, startX);
2008
- const sY0 = Math.max(0, startY);
2009
- const sX1 = Math.min(mPitch, startX + w);
2010
- const sY1 = Math.min(mask.h, startY + h);
2011
- const finalW = sX1 - sX0;
2012
- const finalH = sY1 - sY0;
2013
- if (finalW <= 0) return false;
2014
- if (finalH <= 0) return false;
2015
- const xShift = sX0 - startX;
2016
- const yShift = sY0 - startY;
2035
+ const actualW = Math.min(w, dst.width - x);
2036
+ const actualH = Math.min(h, dst.height - y);
2037
+ if (actualW <= 0 || actualH <= 0) return false;
2038
+ let finalSrcColor = color;
2039
+ if (globalAlpha < 255) {
2040
+ const a = baseSrcAlpha * globalAlpha + 128 >> 8;
2041
+ if (a === 0 && !isOverwrite) return false;
2042
+ finalSrcColor = (color & 16777215 | a << 24) >>> 0;
2043
+ }
2017
2044
  const dst32 = dst.data32;
2018
2045
  const dw = dst.width;
2019
- const dStride = dw - finalW;
2020
- const mStride = mPitch - finalW;
2021
- const maskData = mask.data;
2022
- let dIdx = (y + yShift) * dw + (x + xShift);
2023
- let mIdx = sY0 * mPitch + sX0;
2046
+ let dIdx = y * dw + x | 0;
2047
+ const dStride = dw - actualW | 0;
2024
2048
  let didChange = false;
2025
- for (let iy = 0; iy < h; iy++) {
2026
- for (let ix = 0; ix < w; ix++) {
2027
- const mVal = maskData[mIdx];
2028
- const effectiveM = invertMask ? 255 - mVal : mVal;
2029
- let weight = 0;
2030
- if (effectiveM === 0) {
2031
- weight = 0;
2032
- } else if (effectiveM === 255) {
2033
- weight = globalAlpha;
2034
- } else if (globalAlpha === 255) {
2035
- weight = effectiveM;
2036
- } else {
2037
- weight = effectiveM * globalAlpha + 128 >> 8;
2038
- }
2039
- if (weight === 0) {
2040
- dst32[dIdx] = (dst32[dIdx] & 16777215) >>> 0;
2049
+ for (let iy = 0; iy < actualH; iy++) {
2050
+ for (let ix = 0; ix < actualW; ix++) {
2051
+ const current = dst32[dIdx];
2052
+ const next = blendFn(finalSrcColor, current);
2053
+ if (current !== next) {
2054
+ dst32[dIdx] = next;
2041
2055
  didChange = true;
2042
- } else if (weight !== 255) {
2043
- const d = dst32[dIdx];
2044
- const da = d >>> 24;
2045
- if (da !== 0) {
2046
- const finalAlpha = da === 255 ? weight : da * weight + 128 >> 8;
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
- }
2053
- }
2054
2056
  }
2055
2057
  dIdx++;
2056
- mIdx++;
2057
2058
  }
2058
2059
  dIdx += dStride;
2059
- mIdx += mStride;
2060
2060
  }
2061
2061
  return didChange;
2062
2062
  }
2063
2063
 
2064
- // src/History/PixelMutator/mutatorApplyAlphaMask.ts
2064
+ // src/History/PixelMutator/mutatorBlendColor.ts
2065
2065
  var defaults2 = {
2066
- applyAlphaMaskToPixelData
2066
+ blendColorPixelData
2067
2067
  };
2068
- var mutatorApplyAlphaMask = ((writer, deps = defaults2) => {
2068
+ var mutatorBlendColor = ((writer, deps = defaults2) => {
2069
2069
  const {
2070
- applyAlphaMaskToPixelData: applyAlphaMaskToPixelData2 = defaults2.applyAlphaMaskToPixelData
2070
+ blendColorPixelData: blendColorPixelData2 = defaults2.blendColorPixelData
2071
2071
  } = deps;
2072
2072
  return {
2073
- applyAlphaMask(mask, opts = {}) {
2074
- let target = writer.config.target;
2073
+ blendColor(color, opts = {}) {
2074
+ const target = writer.config.target;
2075
2075
  const {
2076
2076
  x = 0,
2077
2077
  y = 0,
@@ -2079,159 +2079,227 @@ var mutatorApplyAlphaMask = ((writer, deps = defaults2) => {
2079
2079
  h = target.height
2080
2080
  } = opts;
2081
2081
  const didChange = writer.accumulator.storeRegionBeforeState(x, y, w, h);
2082
- return didChange(applyAlphaMaskToPixelData2(target, mask, opts));
2082
+ return didChange(blendColorPixelData2(target, color, opts));
2083
2083
  }
2084
2084
  };
2085
2085
  });
2086
2086
 
2087
- // src/PixelData/applyBinaryMaskToPixelData.ts
2088
- function applyBinaryMaskToPixelData(dst, mask, opts = {}) {
2087
+ // src/PixelData/blendPixel.ts
2088
+ function blendPixel(target, x, y, color, alpha = 255, blendFn = sourceOverPerfect) {
2089
+ if (alpha === 0) return false;
2090
+ let width = target.width;
2091
+ let height = target.height;
2092
+ if (x < 0 || x >= width || y < 0 || y >= height) return false;
2093
+ let srcAlpha = color >>> 24;
2094
+ let isOverwrite = blendFn.isOverwrite;
2095
+ if (srcAlpha === 0 && !isOverwrite) return false;
2096
+ let dst32 = target.data32;
2097
+ let index = y * width + x;
2098
+ let finalColor = color;
2099
+ if (alpha !== 255) {
2100
+ let finalAlpha = srcAlpha * alpha + 128 >> 8;
2101
+ if (finalAlpha === 0 && !isOverwrite) return false;
2102
+ finalColor = (color & 16777215 | finalAlpha << 24) >>> 0;
2103
+ }
2104
+ let current = dst32[index];
2105
+ let next = blendFn(finalColor, current);
2106
+ if (current !== next) {
2107
+ dst32[index] = next;
2108
+ return true;
2109
+ }
2110
+ return false;
2111
+ }
2112
+
2113
+ // src/History/PixelMutator/mutatorBlendPixel.ts
2114
+ var defaults3 = {
2115
+ blendPixel
2116
+ };
2117
+ var mutatorBlendPixel = ((writer, deps = defaults3) => {
2118
+ const {
2119
+ blendPixel: blendPixel2 = defaults3.blendPixel
2120
+ } = deps;
2121
+ return {
2122
+ blendPixel(x, y, color, alpha, blendFn) {
2123
+ const didChange = writer.accumulator.storePixelBeforeState(x, y);
2124
+ return didChange(blendPixel2(writer.config.target, x, y, color, alpha, blendFn));
2125
+ }
2126
+ };
2127
+ });
2128
+
2129
+ // src/PixelData/blendPixelData.ts
2130
+ function blendPixelData(dst, src, opts = {}) {
2089
2131
  const {
2090
2132
  x: targetX = 0,
2091
2133
  y: targetY = 0,
2092
- w: width = dst.width,
2093
- h: height = dst.height,
2134
+ sx: sourceX = 0,
2135
+ sy: sourceY = 0,
2136
+ w: width = src.width,
2137
+ h: height = src.height,
2094
2138
  alpha: globalAlpha = 255,
2095
- mx = 0,
2096
- my = 0,
2097
- invertMask = false
2139
+ blendFn = sourceOverPerfect
2098
2140
  } = opts;
2099
2141
  if (globalAlpha === 0) return false;
2100
2142
  let x = targetX;
2101
2143
  let y = targetY;
2144
+ let sx = sourceX;
2145
+ let sy = sourceY;
2102
2146
  let w = width;
2103
2147
  let h = height;
2148
+ if (sx < 0) {
2149
+ x -= sx;
2150
+ w += sx;
2151
+ sx = 0;
2152
+ }
2153
+ if (sy < 0) {
2154
+ y -= sy;
2155
+ h += sy;
2156
+ sy = 0;
2157
+ }
2158
+ w = Math.min(w, src.width - sx);
2159
+ h = Math.min(h, src.height - sy);
2104
2160
  if (x < 0) {
2161
+ sx -= x;
2105
2162
  w += x;
2106
2163
  x = 0;
2107
2164
  }
2108
2165
  if (y < 0) {
2166
+ sy -= y;
2109
2167
  h += y;
2110
2168
  y = 0;
2111
2169
  }
2112
- w = Math.min(w, dst.width - x);
2113
- h = Math.min(h, dst.height - y);
2114
- if (w <= 0 || h <= 0) return false;
2115
- const mPitch = mask.w;
2116
- if (mPitch <= 0) return false;
2117
- const startX = mx + (x - targetX);
2118
- const startY = my + (y - targetY);
2119
- const sX0 = Math.max(0, startX);
2120
- const sY0 = Math.max(0, startY);
2121
- const sX1 = Math.min(mPitch, startX + w);
2122
- const sY1 = Math.min(mask.h, startY + h);
2123
- const finalW = sX1 - sX0;
2124
- const finalH = sY1 - sY0;
2125
- if (finalW <= 0 || finalH <= 0) {
2126
- return false;
2127
- }
2128
- const xShift = sX0 - startX;
2129
- const yShift = sY0 - startY;
2170
+ const actualW = Math.min(w, dst.width - x);
2171
+ const actualH = Math.min(h, dst.height - y);
2172
+ if (actualW <= 0 || actualH <= 0) return false;
2130
2173
  const dst32 = dst.data32;
2174
+ const src32 = src.data32;
2131
2175
  const dw = dst.width;
2132
- const dStride = dw - finalW;
2133
- const mStride = mPitch - finalW;
2134
- const maskData = mask.data;
2135
- let dIdx = (y + yShift) * dw + (x + xShift);
2136
- let mIdx = sY0 * mPitch + sX0;
2176
+ const sw = src.width;
2177
+ let dIdx = y * dw + x | 0;
2178
+ let sIdx = sy * sw + sx | 0;
2179
+ const dStride = dw - actualW | 0;
2180
+ const sStride = sw - actualW | 0;
2181
+ const isOpaque = globalAlpha === 255;
2182
+ const isOverwrite = blendFn.isOverwrite;
2137
2183
  let didChange = false;
2138
- for (let iy = 0; iy < finalH; iy++) {
2139
- for (let ix = 0; ix < finalW; ix++) {
2140
- const mVal = maskData[mIdx];
2141
- const isMaskedOut = invertMask ? mVal !== 0 : mVal === 0;
2142
- if (isMaskedOut) {
2143
- const current = dst32[dIdx];
2144
- const next = (current & 16777215) >>> 0;
2145
- if (current !== next) {
2146
- dst32[dIdx] = next;
2147
- didChange = true;
2148
- }
2149
- } else if (globalAlpha !== 255) {
2150
- const d = dst32[dIdx];
2151
- const da = d >>> 24;
2152
- if (da !== 0) {
2153
- const finalAlpha = da === 255 ? globalAlpha : da * globalAlpha + 128 >> 8;
2154
- const next = (d & 16777215 | finalAlpha << 24) >>> 0;
2155
- if (d !== next) {
2156
- dst32[dIdx] = next;
2157
- didChange = true;
2158
- }
2184
+ for (let iy = 0; iy < actualH; iy++) {
2185
+ for (let ix = 0; ix < actualW; ix++) {
2186
+ const srcCol = src32[sIdx];
2187
+ const srcAlpha = srcCol >>> 24;
2188
+ if (srcAlpha === 0 && !isOverwrite) {
2189
+ dIdx++;
2190
+ sIdx++;
2191
+ continue;
2192
+ }
2193
+ let finalCol = srcCol;
2194
+ if (!isOpaque) {
2195
+ const a = srcAlpha * globalAlpha + 128 >> 8;
2196
+ if (a === 0 && !isOverwrite) {
2197
+ dIdx++;
2198
+ sIdx++;
2199
+ continue;
2159
2200
  }
2201
+ finalCol = (srcCol & 16777215 | a << 24) >>> 0;
2202
+ }
2203
+ const current = dst32[dIdx];
2204
+ const next = blendFn(finalCol, dst32[dIdx]);
2205
+ if (current !== next) {
2206
+ dst32[dIdx] = next;
2207
+ didChange = true;
2160
2208
  }
2161
2209
  dIdx++;
2162
- mIdx++;
2210
+ sIdx++;
2163
2211
  }
2164
2212
  dIdx += dStride;
2165
- mIdx += mStride;
2213
+ sIdx += sStride;
2166
2214
  }
2167
2215
  return didChange;
2168
2216
  }
2169
2217
 
2170
- // src/History/PixelMutator/mutatorApplyBinaryMask.ts
2171
- var defaults3 = {
2172
- applyBinaryMaskToPixelData
2218
+ // src/History/PixelMutator/mutatorBlendPixelData.ts
2219
+ var defaults4 = {
2220
+ blendPixelData
2173
2221
  };
2174
- var mutatorApplyBinaryMask = ((writer, deps = defaults3) => {
2222
+ var mutatorBlendPixelData = ((writer, deps = defaults4) => {
2175
2223
  const {
2176
- applyBinaryMaskToPixelData: applyBinaryMaskToPixelData2 = defaults3.applyBinaryMaskToPixelData
2224
+ blendPixelData: blendPixelData2 = defaults4.blendPixelData
2177
2225
  } = deps;
2178
2226
  return {
2179
- applyBinaryMask(mask, opts = {}) {
2180
- let target = writer.config.target;
2227
+ blendPixelData(src, opts = {}) {
2181
2228
  const {
2182
2229
  x = 0,
2183
2230
  y = 0,
2184
- w = target.width,
2185
- h = target.height
2231
+ w = src.width,
2232
+ h = src.height
2186
2233
  } = opts;
2187
2234
  const didChange = writer.accumulator.storeRegionBeforeState(x, y, w, h);
2188
- return didChange(applyBinaryMaskToPixelData2(target, mask, opts));
2235
+ return didChange(blendPixelData2(writer.config.target, src, opts));
2189
2236
  }
2190
2237
  };
2191
2238
  });
2192
2239
 
2193
- // src/PixelData/blendColorPixelDataAlphaMask.ts
2194
- function blendColorPixelDataAlphaMask(dst, color, mask, opts = {}) {
2195
- const targetX = opts.x ?? 0;
2196
- const targetY = opts.y ?? 0;
2197
- const w = opts.w ?? mask.w;
2198
- const h = opts.h ?? mask.h;
2199
- const globalAlpha = opts.alpha ?? 255;
2200
- const blendFn = opts.blendFn ?? sourceOverPerfect;
2201
- const mx = opts.mx ?? 0;
2202
- const my = opts.my ?? 0;
2203
- const invertMask = opts.invertMask ?? false;
2240
+ // src/PixelData/blendPixelDataAlphaMask.ts
2241
+ function blendPixelDataAlphaMask(dst, src, alphaMask, opts = {}) {
2242
+ const {
2243
+ x: targetX = 0,
2244
+ y: targetY = 0,
2245
+ sx: sourceX = 0,
2246
+ sy: sourceY = 0,
2247
+ w: width = src.width,
2248
+ h: height = src.height,
2249
+ alpha: globalAlpha = 255,
2250
+ blendFn = sourceOverPerfect,
2251
+ mx = 0,
2252
+ my = 0,
2253
+ invertMask = false
2254
+ } = opts;
2204
2255
  if (globalAlpha === 0) return false;
2205
- const baseSrcAlpha = color >>> 24;
2206
- const isOverwrite = blendFn.isOverwrite || false;
2207
- if (baseSrcAlpha === 0 && !isOverwrite) return false;
2208
2256
  let x = targetX;
2209
2257
  let y = targetY;
2210
- let actualW = w;
2211
- let actualH = h;
2258
+ let sx = sourceX;
2259
+ let sy = sourceY;
2260
+ let w = width;
2261
+ let h = height;
2262
+ if (sx < 0) {
2263
+ x -= sx;
2264
+ w += sx;
2265
+ sx = 0;
2266
+ }
2267
+ if (sy < 0) {
2268
+ y -= sy;
2269
+ h += sy;
2270
+ sy = 0;
2271
+ }
2272
+ w = Math.min(w, src.width - sx);
2273
+ h = Math.min(h, src.height - sy);
2212
2274
  if (x < 0) {
2213
- actualW += x;
2275
+ sx -= x;
2276
+ w += x;
2214
2277
  x = 0;
2215
2278
  }
2216
2279
  if (y < 0) {
2217
- actualH += y;
2280
+ sy -= y;
2281
+ h += y;
2218
2282
  y = 0;
2219
2283
  }
2220
- actualW = Math.min(actualW, dst.width - x);
2221
- actualH = Math.min(actualH, dst.height - y);
2284
+ const actualW = Math.min(w, dst.width - x);
2285
+ const actualH = Math.min(h, dst.height - y);
2222
2286
  if (actualW <= 0 || actualH <= 0) return false;
2287
+ const dw = dst.width;
2288
+ const sw = src.width;
2289
+ const mPitch = alphaMask.w;
2290
+ const maskData = alphaMask.data;
2223
2291
  const dx = x - targetX | 0;
2224
2292
  const dy = y - targetY | 0;
2225
2293
  const dst32 = dst.data32;
2226
- const dw = dst.width;
2227
- const mPitch = mask.w;
2228
- const maskData = mask.data;
2294
+ const src32 = src.data32;
2229
2295
  let dIdx = y * dw + x | 0;
2296
+ let sIdx = sy * sw + sx | 0;
2230
2297
  let mIdx = (my + dy) * mPitch + (mx + dx) | 0;
2231
2298
  const dStride = dw - actualW | 0;
2299
+ const sStride = sw - actualW | 0;
2232
2300
  const mStride = mPitch - actualW | 0;
2233
2301
  const isOpaque = globalAlpha === 255;
2234
- const colorRGB = color & 16777215;
2302
+ const isOverwrite = blendFn.isOverwrite || false;
2235
2303
  let didChange = false;
2236
2304
  for (let iy = 0; iy < actualH; iy++) {
2237
2305
  for (let ix = 0; ix < actualW; ix++) {
@@ -2239,6 +2307,15 @@ function blendColorPixelDataAlphaMask(dst, color, mask, opts = {}) {
2239
2307
  const effM = invertMask ? 255 - mVal : mVal;
2240
2308
  if (effM === 0) {
2241
2309
  dIdx++;
2310
+ sIdx++;
2311
+ mIdx++;
2312
+ continue;
2313
+ }
2314
+ const srcCol = src32[sIdx];
2315
+ const srcAlpha = srcCol >>> 24;
2316
+ if (srcAlpha === 0 && !isOverwrite) {
2317
+ dIdx++;
2318
+ sIdx++;
2242
2319
  mIdx++;
2243
2320
  continue;
2244
2321
  }
@@ -2250,844 +2327,459 @@ function blendColorPixelDataAlphaMask(dst, color, mask, opts = {}) {
2250
2327
  }
2251
2328
  if (weight === 0) {
2252
2329
  dIdx++;
2330
+ sIdx++;
2253
2331
  mIdx++;
2254
2332
  continue;
2255
2333
  }
2256
- let finalCol = color;
2334
+ let finalCol = srcCol;
2257
2335
  if (weight < 255) {
2258
- const a = baseSrcAlpha * weight + 128 >> 8;
2336
+ const a = srcAlpha * weight + 128 >> 8;
2259
2337
  if (a === 0 && !isOverwrite) {
2260
2338
  dIdx++;
2339
+ sIdx++;
2261
2340
  mIdx++;
2262
2341
  continue;
2263
2342
  }
2264
- finalCol = (colorRGB | a << 24) >>> 0;
2343
+ finalCol = (srcCol & 16777215 | a << 24) >>> 0;
2265
2344
  }
2266
2345
  const current = dst32[dIdx];
2267
- const next = blendFn(finalCol, current);
2346
+ const next = blendFn(finalCol, dst32[dIdx]);
2268
2347
  if (current !== next) {
2269
2348
  dst32[dIdx] = next;
2270
2349
  didChange = true;
2271
2350
  }
2272
2351
  dIdx++;
2352
+ sIdx++;
2273
2353
  mIdx++;
2274
2354
  }
2275
2355
  dIdx += dStride;
2356
+ sIdx += sStride;
2276
2357
  mIdx += mStride;
2277
2358
  }
2278
2359
  return didChange;
2279
2360
  }
2280
2361
 
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 ?? {
2291
- x: 0,
2292
- y: 0,
2293
- w: 0,
2294
- h: 0
2295
- };
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;
2307
- }
2308
-
2309
- // src/Rect/getCircleBrushOrPencilStrokeBounds.ts
2310
- function getCircleBrushOrPencilStrokeBounds(x0, y0, x1, y1, brushSize, result) {
2311
- const r = Math.ceil(brushSize / 2);
2312
- const minX = Math.min(x0, x1) - r;
2313
- const minY = Math.min(y0, y1) - r;
2314
- const maxX = Math.max(x0, x1) + r;
2315
- const maxY = Math.max(x0, y1) + r;
2316
- result.x = Math.floor(minX);
2317
- result.y = Math.floor(minY);
2318
- result.w = Math.ceil(maxX - minX);
2319
- result.h = Math.ceil(maxY - minY);
2320
- return result;
2321
- }
2322
-
2323
- // src/History/PixelMutator/mutatorApplyCircleBrushStroke.ts
2324
- var defaults4 = {
2325
- forEachLinePoint,
2326
- blendColorPixelDataAlphaMask,
2327
- getCircleBrushOrPencilBounds,
2328
- getCircleBrushOrPencilStrokeBounds
2362
+ // src/History/PixelMutator/mutatorBlendPixelDataAlphaMask.ts
2363
+ var defaults5 = {
2364
+ blendPixelDataAlphaMask
2329
2365
  };
2330
- var mutatorApplyCircleBrushStroke = ((writer, deps = defaults4) => {
2366
+ var mutatorBlendPixelDataAlphaMask = ((writer, deps = defaults5) => {
2331
2367
  const {
2332
- forEachLinePoint: forEachLinePoint2 = defaults4.forEachLinePoint,
2333
- blendColorPixelDataAlphaMask: blendColorPixelDataAlphaMask2 = defaults4.blendColorPixelDataAlphaMask,
2334
- getCircleBrushOrPencilBounds: getCircleBrushOrPencilBounds2 = defaults4.getCircleBrushOrPencilBounds,
2335
- getCircleBrushOrPencilStrokeBounds: getCircleBrushOrPencilStrokeBounds2 = defaults4.getCircleBrushOrPencilStrokeBounds
2368
+ blendPixelDataAlphaMask: blendPixelDataAlphaMask2 = defaults5.blendPixelDataAlphaMask
2336
2369
  } = deps;
2337
- const strokeBoundsOut = {
2338
- x: 0,
2339
- y: 0,
2340
- w: 0,
2341
- h: 0
2342
- };
2343
- const circleBrushBounds = {
2344
- x: 0,
2345
- y: 0,
2346
- w: 0,
2347
- h: 0
2348
- };
2349
- const blendColorPixelOptions = {
2350
- alpha: 255,
2351
- blendFn: sourceOverPerfect,
2352
- x: 0,
2353
- y: 0,
2354
- w: 0,
2355
- h: 0
2356
- };
2357
- const mask = {
2358
- type: 0 /* ALPHA */,
2359
- data: null,
2360
- w: 0,
2361
- h: 0
2362
- };
2363
2370
  return {
2364
- applyCircleBrushStroke(color, x0, y0, x1, y1, brush, alpha = 255, blendFn = sourceOverPerfect) {
2365
- const brushSize = brush.size;
2366
- const {
2367
- x: bx,
2368
- y: by,
2369
- w: bw,
2370
- h: bh
2371
- } = getCircleBrushOrPencilStrokeBounds2(x0, y0, x1, y1, brushSize, strokeBoundsOut);
2372
- if (bw <= 0 || bh <= 0) return;
2373
- mask.data = new Uint8Array(bw * bh);
2374
- mask.w = bw;
2375
- mask.h = bh;
2376
- const maskData = mask.data;
2377
- const brushData = brush.data;
2378
- const minOffset = brush.minOffset;
2379
- const target = writer.config.target;
2380
- const targetWidth = target.width;
2381
- const targetHeight = target.height;
2382
- forEachLinePoint2(x0, y0, x1, y1, (px, py) => {
2383
- const {
2384
- x: cbx,
2385
- y: cby,
2386
- w: cbw,
2387
- h: cbh
2388
- } = getCircleBrushOrPencilBounds2(px, py, brushSize, targetWidth, targetHeight, circleBrushBounds);
2389
- writer.accumulator.storeRegionBeforeState(cbx, cby, cbw, cbh);
2390
- const startX = Math.max(bx, cbx);
2391
- const startY = Math.max(by, cby);
2392
- const endX = Math.min(bx + bw, cbx + cbw);
2393
- const endY = Math.min(by + bh, cby + cbh);
2394
- const unclippedStartX = Math.floor(px + minOffset);
2395
- const unclippedStartY = Math.floor(py + minOffset);
2396
- for (let my = startY; my < endY; my++) {
2397
- const strokeMaskY = my - by;
2398
- const strokeMaskRowOffset = strokeMaskY * bw;
2399
- const brushY = my - unclippedStartY;
2400
- const brushRowOffset = brushY * brushSize;
2401
- for (let mx = startX; mx < endX; mx++) {
2402
- const brushX = mx - unclippedStartX;
2403
- const brushVal = brushData[brushRowOffset + brushX];
2404
- if (brushVal > 0) {
2405
- const strokeMaskIdx = strokeMaskRowOffset + (mx - bx);
2406
- if (brushVal > maskData[strokeMaskIdx]) {
2407
- maskData[strokeMaskIdx] = brushVal;
2408
- }
2409
- }
2410
- }
2411
- }
2412
- });
2413
- blendColorPixelOptions.blendFn = blendFn;
2414
- blendColorPixelOptions.alpha = alpha;
2415
- blendColorPixelOptions.x = bx;
2416
- blendColorPixelOptions.y = by;
2417
- blendColorPixelOptions.w = bw;
2418
- blendColorPixelOptions.h = bh;
2419
- blendColorPixelDataAlphaMask2(target, color, mask, blendColorPixelOptions);
2371
+ blendPixelDataAlphaMask(src, mask, opts = {}) {
2372
+ const x = opts.x ?? 0;
2373
+ const y = opts.y ?? 0;
2374
+ const w = opts.w ?? src.width;
2375
+ const h = opts.h ?? src.height;
2376
+ const didChange = writer.accumulator.storeRegionBeforeState(x, y, w, h);
2377
+ return didChange(blendPixelDataAlphaMask2(writer.config.target, src, mask, opts));
2420
2378
  }
2421
2379
  };
2422
2380
  });
2423
2381
 
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;
2382
+ // src/PixelData/blendPixelDataBinaryMask.ts
2383
+ function blendPixelDataBinaryMask(dst, src, binaryMask, opts = {}) {
2384
+ const {
2385
+ x: targetX = 0,
2386
+ y: targetY = 0,
2387
+ sx: sourceX = 0,
2388
+ sy: sourceY = 0,
2389
+ w: width = src.width,
2390
+ h: height = src.height,
2391
+ alpha: globalAlpha = 255,
2392
+ blendFn = sourceOverPerfect,
2393
+ mx = 0,
2394
+ my = 0,
2395
+ invertMask = false
2396
+ } = opts;
2435
2397
  if (globalAlpha === 0) return false;
2436
- const baseSrcAlpha = color >>> 24;
2437
- const isOverwrite = blendFn.isOverwrite || false;
2438
- if (baseSrcAlpha === 0 && !isOverwrite) return false;
2439
2398
  let x = targetX;
2440
2399
  let y = targetY;
2400
+ let sx = sourceX;
2401
+ let sy = sourceY;
2402
+ let w = width;
2403
+ let h = height;
2404
+ if (sx < 0) {
2405
+ x -= sx;
2406
+ w += sx;
2407
+ sx = 0;
2408
+ }
2409
+ if (sy < 0) {
2410
+ y -= sy;
2411
+ h += sy;
2412
+ sy = 0;
2413
+ }
2414
+ w = Math.min(w, src.width - sx);
2415
+ h = Math.min(h, src.height - sy);
2441
2416
  if (x < 0) {
2417
+ sx -= x;
2442
2418
  w += x;
2443
2419
  x = 0;
2444
2420
  }
2445
2421
  if (y < 0) {
2422
+ sy -= y;
2446
2423
  h += y;
2447
2424
  y = 0;
2448
2425
  }
2449
2426
  const actualW = Math.min(w, dst.width - x);
2450
2427
  const actualH = Math.min(h, dst.height - y);
2451
2428
  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
2429
  const dx = x - targetX | 0;
2459
2430
  const dy = y - targetY | 0;
2460
2431
  const dst32 = dst.data32;
2432
+ const src32 = src.data32;
2461
2433
  const dw = dst.width;
2462
- const mPitch = mask.w;
2463
- const maskData = mask.data;
2434
+ const sw = src.width;
2435
+ const mPitch = binaryMask.w;
2436
+ const maskData = binaryMask.data;
2464
2437
  let dIdx = y * dw + x | 0;
2438
+ let sIdx = sy * sw + sx | 0;
2465
2439
  let mIdx = (my + dy) * mPitch + (mx + dx) | 0;
2466
2440
  const dStride = dw - actualW | 0;
2441
+ const sStride = sw - actualW | 0;
2467
2442
  const mStride = mPitch - actualW | 0;
2468
2443
  const skipVal = invertMask ? 1 : 0;
2444
+ const isOpaque = globalAlpha === 255;
2445
+ const isOverwrite = blendFn.isOverwrite || false;
2469
2446
  let didChange = false;
2470
2447
  for (let iy = 0; iy < actualH; iy++) {
2471
2448
  for (let ix = 0; ix < actualW; ix++) {
2472
2449
  if (maskData[mIdx] === skipVal) {
2473
2450
  dIdx++;
2451
+ sIdx++;
2452
+ mIdx++;
2453
+ continue;
2454
+ }
2455
+ const srcCol = src32[sIdx];
2456
+ const srcAlpha = srcCol >>> 24;
2457
+ if (srcAlpha === 0 && !isOverwrite) {
2458
+ dIdx++;
2459
+ sIdx++;
2474
2460
  mIdx++;
2475
2461
  continue;
2476
2462
  }
2463
+ let finalCol = srcCol;
2464
+ if (!isOpaque) {
2465
+ const a = srcAlpha * globalAlpha + 128 >> 8;
2466
+ if (a === 0 && !isOverwrite) {
2467
+ dIdx++;
2468
+ sIdx++;
2469
+ mIdx++;
2470
+ continue;
2471
+ }
2472
+ finalCol = (srcCol & 16777215 | a << 24) >>> 0;
2473
+ }
2477
2474
  const current = dst32[dIdx];
2478
- const next = blendFn(baseColorWithGlobalAlpha, current);
2475
+ const next = blendFn(finalCol, dst32[dIdx]);
2479
2476
  if (current !== next) {
2480
2477
  dst32[dIdx] = next;
2481
2478
  didChange = true;
2482
2479
  }
2483
2480
  dIdx++;
2481
+ sIdx++;
2484
2482
  mIdx++;
2485
2483
  }
2486
2484
  dIdx += dStride;
2485
+ sIdx += sStride;
2487
2486
  mIdx += mStride;
2488
2487
  }
2489
2488
  return didChange;
2490
2489
  }
2491
2490
 
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
2523
- var defaults5 = {
2524
- blendColorPixelDataCircleMask,
2525
- getCircleBrushOrPencilBounds
2491
+ // src/History/PixelMutator/mutatorBlendPixelDataBinaryMask.ts
2492
+ var defaults6 = {
2493
+ blendPixelDataBinaryMask
2526
2494
  };
2527
- var mutatorBlendColorCircleMask = ((writer, deps = defaults5) => {
2495
+ var mutatorBlendPixelDataBinaryMask = ((writer, deps = defaults6) => {
2528
2496
  const {
2529
- blendColorPixelDataCircleMask: blendColorPixelDataCircleMask2 = defaults5.blendColorPixelDataCircleMask,
2530
- getCircleBrushOrPencilBounds: getCircleBrushOrPencilBounds2 = defaults5.getCircleBrushOrPencilBounds
2497
+ blendPixelDataBinaryMask: blendPixelDataBinaryMask2 = defaults6.blendPixelDataBinaryMask
2531
2498
  } = deps;
2532
- const boundsOut = {
2533
- x: 0,
2534
- y: 0,
2535
- w: 0,
2536
- h: 0
2537
- };
2538
- const blendColorPixelOptions = {
2539
- alpha: 255,
2540
- blendFn: sourceOverPerfect,
2541
- x: 0,
2542
- y: 0,
2543
- w: 0,
2544
- h: 0
2545
- };
2546
2499
  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));
2500
+ blendPixelDataBinaryMask(src, mask, opts = {}) {
2501
+ const x = opts.x ?? 0;
2502
+ const y = opts.y ?? 0;
2503
+ const w = opts.w ?? src.width;
2504
+ const h = opts.h ?? src.height;
2505
+ const didChange = writer.accumulator.storeRegionBeforeState(x, y, w, h);
2506
+ return didChange(blendPixelDataBinaryMask2(writer.config.target, src, mask, opts));
2552
2507
  }
2553
2508
  };
2554
2509
  });
2555
2510
 
2556
- // src/History/PixelMutator/mutatorApplyCirclePencil.ts
2557
- var defaults6 = {
2558
- applyCircleMaskToPixelData: blendColorPixelDataCircleMask,
2559
- getCircleBrushOrPencilBounds
2560
- };
2561
- var mutatorApplyCirclePencil = ((writer, deps = defaults6) => {
2511
+ // src/PixelData/fillPixelDataFast.ts
2512
+ var SCRATCH_RECT = makeClippedRect();
2513
+ function fillPixelDataFast(dst, color, _x, _y, _w, _h) {
2514
+ let x;
2515
+ let y;
2516
+ let w;
2517
+ let h;
2518
+ if (typeof _x === "object") {
2519
+ x = _x.x ?? 0;
2520
+ y = _x.y ?? 0;
2521
+ w = _x.w ?? dst.width;
2522
+ h = _x.h ?? dst.height;
2523
+ } else if (typeof _x === "number") {
2524
+ x = _x;
2525
+ y = _y;
2526
+ w = _w;
2527
+ h = _h;
2528
+ } else {
2529
+ x = 0;
2530
+ y = 0;
2531
+ w = dst.width;
2532
+ h = dst.height;
2533
+ }
2534
+ const clip = resolveRectClipping(x, y, w, h, dst.width, dst.height, SCRATCH_RECT);
2535
+ if (!clip.inBounds) return;
2562
2536
  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
- };
2572
- return {
2573
- applyCirclePencil(color, centerX, centerY, brush, alpha = 255, blendFn) {
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));
2578
- }
2579
- };
2580
- });
2537
+ x: finalX,
2538
+ y: finalY,
2539
+ w: actualW,
2540
+ h: actualH
2541
+ } = clip;
2542
+ const dst32 = dst.data32;
2543
+ const dw = dst.width;
2544
+ if (actualW === dw && actualH === dst.height && finalX === 0 && finalY === 0) {
2545
+ dst32.fill(color);
2546
+ return;
2547
+ }
2548
+ for (let iy = 0; iy < actualH; iy++) {
2549
+ const start = (finalY + iy) * dw + finalX;
2550
+ const end = start + actualW;
2551
+ dst32.fill(color, start, end);
2552
+ }
2553
+ }
2581
2554
 
2582
- // src/History/PixelMutator/mutatorApplyCirclePencilStroke.ts
2555
+ // src/History/PixelMutator/mutatorClear.ts
2583
2556
  var defaults7 = {
2584
- forEachLinePoint,
2585
- blendColorPixelDataBinaryMask,
2586
- getCircleBrushOrPencilBounds,
2587
- getCircleBrushOrPencilStrokeBounds
2557
+ fillPixelData: fillPixelDataFast
2588
2558
  };
2589
- var mutatorApplyCirclePencilStroke = ((writer, deps = defaults7) => {
2559
+ var mutatorClear = ((writer, deps = defaults7) => {
2590
2560
  const {
2591
- forEachLinePoint: forEachLinePoint2 = defaults7.forEachLinePoint,
2592
- blendColorPixelDataBinaryMask: blendColorPixelDataBinaryMask2 = defaults7.blendColorPixelDataBinaryMask,
2593
- getCircleBrushOrPencilStrokeBounds: getCircleBrushOrPencilStrokeBounds2 = defaults7.getCircleBrushOrPencilStrokeBounds,
2594
- getCircleBrushOrPencilBounds: getCircleBrushOrPencilBounds2 = defaults7.getCircleBrushOrPencilBounds
2561
+ fillPixelData: fillPixelData2 = defaults7.fillPixelData
2595
2562
  } = deps;
2596
- const strokeBoundsOut = {
2597
- x: 0,
2598
- y: 0,
2599
- w: 0,
2600
- h: 0
2601
- };
2602
- const circlePencilBounds = {
2603
- x: 0,
2604
- y: 0,
2605
- w: 0,
2606
- h: 0
2607
- };
2608
- const blendColorPixelOptions = {
2609
- alpha: 255,
2610
- blendFn: sourceOverPerfect,
2611
- x: 0,
2612
- y: 0,
2613
- w: 0,
2614
- h: 0
2615
- };
2616
- const mask = {
2617
- type: 1 /* BINARY */,
2618
- data: null,
2619
- w: 0,
2620
- h: 0
2621
- };
2622
2563
  return {
2623
- applyCirclePencilStroke(color, x0, y0, x1, y1, brush, alpha = 255, blendFn = sourceOverPerfect) {
2624
- const {
2625
- x: bx,
2626
- y: by,
2627
- w: bw,
2628
- h: bh
2629
- } = getCircleBrushOrPencilStrokeBounds2(x0, y0, x1, y1, brush.size, strokeBoundsOut);
2630
- if (bw <= 0 || bh <= 0) return;
2631
- mask.data = new Uint8Array(bw * bh);
2632
- mask.w = bw;
2633
- mask.h = bh;
2634
- const maskData = mask.data;
2564
+ clear(rect = {}) {
2635
2565
  const target = writer.config.target;
2636
- const targetWidth = target.width;
2637
- const targetHeight = target.height;
2638
- forEachLinePoint2(x0, y0, x1, y1, (px, py) => {
2639
- const {
2640
- x: cbx,
2641
- y: cby,
2642
- w: cbw,
2643
- h: cbh
2644
- } = getCircleBrushOrPencilBounds2(px, py, brush.size, targetWidth, targetHeight, circlePencilBounds);
2645
- writer.accumulator.storeRegionBeforeState(cbx, cby, cbw, cbh);
2646
- const unclippedStartX = Math.floor(px + brush.minOffset);
2647
- const unclippedStartY = Math.floor(py + brush.minOffset);
2648
- const startX = Math.max(bx, unclippedStartX);
2649
- const startY = Math.max(by, unclippedStartY);
2650
- const endX = Math.min(bx + bw, unclippedStartX + brush.w);
2651
- const endY = Math.min(by + bh, unclippedStartY + brush.h);
2652
- for (let my = startY; my < endY; my++) {
2653
- const brushY = my - unclippedStartY;
2654
- const maskRowOffset = (my - by) * bw;
2655
- const brushRowOffset = brushY * brush.w;
2656
- for (let mx = startX; mx < endX; mx++) {
2657
- const brushX = mx - unclippedStartX;
2658
- const brushAlpha = brush.data[brushRowOffset + brushX];
2659
- if (brushAlpha > 0) {
2660
- const maskIdx = maskRowOffset + (mx - bx);
2661
- maskData[maskIdx] = brushAlpha;
2662
- }
2663
- }
2664
- }
2665
- });
2666
- blendColorPixelOptions.blendFn = blendFn;
2667
- blendColorPixelOptions.alpha = alpha;
2668
- blendColorPixelOptions.x = bx;
2669
- blendColorPixelOptions.y = by;
2670
- blendColorPixelOptions.w = bw;
2671
- blendColorPixelOptions.h = bh;
2672
- blendColorPixelDataBinaryMask2(target, color, mask, blendColorPixelOptions);
2566
+ const x = rect.x ?? 0;
2567
+ const y = rect.y ?? 0;
2568
+ const w = rect.w ?? target.width;
2569
+ const h = rect.h ?? target.height;
2570
+ writer.accumulator.storeRegionBeforeState(x, y, w, h);
2571
+ fillPixelData2(target, 0, x, y, w, h);
2673
2572
  }
2674
2573
  };
2675
2574
  });
2676
2575
 
2677
- // src/Rect/getRectBrushOrPencilBounds.ts
2678
- function getRectBrushOrPencilBounds(centerX, centerY, brushWidth, brushHeight, targetWidth, targetHeight, out) {
2679
- const startX = Math.floor(centerX - brushWidth / 2);
2680
- const startY = Math.floor(centerY - brushHeight / 2);
2681
- const endX = startX + brushWidth;
2682
- const endY = startY + brushHeight;
2683
- const res = out ?? {
2684
- x: 0,
2685
- y: 0,
2686
- w: 0,
2687
- h: 0
2688
- };
2689
- const cStartX = Math.max(0, startX);
2690
- const cStartY = Math.max(0, startY);
2691
- const cEndX = Math.min(targetWidth, endX);
2692
- const cEndY = Math.min(targetHeight, endY);
2693
- const w = cEndX - cStartX;
2694
- const h = cEndY - cStartY;
2695
- res.x = cStartX;
2696
- res.y = cStartY;
2697
- res.w = w < 0 ? 0 : w;
2698
- res.h = h < 0 ? 0 : h;
2699
- return res;
2700
- }
2701
-
2702
- // src/PixelData/applyRectBrushToPixelData.ts
2703
- function applyRectBrushToPixelData(target, color, centerX, centerY, brushWidth, brushHeight, alpha = 255, fallOff, blendFn = sourceOverPerfect, bounds) {
2704
- const targetWidth = target.width;
2705
- const targetHeight = target.height;
2706
- const b = bounds ?? getRectBrushOrPencilBounds(centerX, centerY, brushWidth, brushHeight, targetWidth, targetHeight);
2707
- if (b.w <= 0 || b.h <= 0) {
2708
- return false;
2576
+ // src/PixelData/fillPixelData.ts
2577
+ var SCRATCH_RECT2 = makeClippedRect();
2578
+ function fillPixelData(dst, color, _x, _y, _w, _h) {
2579
+ let x;
2580
+ let y;
2581
+ let w;
2582
+ let h;
2583
+ if (typeof _x === "object") {
2584
+ x = _x.x ?? 0;
2585
+ y = _x.y ?? 0;
2586
+ w = _x.w ?? dst.width;
2587
+ h = _x.h ?? dst.height;
2588
+ } else if (typeof _x === "number") {
2589
+ x = _x;
2590
+ y = _y;
2591
+ w = _w;
2592
+ h = _h;
2593
+ } else {
2594
+ x = 0;
2595
+ y = 0;
2596
+ w = dst.width;
2597
+ h = dst.height;
2709
2598
  }
2710
- const data32 = target.data32;
2711
- const baseColor = color & 16777215;
2712
- const baseSrcAlpha = color >>> 24;
2713
- const isOpaque = alpha === 255;
2714
- const invHalfW = 1 / (brushWidth / 2);
2715
- const invHalfH = 1 / (brushHeight / 2);
2716
- const centerOffsetX = brushWidth % 2 === 0 ? 0.5 : 0;
2717
- const centerOffsetY = brushHeight % 2 === 0 ? 0.5 : 0;
2718
- const fCenterX = Math.floor(centerX);
2719
- const fCenterY = Math.floor(centerY);
2720
- const endX = b.x + b.w;
2721
- const endY = b.y + b.h;
2722
- const isOverwrite = blendFn.isOverwrite;
2723
- let didChange = false;
2724
- for (let py = b.y; py < endY; py++) {
2725
- const rowOffset = py * targetWidth;
2726
- const dy = Math.abs(py - fCenterY + centerOffsetY) * invHalfH;
2727
- for (let px = b.x; px < endX; px++) {
2728
- const idx = rowOffset + px;
2729
- const dx = Math.abs(px - fCenterX + centerOffsetX) * invHalfW;
2730
- const dist = dx > dy ? dx : dy;
2731
- const strength = fallOff(dist);
2732
- const maskVal = strength * 255 | 0;
2733
- if (maskVal <= 0) continue;
2734
- let weight = alpha;
2735
- if (isOpaque) {
2736
- weight = maskVal;
2737
- } else if (maskVal !== 255) {
2738
- weight = maskVal * alpha + 128 >> 8;
2739
- }
2740
- let finalCol = color;
2741
- if (weight < 255) {
2742
- const a = baseSrcAlpha * weight + 128 >> 8;
2743
- if (a === 0 && !isOverwrite) continue;
2744
- finalCol = (a << 24 | baseColor) >>> 0;
2745
- }
2746
- const current = data32[idx];
2747
- const next = blendFn(finalCol, current);
2748
- if (current !== next) {
2749
- data32[idx] = next;
2750
- didChange = true;
2599
+ const clip = resolveRectClipping(x, y, w, h, dst.width, dst.height, SCRATCH_RECT2);
2600
+ if (!clip.inBounds) return false;
2601
+ const {
2602
+ x: finalX,
2603
+ y: finalY,
2604
+ w: actualW,
2605
+ h: actualH
2606
+ } = clip;
2607
+ const dst32 = dst.data32;
2608
+ const dw = dst.width;
2609
+ let hasChanged = false;
2610
+ for (let iy = 0; iy < actualH; iy++) {
2611
+ const rowOffset = (finalY + iy) * dw;
2612
+ const start = rowOffset + finalX;
2613
+ const end = start + actualW;
2614
+ for (let i = start; i < end; i++) {
2615
+ if (dst32[i] !== color) {
2616
+ dst32[i] = color;
2617
+ hasChanged = true;
2751
2618
  }
2752
2619
  }
2753
2620
  }
2754
- return didChange;
2621
+ return hasChanged;
2755
2622
  }
2756
2623
 
2757
- // src/History/PixelMutator/mutatorApplyRectBrush.ts
2624
+ // src/History/PixelMutator/mutatorFill.ts
2758
2625
  var defaults8 = {
2759
- applyRectBrushToPixelData,
2760
- getRectBrushOrPencilBounds
2626
+ fillPixelData
2761
2627
  };
2762
- var mutatorApplyRectBrush = ((writer, deps = defaults8) => {
2628
+ var mutatorFill = ((writer, deps = defaults8) => {
2763
2629
  const {
2764
- applyRectBrushToPixelData: applyRectBrushToPixelData2 = defaults8.applyRectBrushToPixelData,
2765
- getRectBrushOrPencilBounds: getRectBrushOrPencilBounds2 = defaults8.getRectBrushOrPencilBounds
2630
+ fillPixelData: fillPixelData2 = defaults8.fillPixelData
2766
2631
  } = deps;
2767
- const boundsOut = {
2768
- x: 0,
2769
- y: 0,
2770
- w: 0,
2771
- h: 0
2772
- };
2773
2632
  return {
2774
- applyRectBrush(color, centerX, centerY, brushWidth, brushHeight, alpha = 255, fallOff, blendFn) {
2633
+ fill(color, x = 0, y = 0, w = writer.config.target.width, h = writer.config.target.height) {
2775
2634
  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));
2635
+ const didChange = writer.accumulator.storeRegionBeforeState(x, y, w, h);
2636
+ return didChange(fillPixelData2(target, color, x, y, w, h));
2779
2637
  }
2780
2638
  };
2781
2639
  });
2782
-
2783
- // src/Rect/getRectBrushOrPencilStrokeBounds.ts
2784
- function getRectBrushOrPencilStrokeBounds(x0, y0, x1, y1, brushWidth, brushHeight, result) {
2785
- const halfW = brushWidth / 2;
2786
- const halfH = brushHeight / 2;
2787
- const minX = Math.min(x0, x1) - halfW;
2788
- const minY = Math.min(y0, y1) - halfH;
2789
- const maxX = Math.max(x0, x1) + halfW;
2790
- const maxY = Math.max(y0, y1) + halfH;
2791
- result.x = Math.floor(minX);
2792
- result.y = Math.floor(minY);
2793
- result.w = Math.ceil(maxX - minX);
2794
- result.h = Math.ceil(maxY - minY);
2795
- return result;
2796
- }
2797
-
2798
- // src/History/PixelMutator/mutatorApplyRectBrushStroke.ts
2799
- var defaults9 = {
2800
- forEachLinePoint,
2801
- blendColorPixelDataAlphaMask,
2802
- getRectBrushOrPencilBounds,
2803
- getRectBrushOrPencilStrokeBounds
2804
- };
2805
- var mutatorApplyRectBrushStroke = ((writer, deps = defaults9) => {
2640
+ var mutatorFillRect = ((writer, deps = defaults8) => {
2806
2641
  const {
2807
- forEachLinePoint: forEachLinePoint2 = defaults9.forEachLinePoint,
2808
- blendColorPixelDataAlphaMask: blendColorPixelDataAlphaMask2 = defaults9.blendColorPixelDataAlphaMask,
2809
- getRectBrushOrPencilBounds: getRectBrushOrPencilBounds2 = defaults9.getRectBrushOrPencilBounds,
2810
- getRectBrushOrPencilStrokeBounds: getRectBrushOrPencilStrokeBounds2 = defaults9.getRectBrushOrPencilStrokeBounds
2642
+ fillPixelData: fillPixelData2 = defaults8.fillPixelData
2811
2643
  } = deps;
2812
- const strokeBoundsOut = {
2813
- x: 0,
2814
- y: 0,
2815
- w: 0,
2816
- h: 0
2817
- };
2818
- const rectBrushBounds = {
2819
- x: 0,
2820
- y: 0,
2821
- w: 0,
2822
- h: 0
2823
- };
2824
- const blendColorPixelOptions = {
2825
- alpha: 255,
2826
- blendFn: sourceOverPerfect,
2827
- x: 0,
2828
- y: 0,
2829
- w: 0,
2830
- h: 0
2831
- };
2832
- const mask = {
2833
- type: 0 /* ALPHA */,
2834
- data: null,
2835
- w: 0,
2836
- h: 0
2837
- };
2838
2644
  return {
2839
- applyRectBrushStroke(color, x0, y0, x1, y1, brushWidth, brushHeight, alpha = 255, fallOff, blendFn = sourceOverPerfect) {
2840
- const {
2841
- x: bx,
2842
- y: by,
2843
- w: bw,
2844
- h: bh
2845
- } = getRectBrushOrPencilStrokeBounds2(x0, y0, x1, y1, brushWidth, brushHeight, strokeBoundsOut);
2846
- if (bw <= 0 || bh <= 0) return;
2847
- mask.data = new Uint8Array(bw * bh);
2848
- mask.w = bw;
2849
- mask.h = bh;
2850
- const maskData = mask.data;
2851
- const halfW = brushWidth / 2;
2852
- const halfH = brushHeight / 2;
2853
- const invHalfW = 1 / halfW;
2854
- const invHalfH = 1 / halfH;
2855
- const centerOffsetX = brushWidth % 2 === 0 ? 0.5 : 0;
2856
- const centerOffsetY = brushHeight % 2 === 0 ? 0.5 : 0;
2645
+ fillRect(color, rect) {
2857
2646
  const target = writer.config.target;
2858
- const targetWidth = target.width;
2859
- const targetHeight = target.height;
2860
- forEachLinePoint2(x0, y0, x1, y1, (px, py) => {
2861
- const {
2862
- x: rbx,
2863
- y: rby,
2864
- w: rbw,
2865
- h: rbh
2866
- } = getRectBrushOrPencilBounds2(px, py, brushWidth, brushHeight, targetWidth, targetHeight, rectBrushBounds);
2867
- writer.accumulator.storeRegionBeforeState(rbx, rby, rbw, rbh);
2868
- const startX = Math.max(bx, rbx);
2869
- const startY = Math.max(by, rby);
2870
- const endX = Math.min(bx + bw, rbx + rbw);
2871
- const endY = Math.min(by + bh, rby + rbh);
2872
- const fPx = Math.floor(px);
2873
- const fPy = Math.floor(py);
2874
- for (let my = startY; my < endY; my++) {
2875
- const dy = Math.abs(my - fPy + centerOffsetY) * invHalfH;
2876
- const maskRowOffset = (my - by) * bw;
2877
- for (let mx = startX; mx < endX; mx++) {
2878
- const dx = Math.abs(mx - fPx + centerOffsetX) * invHalfW;
2879
- const maskIdx = maskRowOffset + (mx - bx);
2880
- const dist = dx > dy ? dx : dy;
2881
- const strength = fallOff(dist);
2882
- if (strength > 0) {
2883
- const intensity = strength * 255 | 0;
2884
- if (intensity > maskData[maskIdx]) {
2885
- maskData[maskIdx] = intensity;
2886
- }
2887
- }
2888
- }
2889
- }
2890
- });
2891
- blendColorPixelOptions.blendFn = blendFn;
2892
- blendColorPixelOptions.alpha = alpha;
2893
- blendColorPixelOptions.x = bx;
2894
- blendColorPixelOptions.y = by;
2895
- blendColorPixelOptions.w = bw;
2896
- blendColorPixelOptions.h = bh;
2897
- blendColorPixelDataAlphaMask2(target, color, mask, blendColorPixelOptions);
2647
+ const didChange = writer.accumulator.storeRegionBeforeState(rect.x, rect.y, rect.w, rect.h);
2648
+ return didChange(fillPixelData2(target, color, rect.x, rect.y, rect.w, rect.h));
2898
2649
  }
2899
2650
  };
2900
2651
  });
2901
2652
 
2902
- // src/History/PixelMutator/mutatorApplyRectPencil.ts
2903
- var defaults10 = {
2904
- applyRectBrushToPixelData,
2905
- getRectBrushOrPencilBounds,
2906
- fallOff: () => 1
2907
- };
2908
- var mutatorApplyRectPencil = ((writer, deps = defaults10) => {
2653
+ // src/PixelData/fillPixelDataBinaryMask.ts
2654
+ var SCRATCH_RECT3 = makeClippedRect();
2655
+ function fillPixelDataBinaryMask(dst, color, mask, alpha = 255, x = 0, y = 0) {
2656
+ if (alpha === 0) return false;
2657
+ const maskW = mask.w;
2658
+ const maskH = mask.h;
2659
+ const clip = resolveRectClipping(x, y, maskW, maskH, dst.width, dst.height, SCRATCH_RECT3);
2660
+ if (!clip.inBounds) return false;
2909
2661
  const {
2910
- applyRectBrushToPixelData: applyRectBrushToPixelData2 = defaults10.applyRectBrushToPixelData,
2911
- getRectBrushOrPencilBounds: getRectBrushOrPencilBounds2 = defaults10.getRectBrushOrPencilBounds,
2912
- fallOff = defaults10.fallOff
2913
- } = deps;
2914
- const boundsOut = {
2915
- x: 0,
2916
- y: 0,
2917
- w: 0,
2918
- h: 0
2919
- };
2920
- return {
2921
- applyRectPencil(color, centerX, centerY, brushWidth, brushHeight, alpha = 255, blendFn) {
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));
2926
- }
2927
- };
2928
- });
2929
-
2930
- // src/History/PixelMutator/mutatorApplyRectPencilStroke.ts
2931
- var defaults11 = {
2932
- forEachLinePoint,
2933
- getRectBrushOrPencilBounds,
2934
- getRectBrushOrPencilStrokeBounds,
2935
- blendColorPixelDataBinaryMask
2662
+ x: finalX,
2663
+ y: finalY,
2664
+ w: actualW,
2665
+ h: actualH
2666
+ } = clip;
2667
+ const maskData = mask.data;
2668
+ const dst32 = dst.data32;
2669
+ const dw = dst.width;
2670
+ let finalCol = color;
2671
+ if (alpha < 255) {
2672
+ const baseSrcAlpha = color >>> 24;
2673
+ const colorRGB = color & 16777215;
2674
+ const a = baseSrcAlpha * alpha + 128 >> 8;
2675
+ finalCol = (colorRGB | a << 24) >>> 0;
2676
+ }
2677
+ let hasChanged = false;
2678
+ for (let iy = 0; iy < actualH; iy++) {
2679
+ const currentY = finalY + iy;
2680
+ const maskY = currentY - y;
2681
+ const maskOffset = maskY * maskW;
2682
+ const dstRowOffset = currentY * dw;
2683
+ for (let ix = 0; ix < actualW; ix++) {
2684
+ const currentX = finalX + ix;
2685
+ const maskX = currentX - x;
2686
+ const maskIndex = maskOffset + maskX;
2687
+ if (maskData[maskIndex]) {
2688
+ const current = dst32[dstRowOffset + currentX];
2689
+ if (current !== finalCol) {
2690
+ dst32[dstRowOffset + currentX] = finalCol;
2691
+ hasChanged = true;
2692
+ }
2693
+ }
2694
+ }
2695
+ }
2696
+ return hasChanged;
2697
+ }
2698
+
2699
+ // src/History/PixelMutator/mutatorFillBinaryMask.ts
2700
+ var defaults9 = {
2701
+ fillPixelDataBinaryMask
2936
2702
  };
2937
- var mutatorApplyRectPencilStroke = ((writer, deps = defaults11) => {
2703
+ var mutatorFillBinaryMask = ((writer, deps = defaults9) => {
2938
2704
  const {
2939
- forEachLinePoint: forEachLinePoint2 = defaults11.forEachLinePoint,
2940
- blendColorPixelDataBinaryMask: blendColorPixelDataBinaryMask2 = defaults11.blendColorPixelDataBinaryMask,
2941
- getRectBrushOrPencilBounds: getRectBrushOrPencilBounds2 = defaults11.getRectBrushOrPencilBounds,
2942
- getRectBrushOrPencilStrokeBounds: getRectBrushOrPencilStrokeBounds2 = defaults11.getRectBrushOrPencilStrokeBounds
2705
+ fillPixelDataBinaryMask: fillPixelDataBinaryMask2 = defaults9.fillPixelDataBinaryMask
2943
2706
  } = deps;
2944
- const strokeBoundsOut = {
2945
- x: 0,
2946
- y: 0,
2947
- w: 0,
2948
- h: 0
2949
- };
2950
- const rectPencilBounds = {
2951
- x: 0,
2952
- y: 0,
2953
- w: 0,
2954
- h: 0
2955
- };
2956
- const blendColorPixelOptions = {
2957
- alpha: 255,
2958
- blendFn: sourceOverPerfect,
2959
- x: 0,
2960
- y: 0,
2961
- w: 0,
2962
- h: 0
2963
- };
2964
- const mask = {
2965
- type: 1 /* BINARY */,
2966
- data: null,
2967
- w: 0,
2968
- h: 0
2969
- };
2970
2707
  return {
2971
- applyRectPencilStroke(color, x0, y0, x1, y1, brushWidth, brushHeight, alpha = 255, blendFn = sourceOverPerfect) {
2972
- const {
2973
- x: bx,
2974
- y: by,
2975
- w: bw,
2976
- h: bh
2977
- } = getRectBrushOrPencilStrokeBounds2(x0, y0, x1, y1, brushWidth, brushHeight, strokeBoundsOut);
2978
- if (bw <= 0 || bh <= 0) return;
2979
- mask.data = new Uint8Array(bw * bh);
2980
- mask.w = bw;
2981
- mask.h = bh;
2982
- const maskData = mask.data;
2983
- const halfW = brushWidth / 2;
2984
- const halfH = brushHeight / 2;
2985
- const centerOffset = brushWidth % 2 === 0 ? 0.5 : 0;
2986
- const target = writer.config.target;
2987
- const targetWidth = target.width;
2988
- const targetHeight = target.height;
2989
- forEachLinePoint2(x0, y0, x1, y1, (px, py) => {
2990
- const {
2991
- x: rbx,
2992
- y: rby,
2993
- w: rbw,
2994
- h: rbh
2995
- } = getRectBrushOrPencilBounds2(px, py, brushWidth, brushHeight, targetWidth, targetHeight, rectPencilBounds);
2996
- writer.accumulator.storeRegionBeforeState(rbx, rby, rbw, rbh);
2997
- const startX = Math.max(bx, rbx);
2998
- const startY = Math.max(by, rby);
2999
- const endX = Math.min(bx + bw, rbx + rbw);
3000
- const endY = Math.min(by + bh, rby + rbh);
3001
- const fPx = Math.floor(px);
3002
- const fPy = Math.floor(py);
3003
- for (let my = startY; my < endY; my++) {
3004
- const dy = Math.abs(my - fPy + centerOffset);
3005
- const maskRowOffset = (my - by) * bw;
3006
- for (let mx = startX; mx < endX; mx++) {
3007
- const dx = Math.abs(mx - fPx + centerOffset);
3008
- const maskIdx = maskRowOffset + (mx - bx);
3009
- if (dx <= halfW && dy <= halfH) {
3010
- maskData[maskIdx] = 1;
3011
- }
3012
- }
3013
- }
3014
- });
3015
- blendColorPixelOptions.blendFn = blendFn;
3016
- blendColorPixelOptions.alpha = alpha;
3017
- blendColorPixelOptions.x = bx;
3018
- blendColorPixelOptions.y = by;
3019
- blendColorPixelOptions.w = bw;
3020
- blendColorPixelOptions.h = bh;
3021
- blendColorPixelDataBinaryMask2(target, color, mask, blendColorPixelOptions);
2708
+ fillBinaryMask(color, mask, alpha = 255, x = 0, y = 0) {
2709
+ const didChange = writer.accumulator.storeRegionBeforeState(x, y, mask.w, mask.h);
2710
+ return didChange(fillPixelDataBinaryMask2(writer.config.target, color, mask, alpha, x, y));
3022
2711
  }
3023
2712
  };
3024
2713
  });
3025
2714
 
3026
- // src/PixelData/blendColorPixelData.ts
3027
- function blendColorPixelData(dst, color, opts = {}) {
2715
+ // src/PixelData/invertPixelData.ts
2716
+ var SCRATCH_RECT4 = makeClippedRect();
2717
+ function invertPixelData(pixelData, opts = {}) {
2718
+ const dst = pixelData;
3028
2719
  const {
3029
2720
  x: targetX = 0,
3030
2721
  y: targetY = 0,
3031
- w: width = dst.width,
3032
- h: height = dst.height,
3033
- alpha: globalAlpha = 255,
3034
- blendFn = sourceOverPerfect
2722
+ w: width = pixelData.width,
2723
+ h: height = pixelData.height,
2724
+ mask,
2725
+ mx = 0,
2726
+ my = 0,
2727
+ invertMask = false
3035
2728
  } = opts;
3036
- if (globalAlpha === 0) return false;
3037
- const baseSrcAlpha = color >>> 24;
3038
- const isOverwrite = blendFn.isOverwrite || false;
3039
- if (baseSrcAlpha === 0 && !isOverwrite) return false;
3040
- let x = targetX;
3041
- let y = targetY;
3042
- let w = width;
3043
- let h = height;
3044
- if (x < 0) {
3045
- w += x;
3046
- x = 0;
3047
- }
3048
- if (y < 0) {
3049
- h += y;
3050
- y = 0;
3051
- }
3052
- const actualW = Math.min(w, dst.width - x);
3053
- const actualH = Math.min(h, dst.height - y);
3054
- if (actualW <= 0 || actualH <= 0) return false;
3055
- let finalSrcColor = color;
3056
- if (globalAlpha < 255) {
3057
- const a = baseSrcAlpha * globalAlpha + 128 >> 8;
3058
- if (a === 0 && !isOverwrite) return false;
3059
- finalSrcColor = (color & 16777215 | a << 24) >>> 0;
3060
- }
2729
+ const clip = resolveRectClipping(targetX, targetY, width, height, dst.width, dst.height, SCRATCH_RECT4);
2730
+ if (!clip.inBounds) return false;
2731
+ const {
2732
+ x,
2733
+ y,
2734
+ w: actualW,
2735
+ h: actualH
2736
+ } = clip;
3061
2737
  const dst32 = dst.data32;
3062
2738
  const dw = dst.width;
3063
- let dIdx = y * dw + x | 0;
3064
- const dStride = dw - actualW | 0;
3065
- let didChange = false;
3066
- for (let iy = 0; iy < actualH; iy++) {
3067
- for (let ix = 0; ix < actualW; ix++) {
3068
- const current = dst32[dIdx];
3069
- const next = blendFn(finalSrcColor, current);
3070
- if (current !== next) {
3071
- dst32[dIdx] = next;
3072
- didChange = true;
2739
+ const mPitch = mask?.w ?? width;
2740
+ const dx = x - targetX;
2741
+ const dy = y - targetY;
2742
+ let dIdx = y * dw + x;
2743
+ let mIdx = (my + dy) * mPitch + (mx + dx);
2744
+ const dStride = dw - actualW;
2745
+ const mStride = mPitch - actualW;
2746
+ if (mask) {
2747
+ const maskData = mask.data;
2748
+ for (let iy = 0; iy < actualH; iy++) {
2749
+ for (let ix = 0; ix < actualW; ix++) {
2750
+ const mVal = maskData[mIdx];
2751
+ const isHit = invertMask ? mVal === 0 : mVal === 1;
2752
+ if (isHit) {
2753
+ dst32[dIdx] = dst32[dIdx] ^ 16777215;
2754
+ }
2755
+ dIdx++;
2756
+ mIdx++;
3073
2757
  }
3074
- dIdx++;
2758
+ dIdx += dStride;
2759
+ mIdx += mStride;
2760
+ }
2761
+ } else {
2762
+ for (let iy = 0; iy < actualH; iy++) {
2763
+ for (let ix = 0; ix < actualW; ix++) {
2764
+ dst32[dIdx] = dst32[dIdx] ^ 16777215;
2765
+ dIdx++;
2766
+ }
2767
+ dIdx += dStride;
3075
2768
  }
3076
- dIdx += dStride;
3077
2769
  }
3078
- return didChange;
2770
+ return true;
3079
2771
  }
3080
2772
 
3081
- // src/History/PixelMutator/mutatorBlendColor.ts
3082
- var defaults12 = {
3083
- blendColorPixelData
2773
+ // src/History/PixelMutator/mutatorInvert.ts
2774
+ var defaults10 = {
2775
+ invertPixelData
3084
2776
  };
3085
- var mutatorBlendColor = ((writer, deps = defaults12) => {
2777
+ var mutatorInvert = ((writer, deps = defaults10) => {
3086
2778
  const {
3087
- blendColorPixelData: blendColorPixelData2 = defaults12.blendColorPixelData
2779
+ invertPixelData: invertPixelData2 = defaults10.invertPixelData
3088
2780
  } = deps;
3089
2781
  return {
3090
- blendColor(color, opts = {}) {
2782
+ invert(opts = {}) {
3091
2783
  const target = writer.config.target;
3092
2784
  const {
3093
2785
  x = 0,
@@ -3096,708 +2788,644 @@ var mutatorBlendColor = ((writer, deps = defaults12) => {
3096
2788
  h = target.height
3097
2789
  } = opts;
3098
2790
  const didChange = writer.accumulator.storeRegionBeforeState(x, y, w, h);
3099
- return didChange(blendColorPixelData2(target, color, opts));
2791
+ return didChange(invertPixelData2(target, opts));
3100
2792
  }
3101
2793
  };
3102
2794
  });
3103
2795
 
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;
2796
+ // src/History/PixelMutator.ts
2797
+ function makeFullPixelMutator(writer) {
2798
+ return {
2799
+ // @sort
2800
+ ...mutatorBlendColor(writer),
2801
+ ...mutatorBlendPixel(writer),
2802
+ ...mutatorBlendPixelData(writer),
2803
+ ...mutatorBlendPixelDataAlphaMask(writer),
2804
+ ...mutatorBlendPixelDataBinaryMask(writer),
2805
+ ...mutatorClear(writer),
2806
+ ...mutatorFill(writer),
2807
+ ...mutatorFillBinaryMask(writer),
2808
+ ...mutatorFillRect(writer),
2809
+ ...mutatorInvert(writer)
2810
+ };
2811
+ }
2812
+
2813
+ // src/ImageData/resizeImageData.ts
2814
+ function resizeImageData(target, newWidth, newHeight, offsetX = 0, offsetY = 0) {
2815
+ const result = new ImageData(newWidth, newHeight);
2816
+ const {
2817
+ width: oldW,
2818
+ height: oldH,
2819
+ data: oldData
2820
+ } = target;
2821
+ const newData = result.data;
2822
+ const x0 = Math.max(0, offsetX);
2823
+ const y0 = Math.max(0, offsetY);
2824
+ const x1 = Math.min(newWidth, offsetX + oldW);
2825
+ const y1 = Math.min(newHeight, offsetY + oldH);
2826
+ if (x1 <= x0 || y1 <= y0) {
2827
+ return result;
3120
2828
  }
3121
- let current = dst32[index];
3122
- let next = blendFn(finalColor, current);
3123
- if (current !== next) {
3124
- dst32[index] = next;
3125
- return true;
2829
+ const rowCount = y1 - y0;
2830
+ const rowLen = (x1 - x0) * 4;
2831
+ for (let row = 0; row < rowCount; row++) {
2832
+ const dstY = y0 + row;
2833
+ const srcY = dstY - offsetY;
2834
+ const srcX = x0 - offsetX;
2835
+ const dstStart = (dstY * newWidth + x0) * 4;
2836
+ const srcStart = (srcY * oldW + srcX) * 4;
2837
+ newData.set(oldData.subarray(srcStart, srcStart + rowLen), dstStart);
3126
2838
  }
3127
- return false;
2839
+ return result;
3128
2840
  }
3129
2841
 
3130
- // src/History/PixelMutator/mutatorBlendPixel.ts
3131
- var defaults13 = {
3132
- blendPixel
3133
- };
3134
- var mutatorBlendPixel = ((writer, deps = defaults13) => {
3135
- const {
3136
- blendPixel: blendPixel2 = defaults13.blendPixel
3137
- } = deps;
3138
- return {
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));
3142
- }
3143
- };
3144
- });
2842
+ // src/Internal/helpers.ts
2843
+ var macro_halfAndFloor = (value) => value >> 1;
3145
2844
 
3146
- // src/PixelData/blendPixelData.ts
3147
- function blendPixelData(dst, src, opts = {}) {
3148
- const {
3149
- x: targetX = 0,
3150
- y: targetY = 0,
3151
- sx: sourceX = 0,
3152
- sy: sourceY = 0,
3153
- w: width = src.width,
3154
- h: height = src.height,
3155
- alpha: globalAlpha = 255,
3156
- blendFn = sourceOverPerfect
3157
- } = opts;
3158
- if (globalAlpha === 0) return false;
3159
- let x = targetX;
3160
- let y = targetY;
3161
- let sx = sourceX;
3162
- let sy = sourceY;
3163
- let w = width;
3164
- let h = height;
3165
- if (sx < 0) {
3166
- x -= sx;
3167
- w += sx;
3168
- sx = 0;
3169
- }
3170
- if (sy < 0) {
3171
- y -= sy;
3172
- h += sy;
3173
- sy = 0;
3174
- }
3175
- w = Math.min(w, src.width - sx);
3176
- h = Math.min(h, src.height - sy);
3177
- if (x < 0) {
3178
- sx -= x;
3179
- w += x;
3180
- x = 0;
3181
- }
3182
- if (y < 0) {
3183
- sy -= y;
3184
- h += y;
3185
- y = 0;
3186
- }
3187
- const actualW = Math.min(w, dst.width - x);
3188
- const actualH = Math.min(h, dst.height - y);
3189
- if (actualW <= 0 || actualH <= 0) return false;
3190
- const dst32 = dst.data32;
3191
- const src32 = src.data32;
3192
- const dw = dst.width;
3193
- const sw = src.width;
3194
- let dIdx = y * dw + x | 0;
3195
- let sIdx = sy * sw + sx | 0;
3196
- const dStride = dw - actualW | 0;
3197
- const sStride = sw - actualW | 0;
3198
- const isOpaque = globalAlpha === 255;
3199
- const isOverwrite = blendFn.isOverwrite;
3200
- let didChange = false;
3201
- for (let iy = 0; iy < actualH; iy++) {
3202
- for (let ix = 0; ix < actualW; ix++) {
3203
- const srcCol = src32[sIdx];
3204
- const srcAlpha = srcCol >>> 24;
3205
- if (srcAlpha === 0 && !isOverwrite) {
3206
- dIdx++;
3207
- sIdx++;
3208
- continue;
3209
- }
3210
- let finalCol = srcCol;
3211
- if (!isOpaque) {
3212
- const a = srcAlpha * globalAlpha + 128 >> 8;
3213
- if (a === 0 && !isOverwrite) {
3214
- dIdx++;
3215
- sIdx++;
3216
- continue;
3217
- }
3218
- finalCol = (srcCol & 16777215 | a << 24) >>> 0;
3219
- }
3220
- const current = dst32[dIdx];
3221
- const next = blendFn(finalCol, dst32[dIdx]);
3222
- if (current !== next) {
3223
- dst32[dIdx] = next;
3224
- didChange = true;
3225
- }
3226
- dIdx++;
3227
- sIdx++;
3228
- }
3229
- dIdx += dStride;
3230
- sIdx += sStride;
3231
- }
3232
- return didChange;
3233
- }
3234
-
3235
- // src/History/PixelMutator/mutatorBlendPixelData.ts
3236
- var defaults14 = {
3237
- blendPixelData
3238
- };
3239
- var mutatorBlendPixelData = ((writer, deps = defaults14) => {
3240
- const {
3241
- blendPixelData: blendPixelData2 = defaults14.blendPixelData
3242
- } = deps;
3243
- return {
3244
- blendPixelData(src, opts = {}) {
3245
- const {
3246
- x = 0,
3247
- y = 0,
3248
- w = src.width,
3249
- h = src.height
3250
- } = opts;
3251
- const didChange = writer.accumulator.storeRegionBeforeState(x, y, w, h);
3252
- return didChange(blendPixelData2(writer.config.target, src, opts));
3253
- }
3254
- };
3255
- });
3256
-
3257
- // src/PixelData/blendPixelDataAlphaMask.ts
3258
- function blendPixelDataAlphaMask(dst, src, alphaMask, opts = {}) {
3259
- const {
3260
- x: targetX = 0,
3261
- y: targetY = 0,
3262
- sx: sourceX = 0,
3263
- sy: sourceY = 0,
3264
- w: width = src.width,
3265
- h: height = src.height,
3266
- alpha: globalAlpha = 255,
3267
- blendFn = sourceOverPerfect,
3268
- mx = 0,
3269
- my = 0,
3270
- invertMask = false
3271
- } = opts;
3272
- if (globalAlpha === 0) return false;
3273
- let x = targetX;
3274
- let y = targetY;
3275
- let sx = sourceX;
3276
- let sy = sourceY;
3277
- let w = width;
3278
- let h = height;
3279
- if (sx < 0) {
3280
- x -= sx;
3281
- w += sx;
3282
- sx = 0;
3283
- }
3284
- if (sy < 0) {
3285
- y -= sy;
3286
- h += sy;
3287
- sy = 0;
3288
- }
3289
- w = Math.min(w, src.width - sx);
3290
- h = Math.min(h, src.height - sy);
3291
- if (x < 0) {
3292
- sx -= x;
3293
- w += x;
3294
- x = 0;
3295
- }
3296
- if (y < 0) {
3297
- sy -= y;
3298
- h += y;
3299
- y = 0;
3300
- }
3301
- const actualW = Math.min(w, dst.width - x);
3302
- const actualH = Math.min(h, dst.height - y);
3303
- if (actualW <= 0 || actualH <= 0) return false;
3304
- const dw = dst.width;
3305
- const sw = src.width;
3306
- const mPitch = alphaMask.w;
3307
- const maskData = alphaMask.data;
3308
- const dx = x - targetX | 0;
3309
- const dy = y - targetY | 0;
3310
- const dst32 = dst.data32;
3311
- const src32 = src.data32;
3312
- let dIdx = y * dw + x | 0;
3313
- let sIdx = sy * sw + sx | 0;
3314
- let mIdx = (my + dy) * mPitch + (mx + dx) | 0;
3315
- const dStride = dw - actualW | 0;
3316
- const sStride = sw - actualW | 0;
3317
- const mStride = mPitch - actualW | 0;
3318
- const isOpaque = globalAlpha === 255;
3319
- const isOverwrite = blendFn.isOverwrite || false;
3320
- let didChange = false;
3321
- for (let iy = 0; iy < actualH; iy++) {
3322
- for (let ix = 0; ix < actualW; ix++) {
3323
- const mVal = maskData[mIdx];
3324
- const effM = invertMask ? 255 - mVal : mVal;
3325
- if (effM === 0) {
3326
- dIdx++;
3327
- sIdx++;
3328
- mIdx++;
3329
- continue;
3330
- }
3331
- const srcCol = src32[sIdx];
3332
- const srcAlpha = srcCol >>> 24;
3333
- if (srcAlpha === 0 && !isOverwrite) {
3334
- dIdx++;
3335
- sIdx++;
3336
- mIdx++;
3337
- continue;
3338
- }
3339
- let weight = globalAlpha;
3340
- if (isOpaque) {
3341
- weight = effM;
3342
- } else if (effM !== 255) {
3343
- weight = effM * globalAlpha + 128 >> 8;
3344
- }
3345
- if (weight === 0) {
3346
- dIdx++;
3347
- sIdx++;
3348
- mIdx++;
3349
- continue;
3350
- }
3351
- let finalCol = srcCol;
3352
- if (weight < 255) {
3353
- const a = srcAlpha * weight + 128 >> 8;
3354
- if (a === 0 && !isOverwrite) {
3355
- dIdx++;
3356
- sIdx++;
3357
- mIdx++;
3358
- continue;
3359
- }
3360
- finalCol = (srcCol & 16777215 | a << 24) >>> 0;
3361
- }
3362
- const current = dst32[dIdx];
3363
- const next = blendFn(finalCol, dst32[dIdx]);
3364
- if (current !== next) {
3365
- dst32[dIdx] = next;
3366
- didChange = true;
3367
- }
3368
- dIdx++;
3369
- sIdx++;
3370
- mIdx++;
3371
- }
3372
- dIdx += dStride;
3373
- sIdx += sStride;
3374
- mIdx += mStride;
3375
- }
3376
- return didChange;
3377
- }
3378
-
3379
- // src/History/PixelMutator/mutatorBlendPixelDataAlphaMask.ts
3380
- var defaults15 = {
3381
- blendPixelDataAlphaMask
3382
- };
3383
- var mutatorBlendPixelDataAlphaMask = ((writer, deps = defaults15) => {
3384
- const {
3385
- blendPixelDataAlphaMask: blendPixelDataAlphaMask2 = defaults15.blendPixelDataAlphaMask
3386
- } = deps;
3387
- return {
3388
- blendPixelDataAlphaMask(src, mask, opts = {}) {
3389
- const x = opts.x ?? 0;
3390
- const y = opts.y ?? 0;
3391
- const w = opts.w ?? src.width;
3392
- const h = opts.h ?? src.height;
3393
- const didChange = writer.accumulator.storeRegionBeforeState(x, y, w, h);
3394
- return didChange(blendPixelDataAlphaMask2(writer.config.target, src, mask, opts));
3395
- }
2845
+ // src/Rect/trimRectBounds.ts
2846
+ function trimRectBounds(x, y, w, h, targetWidth, targetHeight, out) {
2847
+ const res = out ?? {
2848
+ x: 0,
2849
+ y: 0,
2850
+ w: 0,
2851
+ h: 0
3396
2852
  };
3397
- });
3398
-
3399
- // src/PixelData/blendPixelDataBinaryMask.ts
3400
- function blendPixelDataBinaryMask(dst, src, binaryMask, opts = {}) {
3401
- const {
3402
- x: targetX = 0,
3403
- y: targetY = 0,
3404
- sx: sourceX = 0,
3405
- sy: sourceY = 0,
3406
- w: width = src.width,
3407
- h: height = src.height,
3408
- alpha: globalAlpha = 255,
3409
- blendFn = sourceOverPerfect,
3410
- mx = 0,
3411
- my = 0,
3412
- invertMask = false
3413
- } = opts;
3414
- if (globalAlpha === 0) return false;
3415
- let x = targetX;
3416
- let y = targetY;
3417
- let sx = sourceX;
3418
- let sy = sourceY;
3419
- let w = width;
3420
- let h = height;
3421
- if (sx < 0) {
3422
- x -= sx;
3423
- w += sx;
3424
- sx = 0;
3425
- }
3426
- if (sy < 0) {
3427
- y -= sy;
3428
- h += sy;
3429
- sy = 0;
3430
- }
3431
- w = Math.min(w, src.width - sx);
3432
- h = Math.min(h, src.height - sy);
3433
- if (x < 0) {
3434
- sx -= x;
3435
- w += x;
3436
- x = 0;
3437
- }
3438
- if (y < 0) {
3439
- sy -= y;
3440
- h += y;
3441
- y = 0;
2853
+ const left = Math.max(0, x);
2854
+ const top = Math.max(0, y);
2855
+ const right = Math.min(targetWidth, x + w);
2856
+ const bottom = Math.min(targetHeight, y + h);
2857
+ res.x = left;
2858
+ res.y = top;
2859
+ res.w = Math.max(0, right - left);
2860
+ res.h = Math.max(0, bottom - top);
2861
+ return res;
2862
+ }
2863
+
2864
+ // src/Paint/PaintBuffer.ts
2865
+ var PaintBuffer = class {
2866
+ constructor(config, tilePool) {
2867
+ this.config = config;
2868
+ this.tilePool = tilePool;
2869
+ this.lookup = [];
3442
2870
  }
3443
- const actualW = Math.min(w, dst.width - x);
3444
- const actualH = Math.min(h, dst.height - y);
3445
- if (actualW <= 0 || actualH <= 0) return false;
3446
- const dx = x - targetX | 0;
3447
- const dy = y - targetY | 0;
3448
- const dst32 = dst.data32;
3449
- const src32 = src.data32;
3450
- const dw = dst.width;
3451
- const sw = src.width;
3452
- const mPitch = binaryMask.w;
3453
- const maskData = binaryMask.data;
3454
- let dIdx = y * dw + x | 0;
3455
- let sIdx = sy * sw + sx | 0;
3456
- let mIdx = (my + dy) * mPitch + (mx + dx) | 0;
3457
- const dStride = dw - actualW | 0;
3458
- const sStride = sw - actualW | 0;
3459
- const mStride = mPitch - actualW | 0;
3460
- const skipVal = invertMask ? 1 : 0;
3461
- const isOpaque = globalAlpha === 255;
3462
- const isOverwrite = blendFn.isOverwrite || false;
3463
- let didChange = false;
3464
- for (let iy = 0; iy < actualH; iy++) {
3465
- for (let ix = 0; ix < actualW; ix++) {
3466
- if (maskData[mIdx] === skipVal) {
3467
- dIdx++;
3468
- sIdx++;
3469
- mIdx++;
3470
- continue;
3471
- }
3472
- const srcCol = src32[sIdx];
3473
- const srcAlpha = srcCol >>> 24;
3474
- if (srcAlpha === 0 && !isOverwrite) {
3475
- dIdx++;
3476
- sIdx++;
3477
- mIdx++;
3478
- continue;
3479
- }
3480
- let finalCol = srcCol;
3481
- if (!isOpaque) {
3482
- const a = srcAlpha * globalAlpha + 128 >> 8;
3483
- if (a === 0 && !isOverwrite) {
3484
- dIdx++;
3485
- sIdx++;
3486
- mIdx++;
3487
- continue;
3488
- }
3489
- finalCol = (srcCol & 16777215 | a << 24) >>> 0;
3490
- }
3491
- const current = dst32[dIdx];
3492
- const next = blendFn(finalCol, dst32[dIdx]);
3493
- if (current !== next) {
3494
- dst32[dIdx] = next;
3495
- didChange = true;
2871
+ lookup;
2872
+ scratchBounds = {
2873
+ x: 0,
2874
+ y: 0,
2875
+ w: 0,
2876
+ h: 0
2877
+ };
2878
+ eachTileInBounds(bounds, callback) {
2879
+ const {
2880
+ tileShift,
2881
+ targetColumns,
2882
+ targetRows,
2883
+ tileSize
2884
+ } = this.config;
2885
+ const x1 = Math.max(0, bounds.x >> tileShift);
2886
+ const y1 = Math.max(0, bounds.y >> tileShift);
2887
+ const x2 = Math.min(targetColumns - 1, bounds.x + bounds.w - 1 >> tileShift);
2888
+ const y2 = Math.min(targetRows - 1, bounds.y + bounds.h - 1 >> tileShift);
2889
+ if (x1 > x2 || y1 > y2) return;
2890
+ const lookup = this.lookup;
2891
+ const tilePool = this.tilePool;
2892
+ for (let ty = y1; ty <= y2; ty++) {
2893
+ const rowOffset = ty * targetColumns;
2894
+ const tileTop = ty << tileShift;
2895
+ for (let tx = x1; tx <= x2; tx++) {
2896
+ const id = rowOffset + tx;
2897
+ const tile = lookup[id] ?? (lookup[id] = tilePool.getTile(id, tx, ty));
2898
+ const tileLeft = tx << tileShift;
2899
+ const startX = bounds.x > tileLeft ? bounds.x : tileLeft;
2900
+ const startY = bounds.y > tileTop ? bounds.y : tileTop;
2901
+ const maskEndX = bounds.x + bounds.w;
2902
+ const tileEndX = tileLeft + tileSize;
2903
+ const endX = maskEndX < tileEndX ? maskEndX : tileEndX;
2904
+ const maskEndY = bounds.y + bounds.h;
2905
+ const tileEndY = tileTop + tileSize;
2906
+ const endY = maskEndY < tileEndY ? maskEndY : tileEndY;
2907
+ callback(tile, startX, startY, endX - startX, endY - startY);
3496
2908
  }
3497
- dIdx++;
3498
- sIdx++;
3499
- mIdx++;
3500
2909
  }
3501
- dIdx += dStride;
3502
- sIdx += sStride;
3503
- mIdx += mStride;
3504
2910
  }
3505
- return didChange;
3506
- }
3507
-
3508
- // src/History/PixelMutator/mutatorBlendPixelDataBinaryMask.ts
3509
- var defaults16 = {
3510
- blendPixelDataBinaryMask
3511
- };
3512
- var mutatorBlendPixelDataBinaryMask = ((writer, deps = defaults16) => {
3513
- const {
3514
- blendPixelDataBinaryMask: blendPixelDataBinaryMask2 = defaults16.blendPixelDataBinaryMask
3515
- } = deps;
3516
- return {
3517
- blendPixelDataBinaryMask(src, mask, opts = {}) {
3518
- const x = opts.x ?? 0;
3519
- const y = opts.y ?? 0;
3520
- const w = opts.w ?? src.width;
3521
- const h = opts.h ?? src.height;
3522
- const didChange = writer.accumulator.storeRegionBeforeState(x, y, w, h);
3523
- return didChange(blendPixelDataBinaryMask2(writer.config.target, src, mask, opts));
3524
- }
3525
- };
3526
- });
3527
-
3528
- // src/PixelData/fillPixelDataFast.ts
3529
- var SCRATCH_RECT = makeClippedRect();
3530
- function fillPixelDataFast(dst, color, _x, _y, _w, _h) {
3531
- let x;
3532
- let y;
3533
- let w;
3534
- let h;
3535
- if (typeof _x === "object") {
3536
- x = _x.x ?? 0;
3537
- y = _x.y ?? 0;
3538
- w = _x.w ?? dst.width;
3539
- h = _x.h ?? dst.height;
3540
- } else if (typeof _x === "number") {
3541
- x = _x;
3542
- y = _y;
3543
- w = _w;
3544
- h = _h;
3545
- } else {
3546
- x = 0;
3547
- y = 0;
3548
- w = dst.width;
3549
- h = dst.height;
2911
+ writePaintAlphaMaskStroke(color, brush, x0, y0, x1, y1) {
2912
+ const cA = color >>> 24;
2913
+ if (cA === 0) return false;
2914
+ const {
2915
+ tileShift,
2916
+ tileMask,
2917
+ target
2918
+ } = this.config;
2919
+ const {
2920
+ w: bW,
2921
+ h: bH,
2922
+ data: bD,
2923
+ centerOffsetX,
2924
+ centerOffsetY
2925
+ } = brush;
2926
+ const cRGB = color & 16777215;
2927
+ const scratch = this.scratchBounds;
2928
+ let changed = false;
2929
+ forEachLinePoint(x0, y0, x1, y1, (px, py) => {
2930
+ const topLeftX = Math.floor(px + centerOffsetX);
2931
+ const topLeftY = Math.floor(py + centerOffsetY);
2932
+ trimRectBounds(topLeftX, topLeftY, bW, bH, target.width, target.height, scratch);
2933
+ if (scratch.w <= 0 || scratch.h <= 0) return;
2934
+ this.eachTileInBounds(scratch, (tile, bX, bY, bW_t, bH_t) => {
2935
+ const d32 = tile.data32;
2936
+ let tileChanged = false;
2937
+ for (let i = 0; i < bH_t; i++) {
2938
+ const canvasY = bY + i;
2939
+ const bOff = (canvasY - topLeftY) * bW;
2940
+ const tOff = (canvasY & tileMask) << tileShift;
2941
+ const dS = tOff + (bX & tileMask);
2942
+ for (let j = 0; j < bW_t; j++) {
2943
+ const canvasX = bX + j;
2944
+ const brushA = bD[bOff + (canvasX - topLeftX)];
2945
+ if (brushA === 0) continue;
2946
+ const t = cA * brushA + 128;
2947
+ const blendedA = t + (t >> 8) >> 8;
2948
+ const idx = dS + j;
2949
+ const cur = d32[idx];
2950
+ if (brushA > cur >>> 24) {
2951
+ const next = (cRGB | blendedA << 24) >>> 0;
2952
+ if (cur !== next) {
2953
+ d32[idx] = next;
2954
+ tileChanged = true;
2955
+ }
2956
+ }
2957
+ }
2958
+ }
2959
+ if (tileChanged) changed = true;
2960
+ });
2961
+ });
2962
+ return changed;
3550
2963
  }
3551
- const clip = resolveRectClipping(x, y, w, h, dst.width, dst.height, SCRATCH_RECT);
3552
- if (!clip.inBounds) return;
3553
- const {
3554
- x: finalX,
3555
- y: finalY,
3556
- w: actualW,
3557
- h: actualH
3558
- } = clip;
3559
- const dst32 = dst.data32;
3560
- const dw = dst.width;
3561
- if (actualW === dw && actualH === dst.height && finalX === 0 && finalY === 0) {
3562
- dst32.fill(color);
3563
- return;
2964
+ writePaintBinaryMaskStroke(color, brush, x0, y0, x1, y1) {
2965
+ const alphaIsZero = color >>> 24 === 0;
2966
+ if (alphaIsZero) return false;
2967
+ const {
2968
+ tileShift,
2969
+ tileMask,
2970
+ target
2971
+ } = this.config;
2972
+ const {
2973
+ w: bW,
2974
+ h: bH,
2975
+ data: bD,
2976
+ centerOffsetX,
2977
+ centerOffsetY
2978
+ } = brush;
2979
+ const scratch = this.scratchBounds;
2980
+ let changed = false;
2981
+ forEachLinePoint(x0, y0, x1, y1, (px, py) => {
2982
+ const topLeftX = Math.floor(px + centerOffsetX);
2983
+ const topLeftY = Math.floor(py + centerOffsetY);
2984
+ trimRectBounds(topLeftX, topLeftY, bW, bH, target.width, target.height, scratch);
2985
+ if (scratch.w <= 0 || scratch.h <= 0) return;
2986
+ this.eachTileInBounds(scratch, (tile, bX, bY, bW_t, bH_t) => {
2987
+ const d32 = tile.data32;
2988
+ let tileChanged = false;
2989
+ for (let i = 0; i < bH_t; i++) {
2990
+ const canvasY = bY + i;
2991
+ const bOff = (canvasY - topLeftY) * bW;
2992
+ const tOff = (canvasY & tileMask) << tileShift;
2993
+ const dS = tOff + (bX & tileMask);
2994
+ for (let j = 0; j < bW_t; j++) {
2995
+ const canvasX = bX + j;
2996
+ if (bD[bOff + (canvasX - topLeftX)]) {
2997
+ const idx = dS + j;
2998
+ if (d32[idx] !== color) {
2999
+ d32[idx] = color;
3000
+ tileChanged = true;
3001
+ }
3002
+ }
3003
+ }
3004
+ }
3005
+ if (tileChanged) changed = true;
3006
+ });
3007
+ });
3008
+ return changed;
3009
+ }
3010
+ writeRectStroke(color, brushWidth, brushHeight, x0, y0, x1, y1) {
3011
+ const alphaIsZero = color >>> 24 === 0;
3012
+ if (alphaIsZero) return false;
3013
+ const config = this.config;
3014
+ const tileShift = config.tileShift;
3015
+ const tileMask = config.tileMask;
3016
+ const target = config.target;
3017
+ const scratch = this.scratchBounds;
3018
+ const centerOffsetX = macro_halfAndFloor(brushWidth - 1);
3019
+ const centerOffsetY = macro_halfAndFloor(brushHeight - 1);
3020
+ let changed = false;
3021
+ forEachLinePoint(x0, y0, x1, y1, (px, py) => {
3022
+ const topLeftX = Math.floor(px + centerOffsetX);
3023
+ const topLeftY = Math.floor(py + centerOffsetY);
3024
+ trimRectBounds(topLeftX, topLeftY, brushWidth, brushHeight, target.width, target.height, scratch);
3025
+ if (scratch.w <= 0 || scratch.h <= 0) return;
3026
+ this.eachTileInBounds(scratch, (tile, bX, bY, bW_t, bH_t) => {
3027
+ const d32 = tile.data32;
3028
+ let tileChanged = false;
3029
+ for (let i = 0; i < bH_t; i++) {
3030
+ const canvasY = bY + i;
3031
+ const tOff = (canvasY & tileMask) << tileShift;
3032
+ const dS = tOff + (bX & tileMask);
3033
+ for (let j = 0; j < bW_t; j++) {
3034
+ const idx = dS + j;
3035
+ if (d32[idx] !== color) {
3036
+ d32[idx] = color;
3037
+ tileChanged = true;
3038
+ }
3039
+ }
3040
+ }
3041
+ if (tileChanged) {
3042
+ changed = true;
3043
+ }
3044
+ });
3045
+ });
3046
+ return changed;
3564
3047
  }
3565
- for (let iy = 0; iy < actualH; iy++) {
3566
- const start = (finalY + iy) * dw + finalX;
3567
- const end = start + actualW;
3568
- dst32.fill(color, start, end);
3048
+ clear() {
3049
+ this.tilePool.releaseTiles(this.lookup);
3569
3050
  }
3570
- }
3051
+ };
3571
3052
 
3572
- // src/History/PixelMutator/mutatorClear.ts
3573
- var defaults17 = {
3574
- fillPixelData: fillPixelDataFast
3053
+ // src/PixelTile/PixelTile.ts
3054
+ var PixelTile = class {
3055
+ constructor(id, tx, ty, tileSize, tileArea) {
3056
+ this.id = id;
3057
+ this.tx = tx;
3058
+ this.ty = ty;
3059
+ this.width = this.height = tileSize;
3060
+ this.data32 = new Uint32Array(tileArea);
3061
+ const data8 = new Uint8ClampedArray(this.data32.buffer);
3062
+ this.imageData = new ImageData(data8, tileSize, tileSize);
3063
+ }
3064
+ data32;
3065
+ width;
3066
+ height;
3067
+ imageData;
3575
3068
  };
3576
- var mutatorClear = ((writer, deps = defaults17) => {
3577
- const {
3578
- fillPixelData: fillPixelData2 = defaults17.fillPixelData
3579
- } = deps;
3580
- return {
3581
- clear(rect = {}) {
3582
- const target = writer.config.target;
3583
- const x = rect.x ?? 0;
3584
- const y = rect.y ?? 0;
3585
- const w = rect.w ?? target.width;
3586
- const h = rect.h ?? target.height;
3587
- writer.accumulator.storeRegionBeforeState(x, y, w, h);
3588
- fillPixelData2(target, 0, x, y, w, h);
3589
- }
3590
- };
3591
- });
3592
3069
 
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;
3070
+ // src/PixelTile/PixelTilePool.ts
3071
+ var PixelTilePool = class {
3072
+ pool;
3073
+ tileSize;
3074
+ tileArea;
3075
+ constructor(config) {
3076
+ this.pool = [];
3077
+ this.tileSize = config.tileSize;
3078
+ this.tileArea = config.tileArea;
3615
3079
  }
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;
3080
+ getTile(id, tx, ty) {
3081
+ let tile = this.pool.pop();
3082
+ if (tile) {
3083
+ tile.id = id;
3084
+ tile.tx = tx;
3085
+ tile.ty = ty;
3086
+ tile.data32.fill(0);
3087
+ return tile;
3088
+ }
3089
+ return new PixelTile(id, tx, ty, this.tileSize, this.tileArea);
3090
+ }
3091
+ releaseTile(tile) {
3092
+ this.pool.push(tile);
3093
+ }
3094
+ releaseTiles(tiles) {
3095
+ let length = tiles.length;
3096
+ for (let i = 0; i < length; i++) {
3097
+ let tile = tiles[i];
3098
+ if (tile) {
3099
+ this.pool.push(tile);
3635
3100
  }
3636
3101
  }
3102
+ tiles.length = 0;
3637
3103
  }
3638
- return hasChanged;
3639
- }
3640
-
3641
- // src/History/PixelMutator/mutatorFill.ts
3642
- var defaults18 = {
3643
- fillPixelData
3644
3104
  };
3645
- var mutatorFill = ((writer, deps = defaults18) => {
3646
- const {
3647
- fillPixelData: fillPixelData2 = defaults18.fillPixelData
3648
- } = deps;
3649
- return {
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
- }
3105
+
3106
+ // src/History/PixelWriter.ts
3107
+ var PixelWriter = class {
3108
+ historyManager;
3109
+ accumulator;
3110
+ historyActionFactory;
3111
+ config;
3112
+ pixelTilePool;
3113
+ paintBuffer;
3114
+ mutator;
3115
+ blendPixelDataOpts = {
3116
+ alpha: 255,
3117
+ blendFn: sourceOverPerfect,
3118
+ x: 0,
3119
+ y: 0,
3120
+ w: 0,
3121
+ h: 0
3655
3122
  };
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));
3123
+ _inProgress = false;
3124
+ constructor(target, mutatorFactory, {
3125
+ tileSize = 256,
3126
+ maxHistorySteps = 50,
3127
+ historyManager = new HistoryManager(maxHistorySteps),
3128
+ historyActionFactory = makeHistoryAction,
3129
+ pixelTilePool,
3130
+ accumulator
3131
+ } = {}) {
3132
+ this.config = new PixelEngineConfig(tileSize, target);
3133
+ this.historyManager = historyManager;
3134
+ this.pixelTilePool = pixelTilePool ?? new PixelTilePool(this.config);
3135
+ this.accumulator = accumulator ?? new PixelAccumulator(this.config, this.pixelTilePool);
3136
+ this.historyActionFactory = historyActionFactory;
3137
+ this.mutator = mutatorFactory(this);
3138
+ this.paintBuffer = new PaintBuffer(this.config, this.pixelTilePool);
3139
+ }
3140
+ /**
3141
+ * Executes `transaction` and commits the resulting pixel changes as a single
3142
+ * undoable history action.
3143
+ *
3144
+ * - If `transaction` throws, all accumulated changes are rolled back and the error
3145
+ * is re-thrown. No action is committed.
3146
+ * - If `transaction` completes without modifying any pixels, no action is committed.
3147
+ * - `withHistory` is not re-entrant. Calling it again from inside `transaction` will
3148
+ * throw immediately to prevent silent data loss from a nested extractPatch.
3149
+ *
3150
+ * @param transaction Callback to be executed inside the transaction.
3151
+ * @param after Called after both undo and redo — use for generic change notifications.
3152
+ * @param afterUndo Called after undo only — use for dimension or state changes specific to undo.
3153
+ * @param afterRedo Called after redo only.
3154
+ */
3155
+ withHistory(transaction, after, afterUndo, afterRedo) {
3156
+ if (this._inProgress) {
3157
+ throw new Error("withHistory is not re-entrant \u2014 commit or rollback the current operation first");
3666
3158
  }
3667
- };
3668
- });
3159
+ this._inProgress = true;
3160
+ try {
3161
+ transaction(this.mutator);
3162
+ } catch (e) {
3163
+ this.accumulator.rollbackAfterError();
3164
+ throw e;
3165
+ } finally {
3166
+ this._inProgress = false;
3167
+ }
3168
+ if (this.accumulator.beforeTiles.length === 0) return;
3169
+ const patch = this.accumulator.extractPatch();
3170
+ const action = this.historyActionFactory(this, patch, after, afterUndo, afterRedo);
3171
+ this.historyManager.commit(action);
3172
+ }
3173
+ resize(newWidth, newHeight, offsetX = 0, offsetY = 0, after, afterUndo, afterRedo, resizeImageDataFn = resizeImageData) {
3174
+ if (this._inProgress) {
3175
+ throw new Error("Cannot resize inside a withHistory callback");
3176
+ }
3177
+ if (this.accumulator.beforeTiles.length > 0) {
3178
+ throw new Error("Cannot resize with an open accumulator \u2014 commit or rollback first");
3179
+ }
3180
+ const config = this.config;
3181
+ const target = config.target;
3182
+ const beforeImageData = target.imageData;
3183
+ const afterImageData = resizeImageDataFn(beforeImageData, newWidth, newHeight, offsetX, offsetY);
3184
+ target.set(afterImageData);
3185
+ this.historyManager.commit({
3186
+ undo: () => {
3187
+ target.set(beforeImageData);
3188
+ afterUndo?.(beforeImageData);
3189
+ after?.(beforeImageData);
3190
+ },
3191
+ redo: () => {
3192
+ target.set(afterImageData);
3193
+ afterRedo?.(afterImageData);
3194
+ after?.(afterImageData);
3195
+ }
3196
+ });
3197
+ }
3198
+ commitPaintBuffer(alpha = 255, blendFn = sourceOverPerfect, blendPixelDataFn = blendPixelData) {
3199
+ const paintBuffer = this.paintBuffer;
3200
+ const tileShift = paintBuffer.config.tileShift;
3201
+ const lookup = paintBuffer.lookup;
3202
+ const opts = this.blendPixelDataOpts;
3203
+ opts.alpha = alpha;
3204
+ opts.blendFn = blendFn;
3205
+ for (let i = 0; i < lookup.length; i++) {
3206
+ const tile = lookup[i];
3207
+ if (tile) {
3208
+ const didChange = this.accumulator.storeTileBeforeState(tile.id, tile.tx, tile.ty);
3209
+ const dx = tile.tx << tileShift;
3210
+ const dy = tile.ty << tileShift;
3211
+ opts.x = dx;
3212
+ opts.y = dy;
3213
+ opts.w = tile.width;
3214
+ opts.h = tile.height;
3215
+ didChange(blendPixelDataFn(this.config.target, tile, opts));
3216
+ }
3217
+ }
3218
+ paintBuffer.clear();
3219
+ }
3220
+ };
3669
3221
 
3670
- // src/PixelData/fillPixelDataBinaryMask.ts
3671
- var SCRATCH_RECT3 = makeClippedRect();
3672
- function fillPixelDataBinaryMask(dst, color, mask, alpha = 255, x = 0, y = 0) {
3673
- if (alpha === 0) return false;
3674
- const maskW = mask.w;
3675
- const maskH = mask.h;
3676
- const clip = resolveRectClipping(x, y, maskW, maskH, dst.width, dst.height, SCRATCH_RECT3);
3677
- if (!clip.inBounds) return false;
3222
+ // src/PixelData/applyAlphaMaskToPixelData.ts
3223
+ function applyAlphaMaskToPixelData(dst, mask, opts = {}) {
3678
3224
  const {
3679
- x: finalX,
3680
- y: finalY,
3681
- w: actualW,
3682
- h: actualH
3683
- } = clip;
3684
- const maskData = mask.data;
3225
+ x: targetX = 0,
3226
+ y: targetY = 0,
3227
+ w: width = dst.width,
3228
+ h: height = dst.height,
3229
+ alpha: globalAlpha = 255,
3230
+ mx = 0,
3231
+ my = 0,
3232
+ invertMask = false
3233
+ } = opts;
3234
+ if (globalAlpha === 0) return false;
3235
+ let x = targetX;
3236
+ let y = targetY;
3237
+ let w = width;
3238
+ let h = height;
3239
+ if (x < 0) {
3240
+ w += x;
3241
+ x = 0;
3242
+ }
3243
+ if (y < 0) {
3244
+ h += y;
3245
+ y = 0;
3246
+ }
3247
+ w = Math.min(w, dst.width - x);
3248
+ h = Math.min(h, dst.height - y);
3249
+ if (w <= 0) return false;
3250
+ if (h <= 0) return false;
3251
+ const mPitch = mask.w;
3252
+ if (mPitch <= 0) return false;
3253
+ const startX = mx + (x - targetX);
3254
+ const startY = my + (y - targetY);
3255
+ const sX0 = Math.max(0, startX);
3256
+ const sY0 = Math.max(0, startY);
3257
+ const sX1 = Math.min(mPitch, startX + w);
3258
+ const sY1 = Math.min(mask.h, startY + h);
3259
+ const finalW = sX1 - sX0;
3260
+ const finalH = sY1 - sY0;
3261
+ if (finalW <= 0) return false;
3262
+ if (finalH <= 0) return false;
3263
+ const xShift = sX0 - startX;
3264
+ const yShift = sY0 - startY;
3685
3265
  const dst32 = dst.data32;
3686
3266
  const dw = dst.width;
3687
- let finalCol = color;
3688
- if (alpha < 255) {
3689
- const baseSrcAlpha = color >>> 24;
3690
- const colorRGB = color & 16777215;
3691
- const a = baseSrcAlpha * alpha + 128 >> 8;
3692
- finalCol = (colorRGB | a << 24) >>> 0;
3693
- }
3694
- let hasChanged = false;
3695
- for (let iy = 0; iy < actualH; iy++) {
3696
- const currentY = finalY + iy;
3697
- const maskY = currentY - y;
3698
- const maskOffset = maskY * maskW;
3699
- const dstRowOffset = currentY * dw;
3700
- for (let ix = 0; ix < actualW; ix++) {
3701
- const currentX = finalX + ix;
3702
- const maskX = currentX - x;
3703
- const maskIndex = maskOffset + maskX;
3704
- if (maskData[maskIndex]) {
3705
- const current = dst32[dstRowOffset + currentX];
3706
- if (current !== finalCol) {
3707
- dst32[dstRowOffset + currentX] = finalCol;
3708
- hasChanged = true;
3267
+ const dStride = dw - finalW;
3268
+ const mStride = mPitch - finalW;
3269
+ const maskData = mask.data;
3270
+ let dIdx = (y + yShift) * dw + (x + xShift);
3271
+ let mIdx = sY0 * mPitch + sX0;
3272
+ let didChange = false;
3273
+ for (let iy = 0; iy < h; iy++) {
3274
+ for (let ix = 0; ix < w; ix++) {
3275
+ const mVal = maskData[mIdx];
3276
+ const effectiveM = invertMask ? 255 - mVal : mVal;
3277
+ let weight = 0;
3278
+ if (effectiveM === 0) {
3279
+ weight = 0;
3280
+ } else if (effectiveM === 255) {
3281
+ weight = globalAlpha;
3282
+ } else if (globalAlpha === 255) {
3283
+ weight = effectiveM;
3284
+ } else {
3285
+ weight = effectiveM * globalAlpha + 128 >> 8;
3286
+ }
3287
+ if (weight === 0) {
3288
+ dst32[dIdx] = (dst32[dIdx] & 16777215) >>> 0;
3289
+ didChange = true;
3290
+ } else if (weight !== 255) {
3291
+ const d = dst32[dIdx];
3292
+ const da = d >>> 24;
3293
+ if (da !== 0) {
3294
+ const finalAlpha = da === 255 ? weight : da * weight + 128 >> 8;
3295
+ const current = dst32[dIdx];
3296
+ const next = (d & 16777215 | finalAlpha << 24) >>> 0;
3297
+ if (current !== next) {
3298
+ dst32[dIdx] = next;
3299
+ didChange = true;
3300
+ }
3709
3301
  }
3710
3302
  }
3303
+ dIdx++;
3304
+ mIdx++;
3711
3305
  }
3306
+ dIdx += dStride;
3307
+ mIdx += mStride;
3712
3308
  }
3713
- return hasChanged;
3309
+ return didChange;
3714
3310
  }
3715
3311
 
3716
- // src/History/PixelMutator/mutatorFillBinaryMask.ts
3717
- var defaults19 = {
3718
- fillPixelDataBinaryMask
3312
+ // src/History/PixelMutator/mutatorApplyAlphaMask.ts
3313
+ var defaults11 = {
3314
+ applyAlphaMaskToPixelData
3719
3315
  };
3720
- var mutatorFillBinaryMask = ((writer, deps = defaults19) => {
3316
+ var mutatorApplyAlphaMask = ((writer, deps = defaults11) => {
3721
3317
  const {
3722
- fillPixelDataBinaryMask: fillPixelDataBinaryMask2 = defaults19.fillPixelDataBinaryMask
3318
+ applyAlphaMaskToPixelData: applyAlphaMaskToPixelData2 = defaults11.applyAlphaMaskToPixelData
3723
3319
  } = deps;
3724
3320
  return {
3725
- fillBinaryMask(color, mask, alpha = 255, x = 0, y = 0) {
3726
- const didChange = writer.accumulator.storeRegionBeforeState(x, y, mask.w, mask.h);
3727
- return didChange(fillPixelDataBinaryMask2(writer.config.target, color, mask, alpha, x, y));
3321
+ applyAlphaMask(mask, opts = {}) {
3322
+ let target = writer.config.target;
3323
+ const {
3324
+ x = 0,
3325
+ y = 0,
3326
+ w = target.width,
3327
+ h = target.height
3328
+ } = opts;
3329
+ const didChange = writer.accumulator.storeRegionBeforeState(x, y, w, h);
3330
+ return didChange(applyAlphaMaskToPixelData2(target, mask, opts));
3728
3331
  }
3729
3332
  };
3730
3333
  });
3731
3334
 
3732
- // src/PixelData/invertPixelData.ts
3733
- var SCRATCH_RECT4 = makeClippedRect();
3734
- function invertPixelData(pixelData, opts = {}) {
3735
- const dst = pixelData;
3335
+ // src/PixelData/applyBinaryMaskToPixelData.ts
3336
+ function applyBinaryMaskToPixelData(dst, mask, opts = {}) {
3736
3337
  const {
3737
3338
  x: targetX = 0,
3738
3339
  y: targetY = 0,
3739
- w: width = pixelData.width,
3740
- h: height = pixelData.height,
3741
- mask,
3340
+ w: width = dst.width,
3341
+ h: height = dst.height,
3342
+ alpha: globalAlpha = 255,
3742
3343
  mx = 0,
3743
3344
  my = 0,
3744
3345
  invertMask = false
3745
3346
  } = opts;
3746
- const clip = resolveRectClipping(targetX, targetY, width, height, dst.width, dst.height, SCRATCH_RECT4);
3747
- if (!clip.inBounds) return false;
3748
- const {
3749
- x,
3750
- y,
3751
- w: actualW,
3752
- h: actualH
3753
- } = clip;
3347
+ if (globalAlpha === 0) return false;
3348
+ let x = targetX;
3349
+ let y = targetY;
3350
+ let w = width;
3351
+ let h = height;
3352
+ if (x < 0) {
3353
+ w += x;
3354
+ x = 0;
3355
+ }
3356
+ if (y < 0) {
3357
+ h += y;
3358
+ y = 0;
3359
+ }
3360
+ w = Math.min(w, dst.width - x);
3361
+ h = Math.min(h, dst.height - y);
3362
+ if (w <= 0 || h <= 0) return false;
3363
+ const mPitch = mask.w;
3364
+ if (mPitch <= 0) return false;
3365
+ const startX = mx + (x - targetX);
3366
+ const startY = my + (y - targetY);
3367
+ const sX0 = Math.max(0, startX);
3368
+ const sY0 = Math.max(0, startY);
3369
+ const sX1 = Math.min(mPitch, startX + w);
3370
+ const sY1 = Math.min(mask.h, startY + h);
3371
+ const finalW = sX1 - sX0;
3372
+ const finalH = sY1 - sY0;
3373
+ if (finalW <= 0 || finalH <= 0) {
3374
+ return false;
3375
+ }
3376
+ const xShift = sX0 - startX;
3377
+ const yShift = sY0 - startY;
3754
3378
  const dst32 = dst.data32;
3755
3379
  const dw = dst.width;
3756
- const mPitch = mask?.w ?? width;
3757
- const dx = x - targetX;
3758
- const dy = y - targetY;
3759
- let dIdx = y * dw + x;
3760
- let mIdx = (my + dy) * mPitch + (mx + dx);
3761
- const dStride = dw - actualW;
3762
- const mStride = mPitch - actualW;
3763
- if (mask) {
3764
- const maskData = mask.data;
3765
- for (let iy = 0; iy < actualH; iy++) {
3766
- for (let ix = 0; ix < actualW; ix++) {
3767
- const mVal = maskData[mIdx];
3768
- const isHit = invertMask ? mVal === 0 : mVal === 1;
3769
- if (isHit) {
3770
- dst32[dIdx] = dst32[dIdx] ^ 16777215;
3380
+ const dStride = dw - finalW;
3381
+ const mStride = mPitch - finalW;
3382
+ const maskData = mask.data;
3383
+ let dIdx = (y + yShift) * dw + (x + xShift);
3384
+ let mIdx = sY0 * mPitch + sX0;
3385
+ let didChange = false;
3386
+ for (let iy = 0; iy < finalH; iy++) {
3387
+ for (let ix = 0; ix < finalW; ix++) {
3388
+ const mVal = maskData[mIdx];
3389
+ const isMaskedOut = invertMask ? mVal !== 0 : mVal === 0;
3390
+ if (isMaskedOut) {
3391
+ const current = dst32[dIdx];
3392
+ const next = (current & 16777215) >>> 0;
3393
+ if (current !== next) {
3394
+ dst32[dIdx] = next;
3395
+ didChange = true;
3396
+ }
3397
+ } else if (globalAlpha !== 255) {
3398
+ const d = dst32[dIdx];
3399
+ const da = d >>> 24;
3400
+ if (da !== 0) {
3401
+ const finalAlpha = da === 255 ? globalAlpha : da * globalAlpha + 128 >> 8;
3402
+ const next = (d & 16777215 | finalAlpha << 24) >>> 0;
3403
+ if (d !== next) {
3404
+ dst32[dIdx] = next;
3405
+ didChange = true;
3406
+ }
3771
3407
  }
3772
- dIdx++;
3773
- mIdx++;
3774
- }
3775
- dIdx += dStride;
3776
- mIdx += mStride;
3777
- }
3778
- } else {
3779
- for (let iy = 0; iy < actualH; iy++) {
3780
- for (let ix = 0; ix < actualW; ix++) {
3781
- dst32[dIdx] = dst32[dIdx] ^ 16777215;
3782
- dIdx++;
3783
3408
  }
3784
- dIdx += dStride;
3409
+ dIdx++;
3410
+ mIdx++;
3785
3411
  }
3412
+ dIdx += dStride;
3413
+ mIdx += mStride;
3786
3414
  }
3787
- return true;
3415
+ return didChange;
3788
3416
  }
3789
3417
 
3790
- // src/History/PixelMutator/mutatorInvert.ts
3791
- var defaults20 = {
3792
- invertPixelData
3418
+ // src/History/PixelMutator/mutatorApplyBinaryMask.ts
3419
+ var defaults12 = {
3420
+ applyBinaryMaskToPixelData
3793
3421
  };
3794
- var mutatorInvert = ((writer, deps = defaults20) => {
3422
+ var mutatorApplyBinaryMask = ((writer, deps = defaults12) => {
3795
3423
  const {
3796
- invertPixelData: invertPixelData2 = defaults20.invertPixelData
3424
+ applyBinaryMaskToPixelData: applyBinaryMaskToPixelData2 = defaults12.applyBinaryMaskToPixelData
3797
3425
  } = deps;
3798
3426
  return {
3799
- invert(opts = {}) {
3800
- const target = writer.config.target;
3427
+ applyBinaryMask(mask, opts = {}) {
3428
+ let target = writer.config.target;
3801
3429
  const {
3802
3430
  x = 0,
3803
3431
  y = 0,
@@ -3805,125 +3433,200 @@ var mutatorInvert = ((writer, deps = defaults20) => {
3805
3433
  h = target.height
3806
3434
  } = opts;
3807
3435
  const didChange = writer.accumulator.storeRegionBeforeState(x, y, w, h);
3808
- return didChange(invertPixelData2(target, opts));
3436
+ return didChange(applyBinaryMaskToPixelData2(target, mask, opts));
3809
3437
  }
3810
3438
  };
3811
3439
  });
3812
3440
 
3813
- // src/History/PixelMutator.ts
3814
- function makeFullPixelMutator(writer) {
3815
- return {
3816
- // @sort
3817
- ...mutatorApplyAlphaMask(writer),
3818
- ...mutatorApplyBinaryMask(writer),
3819
- ...mutatorApplyCircleBrushStroke(writer),
3820
- ...mutatorApplyCirclePencil(writer),
3821
- ...mutatorApplyCirclePencilStroke(writer),
3822
- ...mutatorApplyRectBrush(writer),
3823
- ...mutatorApplyRectBrushStroke(writer),
3824
- ...mutatorApplyRectPencil(writer),
3825
- ...mutatorApplyRectPencilStroke(writer),
3826
- ...mutatorBlendColor(writer),
3827
- ...mutatorBlendColorCircleMask(writer),
3828
- ...mutatorBlendPixel(writer),
3829
- ...mutatorBlendPixelData(writer),
3830
- ...mutatorBlendPixelDataAlphaMask(writer),
3831
- ...mutatorBlendPixelDataBinaryMask(writer),
3832
- ...mutatorClear(writer),
3833
- ...mutatorFill(writer),
3834
- ...mutatorFillBinaryMask(writer),
3835
- ...mutatorFillRect(writer),
3836
- ...mutatorInvert(writer)
3837
- };
3838
- }
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);
3441
+ // src/PixelData/blendColorPixelDataAlphaMask.ts
3442
+ function blendColorPixelDataAlphaMask(dst, color, mask, opts = {}) {
3443
+ const targetX = opts.x ?? 0;
3444
+ const targetY = opts.y ?? 0;
3445
+ const w = opts.w ?? mask.w;
3446
+ const h = opts.h ?? mask.h;
3447
+ const globalAlpha = opts.alpha ?? 255;
3448
+ const blendFn = opts.blendFn ?? sourceOverPerfect;
3449
+ const mx = opts.mx ?? 0;
3450
+ const my = opts.my ?? 0;
3451
+ const invertMask = opts.invertMask ?? false;
3452
+ if (globalAlpha === 0) return false;
3453
+ const baseSrcAlpha = color >>> 24;
3454
+ const isOverwrite = blendFn.isOverwrite || false;
3455
+ if (baseSrcAlpha === 0 && !isOverwrite) return false;
3456
+ let x = targetX;
3457
+ let y = targetY;
3458
+ let actualW = w;
3459
+ let actualH = h;
3460
+ if (x < 0) {
3461
+ actualW += x;
3462
+ x = 0;
3850
3463
  }
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;
3464
+ if (y < 0) {
3465
+ actualH += y;
3466
+ y = 0;
3866
3467
  }
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;
3468
+ actualW = Math.min(actualW, dst.width - x);
3469
+ actualH = Math.min(actualH, dst.height - y);
3470
+ if (actualW <= 0 || actualH <= 0) return false;
3471
+ const dx = x - targetX | 0;
3472
+ const dy = y - targetY | 0;
3473
+ const dst32 = dst.data32;
3474
+ const dw = dst.width;
3475
+ const mPitch = mask.w;
3476
+ const maskData = mask.data;
3477
+ let dIdx = y * dw + x | 0;
3478
+ let mIdx = (my + dy) * mPitch + (mx + dx) | 0;
3479
+ const dStride = dw - actualW | 0;
3480
+ const mStride = mPitch - actualW | 0;
3481
+ const isOpaque = globalAlpha === 255;
3482
+ const colorRGB = color & 16777215;
3483
+ let didChange = false;
3484
+ for (let iy = 0; iy < actualH; iy++) {
3485
+ for (let ix = 0; ix < actualW; ix++) {
3486
+ const mVal = maskData[mIdx];
3487
+ const effM = invertMask ? 255 - mVal : mVal;
3488
+ if (effM === 0) {
3489
+ dIdx++;
3490
+ mIdx++;
3491
+ continue;
3492
+ }
3493
+ let weight = globalAlpha;
3494
+ if (isOpaque) {
3495
+ weight = effM;
3496
+ } else if (effM !== 255) {
3497
+ weight = effM * globalAlpha + 128 >> 8;
3498
+ }
3499
+ if (weight === 0) {
3500
+ dIdx++;
3501
+ mIdx++;
3502
+ continue;
3503
+ }
3504
+ let finalCol = color;
3505
+ if (weight < 255) {
3506
+ const a = baseSrcAlpha * weight + 128 >> 8;
3507
+ if (a === 0 && !isOverwrite) {
3508
+ dIdx++;
3509
+ mIdx++;
3510
+ continue;
3511
+ }
3512
+ finalCol = (colorRGB | a << 24) >>> 0;
3513
+ }
3514
+ const current = dst32[dIdx];
3515
+ const next = blendFn(finalCol, current);
3516
+ if (current !== next) {
3517
+ dst32[dIdx] = next;
3518
+ didChange = true;
3519
+ }
3520
+ dIdx++;
3521
+ mIdx++;
3875
3522
  }
3876
- return new PixelTile(id, tx, ty, this.tileSize, this.tileArea);
3523
+ dIdx += dStride;
3524
+ mIdx += mStride;
3877
3525
  }
3878
- releaseTile(tile) {
3879
- this.pool.push(tile);
3526
+ return didChange;
3527
+ }
3528
+
3529
+ // src/PixelData/blendColorPixelDataBinaryMask.ts
3530
+ function blendColorPixelDataBinaryMask(dst, color, mask, opts = {}) {
3531
+ const targetX = opts.x ?? 0;
3532
+ const targetY = opts.y ?? 0;
3533
+ let w = opts.w ?? mask.w;
3534
+ let h = opts.h ?? mask.h;
3535
+ const globalAlpha = opts.alpha ?? 255;
3536
+ const blendFn = opts.blendFn ?? sourceOverPerfect;
3537
+ const mx = opts.mx ?? 0;
3538
+ const my = opts.my ?? 0;
3539
+ const invertMask = opts.invertMask ?? false;
3540
+ if (globalAlpha === 0) return false;
3541
+ const baseSrcAlpha = color >>> 24;
3542
+ const isOverwrite = blendFn.isOverwrite || false;
3543
+ if (baseSrcAlpha === 0 && !isOverwrite) return false;
3544
+ let x = targetX;
3545
+ let y = targetY;
3546
+ if (x < 0) {
3547
+ w += x;
3548
+ x = 0;
3549
+ }
3550
+ if (y < 0) {
3551
+ h += y;
3552
+ y = 0;
3553
+ }
3554
+ const actualW = Math.min(w, dst.width - x);
3555
+ const actualH = Math.min(h, dst.height - y);
3556
+ if (actualW <= 0 || actualH <= 0) return false;
3557
+ let baseColorWithGlobalAlpha = color;
3558
+ if (globalAlpha < 255) {
3559
+ const a = baseSrcAlpha * globalAlpha + 128 >> 8;
3560
+ if (a === 0 && !isOverwrite) return false;
3561
+ baseColorWithGlobalAlpha = (color & 16777215 | a << 24) >>> 0;
3880
3562
  }
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);
3563
+ const dx = x - targetX | 0;
3564
+ const dy = y - targetY | 0;
3565
+ const dst32 = dst.data32;
3566
+ const dw = dst.width;
3567
+ const mPitch = mask.w;
3568
+ const maskData = mask.data;
3569
+ let dIdx = y * dw + x | 0;
3570
+ let mIdx = (my + dy) * mPitch + (mx + dx) | 0;
3571
+ const dStride = dw - actualW | 0;
3572
+ const mStride = mPitch - actualW | 0;
3573
+ const skipVal = invertMask ? 1 : 0;
3574
+ let didChange = false;
3575
+ for (let iy = 0; iy < actualH; iy++) {
3576
+ for (let ix = 0; ix < actualW; ix++) {
3577
+ if (maskData[mIdx] === skipVal) {
3578
+ dIdx++;
3579
+ mIdx++;
3580
+ continue;
3581
+ }
3582
+ const current = dst32[dIdx];
3583
+ const next = blendFn(baseColorWithGlobalAlpha, current);
3584
+ if (current !== next) {
3585
+ dst32[dIdx] = next;
3586
+ didChange = true;
3887
3587
  }
3588
+ dIdx++;
3589
+ mIdx++;
3888
3590
  }
3889
- tiles.length = 0;
3591
+ dIdx += dStride;
3592
+ mIdx += mStride;
3890
3593
  }
3891
- };
3594
+ return didChange;
3595
+ }
3892
3596
 
3893
- // src/History/PixelWriter.ts
3894
- var PixelWriter = class {
3895
- historyManager;
3896
- accumulator;
3897
- historyActionFactory;
3898
- config;
3899
- mutator;
3900
- constructor(target, mutatorFactory, {
3901
- tileSize = 256,
3902
- maxHistorySteps = 50,
3903
- historyManager = new HistoryManager(maxHistorySteps),
3904
- historyActionFactory = makeHistoryAction,
3905
- pixelTilePool
3906
- } = {}) {
3907
- this.config = new PixelEngineConfig(tileSize, target);
3908
- this.historyManager = historyManager;
3909
- pixelTilePool ??= new PixelTilePool(this.config);
3910
- this.accumulator = new PixelAccumulator(this.config, pixelTilePool);
3911
- this.historyActionFactory = historyActionFactory;
3912
- this.mutator = mutatorFactory(this);
3913
- }
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);
3924
- this.historyManager.commit(action);
3925
- }
3597
+ // src/History/PixelMutator/mutatorBlendPaintMask.ts
3598
+ var defaults13 = {
3599
+ blendColorPixelDataAlphaMask,
3600
+ blendColorPixelDataBinaryMask
3926
3601
  };
3602
+ var mutatorBlendPaintMask = ((writer, deps = defaults13) => {
3603
+ const {
3604
+ blendColorPixelDataBinaryMask: blendColorPixelDataBinaryMask2 = defaults13.blendColorPixelDataBinaryMask,
3605
+ blendColorPixelDataAlphaMask: blendColorPixelDataAlphaMask2 = defaults13.blendColorPixelDataAlphaMask
3606
+ } = deps;
3607
+ const OPTS = {
3608
+ x: 0,
3609
+ y: 0,
3610
+ blendFn: sourceOverPerfect,
3611
+ alpha: 255
3612
+ };
3613
+ return {
3614
+ blendColorPaintMask(color, mask, x, y, alpha = 255, blendFn = sourceOverPerfect) {
3615
+ const tx = x + mask.centerOffsetX;
3616
+ const ty = y + mask.centerOffsetY;
3617
+ const didChange = writer.accumulator.storeRegionBeforeState(tx, ty, mask.w, mask.h);
3618
+ OPTS.x = tx;
3619
+ OPTS.y = ty;
3620
+ OPTS.alpha = alpha;
3621
+ OPTS.blendFn = blendFn;
3622
+ if (mask.type === 1 /* BINARY */) {
3623
+ return didChange(blendColorPixelDataBinaryMask2(writer.config.target, color, mask, OPTS));
3624
+ } else {
3625
+ return didChange(blendColorPixelDataAlphaMask2(writer.config.target, color, mask, OPTS));
3626
+ }
3627
+ }
3628
+ };
3629
+ });
3927
3630
 
3928
3631
  // src/ImageData/copyImageData.ts
3929
3632
  function copyImageData({
@@ -4046,35 +3749,6 @@ function resampleImageData(source, factor) {
4046
3749
  return new ImageData(uint8ClampedArray, width, height);
4047
3750
  }
4048
3751
 
4049
- // src/ImageData/resizeImageData.ts
4050
- function resizeImageData(target, newWidth, newHeight, offsetX = 0, offsetY = 0) {
4051
- const result = new ImageData(newWidth, newHeight);
4052
- const {
4053
- width: oldW,
4054
- height: oldH,
4055
- data: oldData
4056
- } = target;
4057
- const newData = result.data;
4058
- const x0 = Math.max(0, offsetX);
4059
- const y0 = Math.max(0, offsetY);
4060
- const x1 = Math.min(newWidth, offsetX + oldW);
4061
- const y1 = Math.min(newHeight, offsetY + oldH);
4062
- if (x1 <= x0 || y1 <= y0) {
4063
- return result;
4064
- }
4065
- const rowCount = y1 - y0;
4066
- const rowLen = (x1 - x0) * 4;
4067
- for (let row = 0; row < rowCount; row++) {
4068
- const dstY = y0 + row;
4069
- const srcY = dstY - offsetY;
4070
- const srcX = x0 - offsetX;
4071
- const dstStart = (dstY * newWidth + x0) * 4;
4072
- const srcStart = (srcY * oldW + srcX) * 4;
4073
- newData.set(oldData.subarray(srcStart, srcStart + rowLen), dstStart);
4074
- }
4075
- return result;
4076
- }
4077
-
4078
3752
  // src/ImageData/ReusableImageData.ts
4079
3753
  function makeReusableImageData() {
4080
3754
  let imageData = null;
@@ -4489,62 +4163,6 @@ function makeBinaryMask(w, h, data) {
4489
4163
  };
4490
4164
  }
4491
4165
 
4492
- // src/Mask/CircleAlphaMask.ts
4493
- function makeCircleAlphaMask(size, fallOff = () => 1) {
4494
- const area = size * size;
4495
- const data = new Uint8Array(area);
4496
- const radius = size / 2;
4497
- const invR = 1 / radius;
4498
- const minOffset = -Math.ceil(radius - 0.5);
4499
- for (let y = 0; y < size; y++) {
4500
- for (let x = 0; x < size; x++) {
4501
- const dx = x - radius + 0.5;
4502
- const dy = y - radius + 0.5;
4503
- const distSqr = dx * dx + dy * dy;
4504
- if (distSqr <= radius * radius) {
4505
- const dist = Math.sqrt(distSqr);
4506
- data[y * size + x] = fallOff(1 - dist * invR) * 255 | 0;
4507
- }
4508
- }
4509
- }
4510
- return {
4511
- type: 0 /* ALPHA */,
4512
- data,
4513
- w: size,
4514
- h: size,
4515
- radius,
4516
- size,
4517
- minOffset
4518
- };
4519
- }
4520
-
4521
- // src/Mask/CircleBinaryMask.ts
4522
- function makeCircleBinaryMask(size) {
4523
- const area = size * size;
4524
- const data = new Uint8Array(area);
4525
- const radius = size / 2;
4526
- const minOffset = -Math.ceil(radius - 0.5);
4527
- for (let y = 0; y < size; y++) {
4528
- for (let x = 0; x < size; x++) {
4529
- const dx = x - radius + 0.5;
4530
- const dy = y - radius + 0.5;
4531
- const distSqr = dx * dx + dy * dy;
4532
- if (distSqr <= radius * radius) {
4533
- data[y * size + x] = 1;
4534
- }
4535
- }
4536
- }
4537
- return {
4538
- type: 1 /* BINARY */,
4539
- data,
4540
- w: size,
4541
- h: size,
4542
- radius,
4543
- size,
4544
- minOffset
4545
- };
4546
- }
4547
-
4548
4166
  // src/Mask/applyBinaryMaskToAlphaMask.ts
4549
4167
  function applyBinaryMaskToAlphaMask(alphaMaskDst, binaryMaskSrc, opts = {}) {
4550
4168
  const {
@@ -5245,103 +4863,137 @@ function writePixelDataBuffer(target, data, _x, _y, _w, _h) {
5245
4863
  }
5246
4864
  }
5247
4865
 
5248
- // src/PixelTile/PaintBuffer.ts
5249
- var PaintBuffer = class {
5250
- constructor(config, tilePool) {
5251
- this.config = config;
5252
- this.tilePool = tilePool;
5253
- this.lookup = [];
4866
+ // src/PixelData/writePaintBufferToPixelData.ts
4867
+ function writePaintBufferToPixelData(target, paintBuffer, writePixelDataBufferFn = writePixelDataBuffer) {
4868
+ const tileShift = paintBuffer.config.tileShift;
4869
+ const lookup = paintBuffer.lookup;
4870
+ for (let i = 0; i < lookup.length; i++) {
4871
+ const tile = lookup[i];
4872
+ if (tile) {
4873
+ const dx = tile.tx << tileShift;
4874
+ const dy = tile.ty << tileShift;
4875
+ writePixelDataBufferFn(target, tile.data32, dx, dy, tile.width, tile.height);
4876
+ }
5254
4877
  }
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;
4878
+ }
4879
+
4880
+ // src/Paint/makeCirclePaintAlphaMask.ts
4881
+ function makeCirclePaintAlphaMask(size, fallOff = (d) => d) {
4882
+ const area = size * size;
4883
+ const data = new Uint8Array(area);
4884
+ const radius = size / 2;
4885
+ const invR = 1 / radius;
4886
+ const centerOffset = -Math.ceil(radius - 0.5);
4887
+ for (let y = 0; y < size; y++) {
4888
+ const rowOffset = y * size;
4889
+ const dy = y - radius + 0.5;
4890
+ const dy2 = dy * dy;
4891
+ for (let x = 0; x < size; x++) {
4892
+ const dx = x - radius + 0.5;
4893
+ const distSqr = dx * dx + dy2;
4894
+ if (distSqr <= radius * radius) {
4895
+ const dist = Math.sqrt(distSqr) * invR;
4896
+ const strength = fallOff(1 - dist);
4897
+ if (strength > 0) {
4898
+ const intensity = strength * 255 | 0;
4899
+ data[rowOffset + x] = Math.max(0, Math.min(255, intensity));
5274
4900
  }
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
4901
  }
5282
4902
  }
5283
4903
  }
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
- }
4904
+ return {
4905
+ type: 0 /* ALPHA */,
4906
+ data,
4907
+ w: size,
4908
+ h: size,
4909
+ centerOffsetX: centerOffset,
4910
+ centerOffsetY: centerOffset
4911
+ };
4912
+ }
4913
+
4914
+ // src/Paint/makeCirclePaintBinaryMask.ts
4915
+ function makeCirclePaintBinaryMask(size) {
4916
+ const area = size * size;
4917
+ const data = new Uint8Array(area);
4918
+ const radius = size / 2;
4919
+ const centerOffset = -Math.ceil(radius - 0.5);
4920
+ for (let y = 0; y < size; y++) {
4921
+ for (let x = 0; x < size; x++) {
4922
+ const dx = x - radius + 0.5;
4923
+ const dy = y - radius + 0.5;
4924
+ const distSqr = dx * dx + dy * dy;
4925
+ if (distSqr <= radius * radius) {
4926
+ data[y * size + x] = 1;
5306
4927
  }
5307
- });
4928
+ }
5308
4929
  }
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
- }
4930
+ return {
4931
+ type: 1 /* BINARY */,
4932
+ data,
4933
+ w: size,
4934
+ h: size,
4935
+ centerOffsetX: centerOffset,
4936
+ centerOffsetY: centerOffset
4937
+ };
4938
+ }
4939
+
4940
+ // src/Paint/makePaintMask.ts
4941
+ function makePaintBinaryMask(mask) {
4942
+ return {
4943
+ type: 1 /* BINARY */,
4944
+ data: mask.data,
4945
+ w: mask.w,
4946
+ h: mask.h,
4947
+ centerOffsetX: -(mask.w >> 1),
4948
+ centerOffsetY: -(mask.h >> 1)
4949
+ };
4950
+ }
4951
+ function makePaintAlphaMask(mask) {
4952
+ return {
4953
+ type: 0 /* ALPHA */,
4954
+ data: mask.data,
4955
+ w: mask.w,
4956
+ h: mask.h,
4957
+ centerOffsetX: -(mask.w >> 1),
4958
+ centerOffsetY: -(mask.h >> 1)
4959
+ };
4960
+ }
4961
+
4962
+ // src/Paint/makeRectFalloffPaintAlphaMask.ts
4963
+ function makeRectFalloffPaintAlphaMask(width, height, fallOff = (d) => d) {
4964
+ const fPx = Math.floor(width / 2);
4965
+ const fPy = Math.floor(height / 2);
4966
+ const invHalfW = 2 / width;
4967
+ const invHalfH = 2 / height;
4968
+ const offX = width % 2 === 0 ? 0.5 : 0;
4969
+ const offY = height % 2 === 0 ? 0.5 : 0;
4970
+ const area = width * height;
4971
+ const data = new Uint8Array(area);
4972
+ for (let y = 0; y < height; y++) {
4973
+ const dy = Math.abs(y - fPy + offY) * invHalfH;
4974
+ const rowOffset = y * width;
4975
+ for (let x = 0; x < width; x++) {
4976
+ const dx = Math.abs(x - fPx + offX) * invHalfW;
4977
+ const dist = dx > dy ? dx : dy;
4978
+ const strength = fallOff(1 - dist);
4979
+ if (strength > 0) {
4980
+ const intensity = strength * 255 | 0;
4981
+ data[rowOffset + x] = Math.max(0, Math.min(255, intensity));
5335
4982
  }
5336
- });
5337
- }
5338
- clear() {
5339
- this.tilePool.releaseTiles(this.lookup);
4983
+ }
5340
4984
  }
5341
- };
4985
+ return {
4986
+ type: 0 /* ALPHA */,
4987
+ data,
4988
+ w: width,
4989
+ h: height,
4990
+ centerOffsetX: -macro_halfAndFloor(width),
4991
+ centerOffsetY: -macro_halfAndFloor(height)
4992
+ };
4993
+ }
5342
4994
 
5343
- // src/PixelTile/PaintBufferRenderer.ts
5344
- function makePaintBufferRenderer(paintBuffer, offscreenCanvasClass = OffscreenCanvas) {
4995
+ // src/Paint/PaintBufferCanvasRenderer.ts
4996
+ function makePaintBufferCanvasRenderer(paintBuffer, offscreenCanvasClass = OffscreenCanvas) {
5345
4997
  const config = paintBuffer.config;
5346
4998
  const tileSize = config.tileSize;
5347
4999
  const tileShift = config.tileShift;
@@ -5350,22 +5002,27 @@ function makePaintBufferRenderer(paintBuffer, offscreenCanvasClass = OffscreenCa
5350
5002
  const ctx = canvas.getContext("2d");
5351
5003
  if (!ctx) throw new Error(CANVAS_CTX_FAILED);
5352
5004
  ctx.imageSmoothingEnabled = false;
5353
- return function drawPaintBuffer(target) {
5005
+ return function drawPaintBuffer(targetCtx, alpha = 255, compOperation = "source-over") {
5006
+ targetCtx.globalAlpha = alpha / 255;
5007
+ targetCtx.globalCompositeOperation = compOperation;
5354
5008
  for (let i = 0; i < lookup.length; i++) {
5355
5009
  const tile = lookup[i];
5356
5010
  if (tile) {
5357
5011
  const dx = tile.tx << tileShift;
5358
5012
  const dy = tile.ty << tileShift;
5359
5013
  ctx.putImageData(tile.imageData, 0, 0);
5360
- target.drawImage(canvas, dx, dy);
5014
+ targetCtx.drawImage(canvas, dx, dy);
5361
5015
  }
5362
5016
  }
5017
+ targetCtx.globalAlpha = 1;
5018
+ targetCtx.globalCompositeOperation = "source-over";
5363
5019
  };
5364
5020
  }
5365
5021
  export {
5366
5022
  BASE_FAST_BLEND_MODE_FUNCTIONS,
5367
5023
  BASE_PERFECT_BLEND_MODE_FUNCTIONS,
5368
5024
  BaseBlendMode,
5025
+ CANVAS_COMPOSITE_MAP,
5369
5026
  CANVAS_CTX_FAILED,
5370
5027
  HistoryManager,
5371
5028
  IndexedImage,
@@ -5384,13 +5041,11 @@ export {
5384
5041
  applyBinaryMaskToAlphaMask,
5385
5042
  applyBinaryMaskToPixelData,
5386
5043
  applyPatchTiles,
5387
- applyRectBrushToPixelData,
5388
5044
  base64DecodeArrayBuffer,
5389
5045
  base64EncodeArrayBuffer,
5390
5046
  blendColorPixelData,
5391
5047
  blendColorPixelDataAlphaMask,
5392
5048
  blendColorPixelDataBinaryMask,
5393
- blendColorPixelDataCircleMask,
5394
5049
  blendPixel,
5395
5050
  blendPixelData,
5396
5051
  blendPixelDataAlphaMask,
@@ -5432,12 +5087,8 @@ export {
5432
5087
  fillPixelDataFast,
5433
5088
  floodFillSelection,
5434
5089
  forEachLinePoint,
5435
- getCircleBrushOrPencilBounds,
5436
- getCircleBrushOrPencilStrokeBounds,
5437
5090
  getImageDataFromClipboard,
5438
5091
  getIndexedImageColorCounts,
5439
- getRectBrushOrPencilBounds,
5440
- getRectBrushOrPencilStrokeBounds,
5441
5092
  getRectsBounds,
5442
5093
  getSupportedPixelFormats,
5443
5094
  hardLightFast,
@@ -5471,15 +5122,18 @@ export {
5471
5122
  makeBinaryMask,
5472
5123
  makeBlendModeRegistry,
5473
5124
  makeCanvasFrameRenderer,
5474
- makeCircleAlphaMask,
5475
- makeCircleBinaryMask,
5125
+ makeCirclePaintAlphaMask,
5126
+ makeCirclePaintBinaryMask,
5476
5127
  makeFastBlendModeRegistry,
5477
5128
  makeFullPixelMutator,
5478
5129
  makeHistoryAction,
5479
5130
  makeImageDataLike,
5480
- makePaintBufferRenderer,
5131
+ makePaintAlphaMask,
5132
+ makePaintBinaryMask,
5133
+ makePaintBufferCanvasRenderer,
5481
5134
  makePerfectBlendModeRegistry,
5482
5135
  makePixelCanvas,
5136
+ makeRectFalloffPaintAlphaMask,
5483
5137
  makeReusableCanvas,
5484
5138
  makeReusableImageData,
5485
5139
  makeReusableOffscreenCanvas,
@@ -5491,15 +5145,8 @@ export {
5491
5145
  multiplyPerfect,
5492
5146
  mutatorApplyAlphaMask,
5493
5147
  mutatorApplyBinaryMask,
5494
- mutatorApplyCircleBrushStroke,
5495
- mutatorApplyCirclePencil,
5496
- mutatorApplyCirclePencilStroke,
5497
- mutatorApplyRectBrush,
5498
- mutatorApplyRectBrushStroke,
5499
- mutatorApplyRectPencil,
5500
- mutatorApplyRectPencilStroke,
5501
5148
  mutatorBlendColor,
5502
- mutatorBlendColorCircleMask,
5149
+ mutatorBlendPaintMask,
5503
5150
  mutatorBlendPixel,
5504
5151
  mutatorBlendPixelData,
5505
5152
  mutatorBlendPixelDataAlphaMask,
@@ -5539,6 +5186,7 @@ export {
5539
5186
  subtractFast,
5540
5187
  subtractPerfect,
5541
5188
  toBlendModeIndexAndName,
5189
+ trimMaskRectBounds,
5542
5190
  trimRectBounds,
5543
5191
  uInt32ArrayToImageData,
5544
5192
  uInt32ArrayToImageDataLike,
@@ -5554,6 +5202,7 @@ export {
5554
5202
  writeImageDataBuffer,
5555
5203
  writeImageDataToClipboard,
5556
5204
  writeImgBlobToClipboard,
5205
+ writePaintBufferToPixelData,
5557
5206
  writePixelDataBuffer
5558
5207
  };
5559
5208
  //# sourceMappingURL=index.prod.js.map