pixel-data-js 0.24.0 → 0.25.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/dist/index.dev.cjs +1431 -1845
  2. package/dist/index.dev.cjs.map +1 -1
  3. package/dist/index.dev.js +1297 -1702
  4. package/dist/index.dev.js.map +1 -1
  5. package/dist/index.prod.cjs +1305 -1719
  6. package/dist/index.prod.cjs.map +1 -1
  7. package/dist/index.prod.d.ts +220 -328
  8. package/dist/index.prod.js +1423 -1828
  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/mutatorBlendPixelData.ts +2 -2
  16. package/src/History/PixelMutator/mutatorBlendPixelDataAlphaMask.ts +2 -2
  17. package/src/History/PixelMutator/mutatorBlendPixelDataBinaryMask.ts +2 -2
  18. package/src/History/PixelMutator.ts +0 -20
  19. package/src/History/PixelPatchTiles.ts +2 -2
  20. package/src/History/PixelWriter.ts +132 -9
  21. package/src/Internal/helpers.ts +2 -0
  22. package/src/Paint/PaintBuffer.ts +269 -0
  23. package/src/{PixelTile/PaintBufferRenderer.ts → Paint/PaintBufferCanvasRenderer.ts} +13 -5
  24. package/src/Paint/makeCirclePaintAlphaMask.ts +41 -0
  25. package/src/{Mask/CircleBinaryMask.ts → Paint/makeCirclePaintBinaryMask.ts} +5 -6
  26. package/src/Paint/makePaintMask.ts +28 -0
  27. package/src/Paint/makeRectFalloffPaintAlphaMask.ts +47 -0
  28. package/src/PixelData/PixelBuffer32.ts +2 -2
  29. package/src/PixelData/PixelData.ts +1 -1
  30. package/src/PixelData/applyAlphaMaskToPixelData.ts +2 -2
  31. package/src/PixelData/applyBinaryMaskToPixelData.ts +2 -2
  32. package/src/PixelData/blendColorPixelData.ts +2 -2
  33. package/src/PixelData/blendColorPixelDataAlphaMask.ts +3 -3
  34. package/src/PixelData/blendColorPixelDataBinaryMask.ts +3 -3
  35. package/src/PixelData/blendPixel.ts +2 -2
  36. package/src/PixelData/blendPixelData.ts +3 -3
  37. package/src/PixelData/blendPixelDataAlphaMask.ts +3 -3
  38. package/src/PixelData/blendPixelDataBinaryMask.ts +3 -3
  39. package/src/PixelData/blendPixelDataPaintBuffer.ts +3 -3
  40. package/src/PixelData/clearPixelData.ts +2 -2
  41. package/src/PixelData/extractPixelData.ts +4 -4
  42. package/src/PixelData/extractPixelDataBuffer.ts +4 -4
  43. package/src/PixelData/fillPixelData.ts +5 -5
  44. package/src/PixelData/fillPixelDataBinaryMask.ts +3 -3
  45. package/src/PixelData/fillPixelDataFast.ts +5 -5
  46. package/src/PixelData/invertPixelData.ts +2 -2
  47. package/src/PixelData/pixelDataToAlphaMask.ts +2 -2
  48. package/src/PixelData/reflectPixelData.ts +3 -3
  49. package/src/PixelData/resamplePixelData.ts +2 -2
  50. package/src/PixelData/writePaintBufferToPixelData.ts +26 -0
  51. package/src/PixelData/writePixelDataBuffer.ts +5 -5
  52. package/src/Rect/trimMaskRectBounds.ts +121 -0
  53. package/src/Rect/trimRectBounds.ts +25 -116
  54. package/src/_types.ts +16 -15
  55. package/src/index.ts +9 -24
  56. package/src/History/PixelMutator/mutatorApplyCircleBrushStroke.ts +0 -182
  57. package/src/History/PixelMutator/mutatorApplyCirclePencil.ts +0 -59
  58. package/src/History/PixelMutator/mutatorApplyCirclePencilStroke.ts +0 -172
  59. package/src/History/PixelMutator/mutatorApplyRectBrush.ts +0 -64
  60. package/src/History/PixelMutator/mutatorApplyRectBrushStroke.ts +0 -184
  61. package/src/History/PixelMutator/mutatorApplyRectPencil.ts +0 -65
  62. package/src/History/PixelMutator/mutatorApplyRectPencilStroke.ts +0 -166
  63. package/src/History/PixelMutator/mutatorBlendColorCircleMask.ts +0 -71
  64. package/src/Mask/CircleAlphaMask.ts +0 -32
  65. package/src/PixelData/applyRectBrushToPixelData.ts +0 -98
  66. package/src/PixelData/blendColorPixelDataCircleMask.ts +0 -92
  67. package/src/PixelTile/PaintBuffer.ts +0 -122
  68. package/src/Rect/getCircleBrushOrPencilBounds.ts +0 -43
  69. package/src/Rect/getCircleBrushOrPencilStrokeBounds.ts +0 -24
  70. package/src/Rect/getRectBrushOrPencilBounds.ts +0 -38
  71. package/src/Rect/getRectBrushOrPencilStrokeBounds.ts +0 -26
package/dist/index.dev.js CHANGED
@@ -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,
@@ -1811,11 +1811,11 @@ var PixelAccumulator = class {
1811
1811
  * @param y pixel y coordinate
1812
1812
  */
1813
1813
  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;
1814
+ const shift = this.config.tileShift;
1815
+ const columns = this.config.targetColumns;
1816
+ const tx = x >> shift;
1817
+ const ty = y >> shift;
1818
+ const id = ty * columns + tx;
1819
1819
  let tile = this.lookup[id];
1820
1820
  let added = false;
1821
1821
  if (!tile) {
@@ -1841,16 +1841,16 @@ var PixelAccumulator = class {
1841
1841
  * @param h pixel height
1842
1842
  */
1843
1843
  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;
1844
+ const shift = this.config.tileShift;
1845
+ const columns = this.config.targetColumns;
1846
+ const startX = x >> shift;
1847
+ const startY = y >> shift;
1848
+ const endX = x + w - 1 >> shift;
1849
+ const endY = y + h - 1 >> shift;
1850
+ const startIndex = this.beforeTiles.length;
1851
1851
  for (let ty = startY; ty <= endY; ty++) {
1852
1852
  for (let tx = startX; tx <= endX; tx++) {
1853
- let id = ty * columns + tx;
1853
+ const id = ty * columns + tx;
1854
1854
  let tile = this.lookup[id];
1855
1855
  if (!tile) {
1856
1856
  tile = this.tilePool.getTile(id, tx, ty);
@@ -1862,7 +1862,7 @@ var PixelAccumulator = class {
1862
1862
  }
1863
1863
  return (didChange) => {
1864
1864
  if (!didChange) {
1865
- let length = this.beforeTiles.length;
1865
+ const length = this.beforeTiles.length;
1866
1866
  for (let i = startIndex; i < length; i++) {
1867
1867
  let t = this.beforeTiles[i];
1868
1868
  if (t) {
@@ -1875,15 +1875,34 @@ var PixelAccumulator = class {
1875
1875
  return didChange;
1876
1876
  };
1877
1877
  }
1878
+ storeTileBeforeState(id, tx, ty) {
1879
+ let tile = this.lookup[id];
1880
+ let added = false;
1881
+ if (!tile) {
1882
+ tile = this.tilePool.getTile(id, tx, ty);
1883
+ this.extractState(tile);
1884
+ this.lookup[id] = tile;
1885
+ this.beforeTiles.push(tile);
1886
+ added = true;
1887
+ }
1888
+ return (didChange) => {
1889
+ if (!didChange && added) {
1890
+ this.beforeTiles.pop();
1891
+ this.lookup[id] = void 0;
1892
+ this.tilePool.releaseTile(tile);
1893
+ }
1894
+ return didChange;
1895
+ };
1896
+ }
1878
1897
  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;
1898
+ const target = this.config.target;
1899
+ const TILE_SIZE = this.config.tileSize;
1900
+ const dst = tile.data32;
1901
+ const src = target.data32;
1902
+ const startX = tile.tx * TILE_SIZE;
1903
+ const startY = tile.ty * TILE_SIZE;
1904
+ const targetWidth = target.width;
1905
+ const targetHeight = target.height;
1887
1906
  if (startX >= targetWidth || startX + TILE_SIZE <= 0 || startY >= targetHeight || startY + TILE_SIZE <= 0) {
1888
1907
  dst.fill(0);
1889
1908
  return;
@@ -1909,8 +1928,8 @@ var PixelAccumulator = class {
1909
1928
  }
1910
1929
  }
1911
1930
  extractPatch() {
1912
- let afterTiles = [];
1913
- let length = this.beforeTiles.length;
1931
+ const afterTiles = [];
1932
+ const length = this.beforeTiles.length;
1914
1933
  for (let i = 0; i < length; i++) {
1915
1934
  let beforeTile = this.beforeTiles[i];
1916
1935
  if (beforeTile) {
@@ -1919,7 +1938,7 @@ var PixelAccumulator = class {
1919
1938
  afterTiles.push(afterTile);
1920
1939
  }
1921
1940
  }
1922
- let beforeTiles = this.beforeTiles;
1941
+ const beforeTiles = this.beforeTiles;
1923
1942
  this.beforeTiles = [];
1924
1943
  this.lookup.length = 0;
1925
1944
  return {
@@ -1927,10 +1946,10 @@ var PixelAccumulator = class {
1927
1946
  afterTiles
1928
1947
  };
1929
1948
  }
1930
- rollback() {
1931
- let target = this.config.target;
1932
- let tileSize = this.config.tileSize;
1933
- let length = this.beforeTiles.length;
1949
+ rollbackAfterError() {
1950
+ const target = this.config.target;
1951
+ const tileSize = this.config.tileSize;
1952
+ const length = this.beforeTiles.length;
1934
1953
  applyPatchTiles(target, this.beforeTiles, tileSize);
1935
1954
  for (let i = 0; i < length; i++) {
1936
1955
  let tile = this.beforeTiles[i];
@@ -1954,6 +1973,7 @@ var PixelEngineConfig = class {
1954
1973
  tileArea;
1955
1974
  target;
1956
1975
  targetColumns = 0;
1976
+ targetRows = 0;
1957
1977
  constructor(tileSize, target) {
1958
1978
  if ((tileSize & tileSize - 1) !== 0) {
1959
1979
  throw new Error("tileSize must be a power of 2");
@@ -1962,28 +1982,26 @@ var PixelEngineConfig = class {
1962
1982
  this.tileShift = 31 - Math.clz32(tileSize);
1963
1983
  this.tileMask = tileSize - 1;
1964
1984
  this.tileArea = tileSize * tileSize;
1965
- this.setTarget(target);
1966
- }
1967
- setTarget(target) {
1968
- ;
1969
1985
  this.target = target;
1970
1986
  this.targetColumns = target.width + this.tileMask >> this.tileShift;
1987
+ this.targetRows = target.height + this.tileMask >> this.tileShift;
1971
1988
  }
1972
1989
  };
1973
1990
 
1974
- // src/PixelData/applyAlphaMaskToPixelData.ts
1975
- function applyAlphaMaskToPixelData(dst, mask, opts = {}) {
1991
+ // src/PixelData/blendColorPixelData.ts
1992
+ function blendColorPixelData(dst, color, opts = {}) {
1976
1993
  const {
1977
1994
  x: targetX = 0,
1978
1995
  y: targetY = 0,
1979
1996
  w: width = dst.width,
1980
1997
  h: height = dst.height,
1981
1998
  alpha: globalAlpha = 255,
1982
- mx = 0,
1983
- my = 0,
1984
- invertMask = false
1999
+ blendFn = sourceOverPerfect
1985
2000
  } = opts;
1986
2001
  if (globalAlpha === 0) return false;
2002
+ const baseSrcAlpha = color >>> 24;
2003
+ const isOverwrite = blendFn.isOverwrite || false;
2004
+ if (baseSrcAlpha === 0 && !isOverwrite) return false;
1987
2005
  let x = targetX;
1988
2006
  let y = targetY;
1989
2007
  let w = width;
@@ -1996,82 +2014,46 @@ function applyAlphaMaskToPixelData(dst, mask, opts = {}) {
1996
2014
  h += y;
1997
2015
  y = 0;
1998
2016
  }
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;
2017
+ const actualW = Math.min(w, dst.width - x);
2018
+ const actualH = Math.min(h, dst.height - y);
2019
+ if (actualW <= 0 || actualH <= 0) return false;
2020
+ let finalSrcColor = color;
2021
+ if (globalAlpha < 255) {
2022
+ const a = baseSrcAlpha * globalAlpha + 128 >> 8;
2023
+ if (a === 0 && !isOverwrite) return false;
2024
+ finalSrcColor = (color & 16777215 | a << 24) >>> 0;
2025
+ }
2017
2026
  const dst32 = dst.data32;
2018
2027
  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;
2028
+ let dIdx = y * dw + x | 0;
2029
+ const dStride = dw - actualW | 0;
2024
2030
  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;
2031
+ for (let iy = 0; iy < actualH; iy++) {
2032
+ for (let ix = 0; ix < actualW; ix++) {
2033
+ const current = dst32[dIdx];
2034
+ const next = blendFn(finalSrcColor, current);
2035
+ if (current !== next) {
2036
+ dst32[dIdx] = next;
2041
2037
  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
2038
  }
2055
2039
  dIdx++;
2056
- mIdx++;
2057
2040
  }
2058
2041
  dIdx += dStride;
2059
- mIdx += mStride;
2060
2042
  }
2061
2043
  return didChange;
2062
2044
  }
2063
2045
 
2064
- // src/History/PixelMutator/mutatorApplyAlphaMask.ts
2046
+ // src/History/PixelMutator/mutatorBlendColor.ts
2065
2047
  var defaults2 = {
2066
- applyAlphaMaskToPixelData
2048
+ blendColorPixelData
2067
2049
  };
2068
- var mutatorApplyAlphaMask = ((writer, deps = defaults2) => {
2050
+ var mutatorBlendColor = ((writer, deps = defaults2) => {
2069
2051
  const {
2070
- applyAlphaMaskToPixelData: applyAlphaMaskToPixelData2 = defaults2.applyAlphaMaskToPixelData
2052
+ blendColorPixelData: blendColorPixelData2 = defaults2.blendColorPixelData
2071
2053
  } = deps;
2072
2054
  return {
2073
- applyAlphaMask(mask, opts = {}) {
2074
- let target = writer.config.target;
2055
+ blendColor(color, opts = {}) {
2056
+ const target = writer.config.target;
2075
2057
  const {
2076
2058
  x = 0,
2077
2059
  y = 0,
@@ -2079,159 +2061,227 @@ var mutatorApplyAlphaMask = ((writer, deps = defaults2) => {
2079
2061
  h = target.height
2080
2062
  } = opts;
2081
2063
  const didChange = writer.accumulator.storeRegionBeforeState(x, y, w, h);
2082
- return didChange(applyAlphaMaskToPixelData2(target, mask, opts));
2064
+ return didChange(blendColorPixelData2(target, color, opts));
2083
2065
  }
2084
2066
  };
2085
2067
  });
2086
2068
 
2087
- // src/PixelData/applyBinaryMaskToPixelData.ts
2088
- function applyBinaryMaskToPixelData(dst, mask, opts = {}) {
2069
+ // src/PixelData/blendPixel.ts
2070
+ function blendPixel(target, x, y, color, alpha = 255, blendFn = sourceOverPerfect) {
2071
+ if (alpha === 0) return false;
2072
+ let width = target.width;
2073
+ let height = target.height;
2074
+ if (x < 0 || x >= width || y < 0 || y >= height) return false;
2075
+ let srcAlpha = color >>> 24;
2076
+ let isOverwrite = blendFn.isOverwrite;
2077
+ if (srcAlpha === 0 && !isOverwrite) return false;
2078
+ let dst32 = target.data32;
2079
+ let index = y * width + x;
2080
+ let finalColor = color;
2081
+ if (alpha !== 255) {
2082
+ let finalAlpha = srcAlpha * alpha + 128 >> 8;
2083
+ if (finalAlpha === 0 && !isOverwrite) return false;
2084
+ finalColor = (color & 16777215 | finalAlpha << 24) >>> 0;
2085
+ }
2086
+ let current = dst32[index];
2087
+ let next = blendFn(finalColor, current);
2088
+ if (current !== next) {
2089
+ dst32[index] = next;
2090
+ return true;
2091
+ }
2092
+ return false;
2093
+ }
2094
+
2095
+ // src/History/PixelMutator/mutatorBlendPixel.ts
2096
+ var defaults3 = {
2097
+ blendPixel
2098
+ };
2099
+ var mutatorBlendPixel = ((writer, deps = defaults3) => {
2100
+ const {
2101
+ blendPixel: blendPixel2 = defaults3.blendPixel
2102
+ } = deps;
2103
+ return {
2104
+ blendPixel(x, y, color, alpha, blendFn) {
2105
+ const didChange = writer.accumulator.storePixelBeforeState(x, y);
2106
+ return didChange(blendPixel2(writer.config.target, x, y, color, alpha, blendFn));
2107
+ }
2108
+ };
2109
+ });
2110
+
2111
+ // src/PixelData/blendPixelData.ts
2112
+ function blendPixelData(dst, src, opts = {}) {
2089
2113
  const {
2090
2114
  x: targetX = 0,
2091
2115
  y: targetY = 0,
2092
- w: width = dst.width,
2093
- h: height = dst.height,
2116
+ sx: sourceX = 0,
2117
+ sy: sourceY = 0,
2118
+ w: width = src.width,
2119
+ h: height = src.height,
2094
2120
  alpha: globalAlpha = 255,
2095
- mx = 0,
2096
- my = 0,
2097
- invertMask = false
2121
+ blendFn = sourceOverPerfect
2098
2122
  } = opts;
2099
2123
  if (globalAlpha === 0) return false;
2100
2124
  let x = targetX;
2101
2125
  let y = targetY;
2126
+ let sx = sourceX;
2127
+ let sy = sourceY;
2102
2128
  let w = width;
2103
2129
  let h = height;
2130
+ if (sx < 0) {
2131
+ x -= sx;
2132
+ w += sx;
2133
+ sx = 0;
2134
+ }
2135
+ if (sy < 0) {
2136
+ y -= sy;
2137
+ h += sy;
2138
+ sy = 0;
2139
+ }
2140
+ w = Math.min(w, src.width - sx);
2141
+ h = Math.min(h, src.height - sy);
2104
2142
  if (x < 0) {
2143
+ sx -= x;
2105
2144
  w += x;
2106
2145
  x = 0;
2107
2146
  }
2108
2147
  if (y < 0) {
2148
+ sy -= y;
2109
2149
  h += y;
2110
2150
  y = 0;
2111
2151
  }
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;
2152
+ const actualW = Math.min(w, dst.width - x);
2153
+ const actualH = Math.min(h, dst.height - y);
2154
+ if (actualW <= 0 || actualH <= 0) return false;
2130
2155
  const dst32 = dst.data32;
2156
+ const src32 = src.data32;
2131
2157
  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;
2158
+ const sw = src.width;
2159
+ let dIdx = y * dw + x | 0;
2160
+ let sIdx = sy * sw + sx | 0;
2161
+ const dStride = dw - actualW | 0;
2162
+ const sStride = sw - actualW | 0;
2163
+ const isOpaque = globalAlpha === 255;
2164
+ const isOverwrite = blendFn.isOverwrite;
2137
2165
  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
- }
2166
+ for (let iy = 0; iy < actualH; iy++) {
2167
+ for (let ix = 0; ix < actualW; ix++) {
2168
+ const srcCol = src32[sIdx];
2169
+ const srcAlpha = srcCol >>> 24;
2170
+ if (srcAlpha === 0 && !isOverwrite) {
2171
+ dIdx++;
2172
+ sIdx++;
2173
+ continue;
2174
+ }
2175
+ let finalCol = srcCol;
2176
+ if (!isOpaque) {
2177
+ const a = srcAlpha * globalAlpha + 128 >> 8;
2178
+ if (a === 0 && !isOverwrite) {
2179
+ dIdx++;
2180
+ sIdx++;
2181
+ continue;
2159
2182
  }
2183
+ finalCol = (srcCol & 16777215 | a << 24) >>> 0;
2160
2184
  }
2161
- dIdx++;
2162
- mIdx++;
2185
+ const current = dst32[dIdx];
2186
+ const next = blendFn(finalCol, dst32[dIdx]);
2187
+ if (current !== next) {
2188
+ dst32[dIdx] = next;
2189
+ didChange = true;
2190
+ }
2191
+ dIdx++;
2192
+ sIdx++;
2163
2193
  }
2164
2194
  dIdx += dStride;
2165
- mIdx += mStride;
2195
+ sIdx += sStride;
2166
2196
  }
2167
2197
  return didChange;
2168
2198
  }
2169
2199
 
2170
- // src/History/PixelMutator/mutatorApplyBinaryMask.ts
2171
- var defaults3 = {
2172
- applyBinaryMaskToPixelData
2200
+ // src/History/PixelMutator/mutatorBlendPixelData.ts
2201
+ var defaults4 = {
2202
+ blendPixelData
2173
2203
  };
2174
- var mutatorApplyBinaryMask = ((writer, deps = defaults3) => {
2204
+ var mutatorBlendPixelData = ((writer, deps = defaults4) => {
2175
2205
  const {
2176
- applyBinaryMaskToPixelData: applyBinaryMaskToPixelData2 = defaults3.applyBinaryMaskToPixelData
2206
+ blendPixelData: blendPixelData2 = defaults4.blendPixelData
2177
2207
  } = deps;
2178
2208
  return {
2179
- applyBinaryMask(mask, opts = {}) {
2180
- let target = writer.config.target;
2209
+ blendPixelData(src, opts = {}) {
2181
2210
  const {
2182
2211
  x = 0,
2183
2212
  y = 0,
2184
- w = target.width,
2185
- h = target.height
2213
+ w = src.width,
2214
+ h = src.height
2186
2215
  } = opts;
2187
2216
  const didChange = writer.accumulator.storeRegionBeforeState(x, y, w, h);
2188
- return didChange(applyBinaryMaskToPixelData2(target, mask, opts));
2217
+ return didChange(blendPixelData2(writer.config.target, src, opts));
2189
2218
  }
2190
2219
  };
2191
2220
  });
2192
2221
 
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;
2222
+ // src/PixelData/blendPixelDataAlphaMask.ts
2223
+ function blendPixelDataAlphaMask(dst, src, alphaMask, opts = {}) {
2224
+ const {
2225
+ x: targetX = 0,
2226
+ y: targetY = 0,
2227
+ sx: sourceX = 0,
2228
+ sy: sourceY = 0,
2229
+ w: width = src.width,
2230
+ h: height = src.height,
2231
+ alpha: globalAlpha = 255,
2232
+ blendFn = sourceOverPerfect,
2233
+ mx = 0,
2234
+ my = 0,
2235
+ invertMask = false
2236
+ } = opts;
2204
2237
  if (globalAlpha === 0) return false;
2205
- const baseSrcAlpha = color >>> 24;
2206
- const isOverwrite = blendFn.isOverwrite || false;
2207
- if (baseSrcAlpha === 0 && !isOverwrite) return false;
2208
2238
  let x = targetX;
2209
2239
  let y = targetY;
2210
- let actualW = w;
2211
- let actualH = h;
2240
+ let sx = sourceX;
2241
+ let sy = sourceY;
2242
+ let w = width;
2243
+ let h = height;
2244
+ if (sx < 0) {
2245
+ x -= sx;
2246
+ w += sx;
2247
+ sx = 0;
2248
+ }
2249
+ if (sy < 0) {
2250
+ y -= sy;
2251
+ h += sy;
2252
+ sy = 0;
2253
+ }
2254
+ w = Math.min(w, src.width - sx);
2255
+ h = Math.min(h, src.height - sy);
2212
2256
  if (x < 0) {
2213
- actualW += x;
2257
+ sx -= x;
2258
+ w += x;
2214
2259
  x = 0;
2215
2260
  }
2216
2261
  if (y < 0) {
2217
- actualH += y;
2262
+ sy -= y;
2263
+ h += y;
2218
2264
  y = 0;
2219
2265
  }
2220
- actualW = Math.min(actualW, dst.width - x);
2221
- actualH = Math.min(actualH, dst.height - y);
2266
+ const actualW = Math.min(w, dst.width - x);
2267
+ const actualH = Math.min(h, dst.height - y);
2222
2268
  if (actualW <= 0 || actualH <= 0) return false;
2269
+ const dw = dst.width;
2270
+ const sw = src.width;
2271
+ const mPitch = alphaMask.w;
2272
+ const maskData = alphaMask.data;
2223
2273
  const dx = x - targetX | 0;
2224
2274
  const dy = y - targetY | 0;
2225
2275
  const dst32 = dst.data32;
2226
- const dw = dst.width;
2227
- const mPitch = mask.w;
2228
- const maskData = mask.data;
2276
+ const src32 = src.data32;
2229
2277
  let dIdx = y * dw + x | 0;
2278
+ let sIdx = sy * sw + sx | 0;
2230
2279
  let mIdx = (my + dy) * mPitch + (mx + dx) | 0;
2231
2280
  const dStride = dw - actualW | 0;
2281
+ const sStride = sw - actualW | 0;
2232
2282
  const mStride = mPitch - actualW | 0;
2233
2283
  const isOpaque = globalAlpha === 255;
2234
- const colorRGB = color & 16777215;
2284
+ const isOverwrite = blendFn.isOverwrite || false;
2235
2285
  let didChange = false;
2236
2286
  for (let iy = 0; iy < actualH; iy++) {
2237
2287
  for (let ix = 0; ix < actualW; ix++) {
@@ -2239,6 +2289,15 @@ function blendColorPixelDataAlphaMask(dst, color, mask, opts = {}) {
2239
2289
  const effM = invertMask ? 255 - mVal : mVal;
2240
2290
  if (effM === 0) {
2241
2291
  dIdx++;
2292
+ sIdx++;
2293
+ mIdx++;
2294
+ continue;
2295
+ }
2296
+ const srcCol = src32[sIdx];
2297
+ const srcAlpha = srcCol >>> 24;
2298
+ if (srcAlpha === 0 && !isOverwrite) {
2299
+ dIdx++;
2300
+ sIdx++;
2242
2301
  mIdx++;
2243
2302
  continue;
2244
2303
  }
@@ -2250,1268 +2309,174 @@ function blendColorPixelDataAlphaMask(dst, color, mask, opts = {}) {
2250
2309
  }
2251
2310
  if (weight === 0) {
2252
2311
  dIdx++;
2312
+ sIdx++;
2253
2313
  mIdx++;
2254
2314
  continue;
2255
2315
  }
2256
- let finalCol = color;
2316
+ let finalCol = srcCol;
2257
2317
  if (weight < 255) {
2258
- const a = baseSrcAlpha * weight + 128 >> 8;
2318
+ const a = srcAlpha * weight + 128 >> 8;
2259
2319
  if (a === 0 && !isOverwrite) {
2260
2320
  dIdx++;
2321
+ sIdx++;
2261
2322
  mIdx++;
2262
2323
  continue;
2263
2324
  }
2264
- finalCol = (colorRGB | a << 24) >>> 0;
2325
+ finalCol = (srcCol & 16777215 | a << 24) >>> 0;
2265
2326
  }
2266
2327
  const current = dst32[dIdx];
2267
- const next = blendFn(finalCol, current);
2328
+ const next = blendFn(finalCol, dst32[dIdx]);
2268
2329
  if (current !== next) {
2269
2330
  dst32[dIdx] = next;
2270
2331
  didChange = true;
2271
2332
  }
2272
2333
  dIdx++;
2334
+ sIdx++;
2273
2335
  mIdx++;
2274
2336
  }
2275
2337
  dIdx += dStride;
2338
+ sIdx += sStride;
2276
2339
  mIdx += mStride;
2277
2340
  }
2278
2341
  return didChange;
2279
2342
  }
2280
2343
 
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
2344
+ // src/History/PixelMutator/mutatorBlendPixelDataAlphaMask.ts
2345
+ var defaults5 = {
2346
+ blendPixelDataAlphaMask
2329
2347
  };
2330
- var mutatorApplyCircleBrushStroke = ((writer, deps = defaults4) => {
2348
+ var mutatorBlendPixelDataAlphaMask = ((writer, deps = defaults5) => {
2331
2349
  const {
2332
- forEachLinePoint: forEachLinePoint2 = defaults4.forEachLinePoint,
2333
- blendColorPixelDataAlphaMask: blendColorPixelDataAlphaMask2 = defaults4.blendColorPixelDataAlphaMask,
2334
- getCircleBrushOrPencilBounds: getCircleBrushOrPencilBounds2 = defaults4.getCircleBrushOrPencilBounds,
2335
- getCircleBrushOrPencilStrokeBounds: getCircleBrushOrPencilStrokeBounds2 = defaults4.getCircleBrushOrPencilStrokeBounds
2350
+ blendPixelDataAlphaMask: blendPixelDataAlphaMask2 = defaults5.blendPixelDataAlphaMask
2336
2351
  } = 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
2352
  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);
2353
+ blendPixelDataAlphaMask(src, mask, opts = {}) {
2354
+ const x = opts.x ?? 0;
2355
+ const y = opts.y ?? 0;
2356
+ const w = opts.w ?? src.width;
2357
+ const h = opts.h ?? src.height;
2358
+ const didChange = writer.accumulator.storeRegionBeforeState(x, y, w, h);
2359
+ return didChange(blendPixelDataAlphaMask2(writer.config.target, src, mask, opts));
2420
2360
  }
2421
2361
  };
2422
2362
  });
2423
2363
 
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;
2364
+ // src/PixelData/blendPixelDataBinaryMask.ts
2365
+ function blendPixelDataBinaryMask(dst, src, binaryMask, opts = {}) {
2366
+ const {
2367
+ x: targetX = 0,
2368
+ y: targetY = 0,
2369
+ sx: sourceX = 0,
2370
+ sy: sourceY = 0,
2371
+ w: width = src.width,
2372
+ h: height = src.height,
2373
+ alpha: globalAlpha = 255,
2374
+ blendFn = sourceOverPerfect,
2375
+ mx = 0,
2376
+ my = 0,
2377
+ invertMask = false
2378
+ } = opts;
2435
2379
  if (globalAlpha === 0) return false;
2436
- const baseSrcAlpha = color >>> 24;
2437
- const isOverwrite = blendFn.isOverwrite || false;
2438
- if (baseSrcAlpha === 0 && !isOverwrite) return false;
2439
2380
  let x = targetX;
2440
2381
  let y = targetY;
2382
+ let sx = sourceX;
2383
+ let sy = sourceY;
2384
+ let w = width;
2385
+ let h = height;
2386
+ if (sx < 0) {
2387
+ x -= sx;
2388
+ w += sx;
2389
+ sx = 0;
2390
+ }
2391
+ if (sy < 0) {
2392
+ y -= sy;
2393
+ h += sy;
2394
+ sy = 0;
2395
+ }
2396
+ w = Math.min(w, src.width - sx);
2397
+ h = Math.min(h, src.height - sy);
2441
2398
  if (x < 0) {
2399
+ sx -= x;
2442
2400
  w += x;
2443
2401
  x = 0;
2444
2402
  }
2445
2403
  if (y < 0) {
2404
+ sy -= y;
2446
2405
  h += y;
2447
2406
  y = 0;
2448
2407
  }
2449
2408
  const actualW = Math.min(w, dst.width - x);
2450
2409
  const actualH = Math.min(h, dst.height - y);
2451
2410
  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
2411
  const dx = x - targetX | 0;
2459
2412
  const dy = y - targetY | 0;
2460
2413
  const dst32 = dst.data32;
2414
+ const src32 = src.data32;
2461
2415
  const dw = dst.width;
2462
- const mPitch = mask.w;
2463
- const maskData = mask.data;
2416
+ const sw = src.width;
2417
+ const mPitch = binaryMask.w;
2418
+ const maskData = binaryMask.data;
2464
2419
  let dIdx = y * dw + x | 0;
2420
+ let sIdx = sy * sw + sx | 0;
2465
2421
  let mIdx = (my + dy) * mPitch + (mx + dx) | 0;
2466
2422
  const dStride = dw - actualW | 0;
2423
+ const sStride = sw - actualW | 0;
2467
2424
  const mStride = mPitch - actualW | 0;
2468
2425
  const skipVal = invertMask ? 1 : 0;
2426
+ const isOpaque = globalAlpha === 255;
2427
+ const isOverwrite = blendFn.isOverwrite || false;
2469
2428
  let didChange = false;
2470
2429
  for (let iy = 0; iy < actualH; iy++) {
2471
2430
  for (let ix = 0; ix < actualW; ix++) {
2472
2431
  if (maskData[mIdx] === skipVal) {
2473
2432
  dIdx++;
2433
+ sIdx++;
2434
+ mIdx++;
2435
+ continue;
2436
+ }
2437
+ const srcCol = src32[sIdx];
2438
+ const srcAlpha = srcCol >>> 24;
2439
+ if (srcAlpha === 0 && !isOverwrite) {
2440
+ dIdx++;
2441
+ sIdx++;
2474
2442
  mIdx++;
2475
2443
  continue;
2476
2444
  }
2445
+ let finalCol = srcCol;
2446
+ if (!isOpaque) {
2447
+ const a = srcAlpha * globalAlpha + 128 >> 8;
2448
+ if (a === 0 && !isOverwrite) {
2449
+ dIdx++;
2450
+ sIdx++;
2451
+ mIdx++;
2452
+ continue;
2453
+ }
2454
+ finalCol = (srcCol & 16777215 | a << 24) >>> 0;
2455
+ }
2477
2456
  const current = dst32[dIdx];
2478
- const next = blendFn(baseColorWithGlobalAlpha, current);
2457
+ const next = blendFn(finalCol, dst32[dIdx]);
2479
2458
  if (current !== next) {
2480
2459
  dst32[dIdx] = next;
2481
2460
  didChange = true;
2482
2461
  }
2483
2462
  dIdx++;
2463
+ sIdx++;
2484
2464
  mIdx++;
2485
2465
  }
2486
2466
  dIdx += dStride;
2487
- mIdx += mStride;
2488
- }
2489
- return didChange;
2490
- }
2491
-
2492
- // src/PixelData/blendColorPixelDataCircleMask.ts
2493
- function blendColorPixelDataCircleMask(target, color, centerX, centerY, brush, alpha = 255, blendFn = sourceOverPerfect, scratchOptions = {}, bounds) {
2494
- const b = bounds ?? getCircleBrushOrPencilBounds(centerX, centerY, brush.size, target.width, target.height);
2495
- if (b.w <= 0 || b.h <= 0) return false;
2496
- const unclippedStartX = Math.floor(centerX + brush.minOffset);
2497
- const unclippedStartY = Math.floor(centerY + brush.minOffset);
2498
- const ix = Math.max(unclippedStartX, b.x);
2499
- const iy = Math.max(unclippedStartY, b.y);
2500
- const ir = Math.min(unclippedStartX + brush.w, b.x + b.w);
2501
- const ib = Math.min(unclippedStartY + brush.h, b.y + b.h);
2502
- const iw = ir - ix;
2503
- const ih = ib - iy;
2504
- if (iw <= 0 || ih <= 0) return false;
2505
- scratchOptions.x = ix;
2506
- scratchOptions.y = iy;
2507
- scratchOptions.w = iw;
2508
- scratchOptions.h = ih;
2509
- scratchOptions.mx = ix - unclippedStartX;
2510
- scratchOptions.my = iy - unclippedStartY;
2511
- scratchOptions.alpha = alpha;
2512
- scratchOptions.blendFn = blendFn;
2513
- if (brush.type === 0 /* ALPHA */) {
2514
- return blendColorPixelDataAlphaMask(target, color, brush, scratchOptions);
2515
- }
2516
- if (brush.type === 1 /* BINARY */) {
2517
- return blendColorPixelDataBinaryMask(target, color, brush, scratchOptions);
2518
- }
2519
- return false;
2520
- }
2521
-
2522
- // src/History/PixelMutator/mutatorBlendColorCircleMask.ts
2523
- var defaults5 = {
2524
- blendColorPixelDataCircleMask,
2525
- getCircleBrushOrPencilBounds
2526
- };
2527
- var mutatorBlendColorCircleMask = ((writer, deps = defaults5) => {
2528
- const {
2529
- blendColorPixelDataCircleMask: blendColorPixelDataCircleMask2 = defaults5.blendColorPixelDataCircleMask,
2530
- getCircleBrushOrPencilBounds: getCircleBrushOrPencilBounds2 = defaults5.getCircleBrushOrPencilBounds
2531
- } = 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
- return {
2547
- applyCircleMask(color, centerX, centerY, brush, alpha = 255, blendFn) {
2548
- const target = writer.config.target;
2549
- const b = getCircleBrushOrPencilBounds2(centerX, centerY, brush.size, target.width, target.height, boundsOut);
2550
- const didChange = writer.accumulator.storeRegionBeforeState(b.x, b.y, b.w, b.h);
2551
- return didChange(blendColorPixelDataCircleMask2(target, color, centerX, centerY, brush, alpha, blendFn, blendColorPixelOptions, b));
2552
- }
2553
- };
2554
- });
2555
-
2556
- // src/History/PixelMutator/mutatorApplyCirclePencil.ts
2557
- var defaults6 = {
2558
- applyCircleMaskToPixelData: blendColorPixelDataCircleMask,
2559
- getCircleBrushOrPencilBounds
2560
- };
2561
- var mutatorApplyCirclePencil = ((writer, deps = defaults6) => {
2562
- const {
2563
- applyCircleMaskToPixelData = defaults6.applyCircleMaskToPixelData,
2564
- getCircleBrushOrPencilBounds: getCircleBrushOrPencilBounds2 = defaults6.getCircleBrushOrPencilBounds
2565
- } = deps;
2566
- const boundsOut = {
2567
- x: 0,
2568
- y: 0,
2569
- w: 0,
2570
- h: 0
2571
- };
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
- });
2581
-
2582
- // src/History/PixelMutator/mutatorApplyCirclePencilStroke.ts
2583
- var defaults7 = {
2584
- forEachLinePoint,
2585
- blendColorPixelDataBinaryMask,
2586
- getCircleBrushOrPencilBounds,
2587
- getCircleBrushOrPencilStrokeBounds
2588
- };
2589
- var mutatorApplyCirclePencilStroke = ((writer, deps = defaults7) => {
2590
- const {
2591
- forEachLinePoint: forEachLinePoint2 = defaults7.forEachLinePoint,
2592
- blendColorPixelDataBinaryMask: blendColorPixelDataBinaryMask2 = defaults7.blendColorPixelDataBinaryMask,
2593
- getCircleBrushOrPencilStrokeBounds: getCircleBrushOrPencilStrokeBounds2 = defaults7.getCircleBrushOrPencilStrokeBounds,
2594
- getCircleBrushOrPencilBounds: getCircleBrushOrPencilBounds2 = defaults7.getCircleBrushOrPencilBounds
2595
- } = 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
- 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;
2635
- 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);
2673
- }
2674
- };
2675
- });
2676
-
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;
2709
- }
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;
2751
- }
2752
- }
2753
- }
2754
- return didChange;
2755
- }
2756
-
2757
- // src/History/PixelMutator/mutatorApplyRectBrush.ts
2758
- var defaults8 = {
2759
- applyRectBrushToPixelData,
2760
- getRectBrushOrPencilBounds
2761
- };
2762
- var mutatorApplyRectBrush = ((writer, deps = defaults8) => {
2763
- const {
2764
- applyRectBrushToPixelData: applyRectBrushToPixelData2 = defaults8.applyRectBrushToPixelData,
2765
- getRectBrushOrPencilBounds: getRectBrushOrPencilBounds2 = defaults8.getRectBrushOrPencilBounds
2766
- } = deps;
2767
- const boundsOut = {
2768
- x: 0,
2769
- y: 0,
2770
- w: 0,
2771
- h: 0
2772
- };
2773
- return {
2774
- applyRectBrush(color, centerX, centerY, brushWidth, brushHeight, alpha = 255, fallOff, blendFn) {
2775
- const target = writer.config.target;
2776
- const b = getRectBrushOrPencilBounds2(centerX, centerY, brushWidth, brushHeight, target.width, target.height, boundsOut);
2777
- const didChange = writer.accumulator.storeRegionBeforeState(b.x, b.y, b.w, b.h);
2778
- return didChange(applyRectBrushToPixelData2(target, color, centerX, centerY, brushWidth, brushHeight, alpha, fallOff, blendFn, b));
2779
- }
2780
- };
2781
- });
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) => {
2806
- const {
2807
- forEachLinePoint: forEachLinePoint2 = defaults9.forEachLinePoint,
2808
- blendColorPixelDataAlphaMask: blendColorPixelDataAlphaMask2 = defaults9.blendColorPixelDataAlphaMask,
2809
- getRectBrushOrPencilBounds: getRectBrushOrPencilBounds2 = defaults9.getRectBrushOrPencilBounds,
2810
- getRectBrushOrPencilStrokeBounds: getRectBrushOrPencilStrokeBounds2 = defaults9.getRectBrushOrPencilStrokeBounds
2811
- } = 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
- 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;
2857
- 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);
2898
- }
2899
- };
2900
- });
2901
-
2902
- // src/History/PixelMutator/mutatorApplyRectPencil.ts
2903
- var defaults10 = {
2904
- applyRectBrushToPixelData,
2905
- getRectBrushOrPencilBounds,
2906
- fallOff: () => 1
2907
- };
2908
- var mutatorApplyRectPencil = ((writer, deps = defaults10) => {
2909
- 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
2936
- };
2937
- var mutatorApplyRectPencilStroke = ((writer, deps = defaults11) => {
2938
- const {
2939
- forEachLinePoint: forEachLinePoint2 = defaults11.forEachLinePoint,
2940
- blendColorPixelDataBinaryMask: blendColorPixelDataBinaryMask2 = defaults11.blendColorPixelDataBinaryMask,
2941
- getRectBrushOrPencilBounds: getRectBrushOrPencilBounds2 = defaults11.getRectBrushOrPencilBounds,
2942
- getRectBrushOrPencilStrokeBounds: getRectBrushOrPencilStrokeBounds2 = defaults11.getRectBrushOrPencilStrokeBounds
2943
- } = 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
- 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);
3022
- }
3023
- };
3024
- });
3025
-
3026
- // src/PixelData/blendColorPixelData.ts
3027
- function blendColorPixelData(dst, color, opts = {}) {
3028
- const {
3029
- x: targetX = 0,
3030
- y: targetY = 0,
3031
- w: width = dst.width,
3032
- h: height = dst.height,
3033
- alpha: globalAlpha = 255,
3034
- blendFn = sourceOverPerfect
3035
- } = 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
- }
3061
- const dst32 = dst.data32;
3062
- 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;
3073
- }
3074
- dIdx++;
3075
- }
3076
- dIdx += dStride;
3077
- }
3078
- return didChange;
3079
- }
3080
-
3081
- // src/History/PixelMutator/mutatorBlendColor.ts
3082
- var defaults12 = {
3083
- blendColorPixelData
3084
- };
3085
- var mutatorBlendColor = ((writer, deps = defaults12) => {
3086
- const {
3087
- blendColorPixelData: blendColorPixelData2 = defaults12.blendColorPixelData
3088
- } = deps;
3089
- return {
3090
- blendColor(color, opts = {}) {
3091
- const target = writer.config.target;
3092
- const {
3093
- x = 0,
3094
- y = 0,
3095
- w = target.width,
3096
- h = target.height
3097
- } = opts;
3098
- const didChange = writer.accumulator.storeRegionBeforeState(x, y, w, h);
3099
- return didChange(blendColorPixelData2(target, color, opts));
3100
- }
3101
- };
3102
- });
3103
-
3104
- // src/PixelData/blendPixel.ts
3105
- function blendPixel(target, x, y, color, alpha = 255, blendFn = sourceOverPerfect) {
3106
- if (alpha === 0) return false;
3107
- let width = target.width;
3108
- let height = target.height;
3109
- if (x < 0 || x >= width || y < 0 || y >= height) return false;
3110
- let srcAlpha = color >>> 24;
3111
- let isOverwrite = blendFn.isOverwrite;
3112
- if (srcAlpha === 0 && !isOverwrite) return false;
3113
- let dst32 = target.data32;
3114
- let index = y * width + x;
3115
- let finalColor = color;
3116
- if (alpha !== 255) {
3117
- let finalAlpha = srcAlpha * alpha + 128 >> 8;
3118
- if (finalAlpha === 0 && !isOverwrite) return false;
3119
- finalColor = (color & 16777215 | finalAlpha << 24) >>> 0;
3120
- }
3121
- let current = dst32[index];
3122
- let next = blendFn(finalColor, current);
3123
- if (current !== next) {
3124
- dst32[index] = next;
3125
- return true;
3126
- }
3127
- return false;
3128
- }
3129
-
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
- });
3145
-
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
- }
3396
- };
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;
3442
- }
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;
3496
- }
3497
- dIdx++;
3498
- sIdx++;
3499
- mIdx++;
3500
- }
3501
- dIdx += dStride;
3502
- sIdx += sStride;
2467
+ sIdx += sStride;
3503
2468
  mIdx += mStride;
3504
2469
  }
3505
2470
  return didChange;
3506
2471
  }
3507
2472
 
3508
2473
  // src/History/PixelMutator/mutatorBlendPixelDataBinaryMask.ts
3509
- var defaults16 = {
2474
+ var defaults6 = {
3510
2475
  blendPixelDataBinaryMask
3511
2476
  };
3512
- var mutatorBlendPixelDataBinaryMask = ((writer, deps = defaults16) => {
2477
+ var mutatorBlendPixelDataBinaryMask = ((writer, deps = defaults6) => {
3513
2478
  const {
3514
- blendPixelDataBinaryMask: blendPixelDataBinaryMask2 = defaults16.blendPixelDataBinaryMask
2479
+ blendPixelDataBinaryMask: blendPixelDataBinaryMask2 = defaults6.blendPixelDataBinaryMask
3515
2480
  } = deps;
3516
2481
  return {
3517
2482
  blendPixelDataBinaryMask(src, mask, opts = {}) {
@@ -3570,12 +2535,12 @@ function fillPixelDataFast(dst, color, _x, _y, _w, _h) {
3570
2535
  }
3571
2536
 
3572
2537
  // src/History/PixelMutator/mutatorClear.ts
3573
- var defaults17 = {
2538
+ var defaults7 = {
3574
2539
  fillPixelData: fillPixelDataFast
3575
2540
  };
3576
- var mutatorClear = ((writer, deps = defaults17) => {
2541
+ var mutatorClear = ((writer, deps = defaults7) => {
3577
2542
  const {
3578
- fillPixelData: fillPixelData2 = defaults17.fillPixelData
2543
+ fillPixelData: fillPixelData2 = defaults7.fillPixelData
3579
2544
  } = deps;
3580
2545
  return {
3581
2546
  clear(rect = {}) {
@@ -3639,12 +2604,12 @@ function fillPixelData(dst, color, _x, _y, _w, _h) {
3639
2604
  }
3640
2605
 
3641
2606
  // src/History/PixelMutator/mutatorFill.ts
3642
- var defaults18 = {
2607
+ var defaults8 = {
3643
2608
  fillPixelData
3644
2609
  };
3645
- var mutatorFill = ((writer, deps = defaults18) => {
2610
+ var mutatorFill = ((writer, deps = defaults8) => {
3646
2611
  const {
3647
- fillPixelData: fillPixelData2 = defaults18.fillPixelData
2612
+ fillPixelData: fillPixelData2 = defaults8.fillPixelData
3648
2613
  } = deps;
3649
2614
  return {
3650
2615
  fill(color, x = 0, y = 0, w = writer.config.target.width, h = writer.config.target.height) {
@@ -3654,9 +2619,9 @@ var mutatorFill = ((writer, deps = defaults18) => {
3654
2619
  }
3655
2620
  };
3656
2621
  });
3657
- var mutatorFillRect = ((writer, deps = defaults18) => {
2622
+ var mutatorFillRect = ((writer, deps = defaults8) => {
3658
2623
  const {
3659
- fillPixelData: fillPixelData2 = defaults18.fillPixelData
2624
+ fillPixelData: fillPixelData2 = defaults8.fillPixelData
3660
2625
  } = deps;
3661
2626
  return {
3662
2627
  fillRect(color, rect) {
@@ -3714,12 +2679,12 @@ function fillPixelDataBinaryMask(dst, color, mask, alpha = 255, x = 0, y = 0) {
3714
2679
  }
3715
2680
 
3716
2681
  // src/History/PixelMutator/mutatorFillBinaryMask.ts
3717
- var defaults19 = {
2682
+ var defaults9 = {
3718
2683
  fillPixelDataBinaryMask
3719
2684
  };
3720
- var mutatorFillBinaryMask = ((writer, deps = defaults19) => {
2685
+ var mutatorFillBinaryMask = ((writer, deps = defaults9) => {
3721
2686
  const {
3722
- fillPixelDataBinaryMask: fillPixelDataBinaryMask2 = defaults19.fillPixelDataBinaryMask
2687
+ fillPixelDataBinaryMask: fillPixelDataBinaryMask2 = defaults9.fillPixelDataBinaryMask
3723
2688
  } = deps;
3724
2689
  return {
3725
2690
  fillBinaryMask(color, mask, alpha = 255, x = 0, y = 0) {
@@ -3784,58 +2749,285 @@ function invertPixelData(pixelData, opts = {}) {
3784
2749
  dIdx += dStride;
3785
2750
  }
3786
2751
  }
3787
- return true;
3788
- }
3789
-
3790
- // src/History/PixelMutator/mutatorInvert.ts
3791
- var defaults20 = {
3792
- invertPixelData
2752
+ return true;
2753
+ }
2754
+
2755
+ // src/History/PixelMutator/mutatorInvert.ts
2756
+ var defaults10 = {
2757
+ invertPixelData
2758
+ };
2759
+ var mutatorInvert = ((writer, deps = defaults10) => {
2760
+ const {
2761
+ invertPixelData: invertPixelData2 = defaults10.invertPixelData
2762
+ } = deps;
2763
+ return {
2764
+ invert(opts = {}) {
2765
+ const target = writer.config.target;
2766
+ const {
2767
+ x = 0,
2768
+ y = 0,
2769
+ w = target.width,
2770
+ h = target.height
2771
+ } = opts;
2772
+ const didChange = writer.accumulator.storeRegionBeforeState(x, y, w, h);
2773
+ return didChange(invertPixelData2(target, opts));
2774
+ }
2775
+ };
2776
+ });
2777
+
2778
+ // src/History/PixelMutator.ts
2779
+ function makeFullPixelMutator(writer) {
2780
+ return {
2781
+ // @sort
2782
+ ...mutatorBlendColor(writer),
2783
+ ...mutatorBlendPixel(writer),
2784
+ ...mutatorBlendPixelData(writer),
2785
+ ...mutatorBlendPixelDataAlphaMask(writer),
2786
+ ...mutatorBlendPixelDataBinaryMask(writer),
2787
+ ...mutatorClear(writer),
2788
+ ...mutatorFill(writer),
2789
+ ...mutatorFillBinaryMask(writer),
2790
+ ...mutatorFillRect(writer),
2791
+ ...mutatorInvert(writer)
2792
+ };
2793
+ }
2794
+
2795
+ // src/ImageData/resizeImageData.ts
2796
+ function resizeImageData(target, newWidth, newHeight, offsetX = 0, offsetY = 0) {
2797
+ const result = new ImageData(newWidth, newHeight);
2798
+ const {
2799
+ width: oldW,
2800
+ height: oldH,
2801
+ data: oldData
2802
+ } = target;
2803
+ const newData = result.data;
2804
+ const x0 = Math.max(0, offsetX);
2805
+ const y0 = Math.max(0, offsetY);
2806
+ const x1 = Math.min(newWidth, offsetX + oldW);
2807
+ const y1 = Math.min(newHeight, offsetY + oldH);
2808
+ if (x1 <= x0 || y1 <= y0) {
2809
+ return result;
2810
+ }
2811
+ const rowCount = y1 - y0;
2812
+ const rowLen = (x1 - x0) * 4;
2813
+ for (let row = 0; row < rowCount; row++) {
2814
+ const dstY = y0 + row;
2815
+ const srcY = dstY - offsetY;
2816
+ const srcX = x0 - offsetX;
2817
+ const dstStart = (dstY * newWidth + x0) * 4;
2818
+ const srcStart = (srcY * oldW + srcX) * 4;
2819
+ newData.set(oldData.subarray(srcStart, srcStart + rowLen), dstStart);
2820
+ }
2821
+ return result;
2822
+ }
2823
+
2824
+ // src/Rect/trimRectBounds.ts
2825
+ function trimRectBounds(x, y, w, h, targetWidth, targetHeight, out) {
2826
+ const res = out ?? {
2827
+ x: 0,
2828
+ y: 0,
2829
+ w: 0,
2830
+ h: 0
2831
+ };
2832
+ const left = Math.max(0, x);
2833
+ const top = Math.max(0, y);
2834
+ const right = Math.min(targetWidth, x + w);
2835
+ const bottom = Math.min(targetHeight, y + h);
2836
+ res.x = left;
2837
+ res.y = top;
2838
+ res.w = Math.max(0, right - left);
2839
+ res.h = Math.max(0, bottom - top);
2840
+ return res;
2841
+ }
2842
+
2843
+ // src/Paint/PaintBuffer.ts
2844
+ var PaintBuffer = class {
2845
+ constructor(config, tilePool) {
2846
+ this.config = config;
2847
+ this.tilePool = tilePool;
2848
+ this.lookup = [];
2849
+ }
2850
+ lookup;
2851
+ scratchBounds = {
2852
+ x: 0,
2853
+ y: 0,
2854
+ w: 0,
2855
+ h: 0
2856
+ };
2857
+ eachTileInBounds(bounds, callback) {
2858
+ const {
2859
+ tileShift,
2860
+ targetColumns,
2861
+ targetRows,
2862
+ tileSize
2863
+ } = this.config;
2864
+ const x1 = Math.max(0, bounds.x >> tileShift);
2865
+ const y1 = Math.max(0, bounds.y >> tileShift);
2866
+ const x2 = Math.min(targetColumns - 1, bounds.x + bounds.w - 1 >> tileShift);
2867
+ const y2 = Math.min(targetRows - 1, bounds.y + bounds.h - 1 >> tileShift);
2868
+ if (x1 > x2 || y1 > y2) return;
2869
+ const lookup = this.lookup;
2870
+ const tilePool = this.tilePool;
2871
+ for (let ty = y1; ty <= y2; ty++) {
2872
+ const rowOffset = ty * targetColumns;
2873
+ const tileTop = ty << tileShift;
2874
+ for (let tx = x1; tx <= x2; tx++) {
2875
+ const id = rowOffset + tx;
2876
+ const tile = lookup[id] ?? (lookup[id] = tilePool.getTile(id, tx, ty));
2877
+ const tileLeft = tx << tileShift;
2878
+ const startX = bounds.x > tileLeft ? bounds.x : tileLeft;
2879
+ const startY = bounds.y > tileTop ? bounds.y : tileTop;
2880
+ const maskEndX = bounds.x + bounds.w;
2881
+ const tileEndX = tileLeft + tileSize;
2882
+ const endX = maskEndX < tileEndX ? maskEndX : tileEndX;
2883
+ const maskEndY = bounds.y + bounds.h;
2884
+ const tileEndY = tileTop + tileSize;
2885
+ const endY = maskEndY < tileEndY ? maskEndY : tileEndY;
2886
+ callback(tile, startX, startY, endX - startX, endY - startY);
2887
+ }
2888
+ }
2889
+ }
2890
+ writePaintAlphaMaskStroke(color, brush, x0, y0, x1, y1) {
2891
+ const cA = color >>> 24;
2892
+ if (cA === 0) return false;
2893
+ const {
2894
+ tileShift,
2895
+ tileMask,
2896
+ target
2897
+ } = this.config;
2898
+ const {
2899
+ w: bW,
2900
+ h: bH,
2901
+ data: bD,
2902
+ centerOffsetX,
2903
+ centerOffsetY
2904
+ } = brush;
2905
+ const cRGB = color & 16777215;
2906
+ const scratch = this.scratchBounds;
2907
+ let changed = false;
2908
+ forEachLinePoint(x0, y0, x1, y1, (px, py) => {
2909
+ const topLeftX = Math.floor(px + centerOffsetX);
2910
+ const topLeftY = Math.floor(py + centerOffsetY);
2911
+ trimRectBounds(topLeftX, topLeftY, bW, bH, target.width, target.height, scratch);
2912
+ if (scratch.w <= 0 || scratch.h <= 0) return;
2913
+ this.eachTileInBounds(scratch, (tile, bX, bY, bW_t, bH_t) => {
2914
+ const d32 = tile.data32;
2915
+ let tileChanged = false;
2916
+ for (let i = 0; i < bH_t; i++) {
2917
+ const canvasY = bY + i;
2918
+ const bOff = (canvasY - topLeftY) * bW;
2919
+ const tOff = (canvasY & tileMask) << tileShift;
2920
+ const dS = tOff + (bX & tileMask);
2921
+ for (let j = 0; j < bW_t; j++) {
2922
+ const canvasX = bX + j;
2923
+ const brushA = bD[bOff + (canvasX - topLeftX)];
2924
+ if (brushA === 0) continue;
2925
+ const t = cA * brushA + 128;
2926
+ const blendedA = t + (t >> 8) >> 8;
2927
+ const idx = dS + j;
2928
+ const cur = d32[idx];
2929
+ if (brushA > cur >>> 24) {
2930
+ const next = (cRGB | blendedA << 24) >>> 0;
2931
+ if (cur !== next) {
2932
+ d32[idx] = next;
2933
+ tileChanged = true;
2934
+ }
2935
+ }
2936
+ }
2937
+ }
2938
+ if (tileChanged) changed = true;
2939
+ });
2940
+ });
2941
+ return changed;
2942
+ }
2943
+ writePaintBinaryMaskStroke(color, brush, x0, y0, x1, y1) {
2944
+ const alphaIsZero = color >>> 24 === 0;
2945
+ if (alphaIsZero) return false;
2946
+ const {
2947
+ tileShift,
2948
+ tileMask,
2949
+ target
2950
+ } = this.config;
2951
+ const {
2952
+ w: bW,
2953
+ h: bH,
2954
+ data: bD,
2955
+ centerOffsetX,
2956
+ centerOffsetY
2957
+ } = brush;
2958
+ const scratch = this.scratchBounds;
2959
+ let changed = false;
2960
+ forEachLinePoint(x0, y0, x1, y1, (px, py) => {
2961
+ const topLeftX = Math.floor(px + centerOffsetX);
2962
+ const topLeftY = Math.floor(py + centerOffsetY);
2963
+ trimRectBounds(topLeftX, topLeftY, bW, bH, target.width, target.height, scratch);
2964
+ if (scratch.w <= 0 || scratch.h <= 0) return;
2965
+ this.eachTileInBounds(scratch, (tile, bX, bY, bW_t, bH_t) => {
2966
+ const d32 = tile.data32;
2967
+ let tileChanged = false;
2968
+ for (let i = 0; i < bH_t; i++) {
2969
+ const canvasY = bY + i;
2970
+ const bOff = (canvasY - topLeftY) * bW;
2971
+ const tOff = (canvasY & tileMask) << tileShift;
2972
+ const dS = tOff + (bX & tileMask);
2973
+ for (let j = 0; j < bW_t; j++) {
2974
+ const canvasX = bX + j;
2975
+ if (bD[bOff + (canvasX - topLeftX)]) {
2976
+ const idx = dS + j;
2977
+ if (d32[idx] !== color) {
2978
+ d32[idx] = color;
2979
+ tileChanged = true;
2980
+ }
2981
+ }
2982
+ }
2983
+ }
2984
+ if (tileChanged) changed = true;
2985
+ });
2986
+ });
2987
+ return changed;
2988
+ }
2989
+ writeRectStroke(color, brushWidth, brushHeight, x0, y0, x1, y1) {
2990
+ const alphaIsZero = color >>> 24 === 0;
2991
+ if (alphaIsZero) return false;
2992
+ const config = this.config;
2993
+ const tileShift = config.tileShift;
2994
+ const tileMask = config.tileMask;
2995
+ const target = config.target;
2996
+ const scratch = this.scratchBounds;
2997
+ const centerOffsetX = brushWidth - 1 >> 1;
2998
+ const centerOffsetY = brushHeight - 1 >> 1;
2999
+ let changed = false;
3000
+ forEachLinePoint(x0, y0, x1, y1, (px, py) => {
3001
+ const topLeftX = Math.floor(px + centerOffsetX);
3002
+ const topLeftY = Math.floor(py + centerOffsetY);
3003
+ trimRectBounds(topLeftX, topLeftY, brushWidth, brushHeight, target.width, target.height, scratch);
3004
+ if (scratch.w <= 0 || scratch.h <= 0) return;
3005
+ this.eachTileInBounds(scratch, (tile, bX, bY, bW_t, bH_t) => {
3006
+ const d32 = tile.data32;
3007
+ let tileChanged = false;
3008
+ for (let i = 0; i < bH_t; i++) {
3009
+ const canvasY = bY + i;
3010
+ const tOff = (canvasY & tileMask) << tileShift;
3011
+ const dS = tOff + (bX & tileMask);
3012
+ for (let j = 0; j < bW_t; j++) {
3013
+ const idx = dS + j;
3014
+ if (d32[idx] !== color) {
3015
+ d32[idx] = color;
3016
+ tileChanged = true;
3017
+ }
3018
+ }
3019
+ }
3020
+ if (tileChanged) {
3021
+ changed = true;
3022
+ }
3023
+ });
3024
+ });
3025
+ return changed;
3026
+ }
3027
+ clear() {
3028
+ this.tilePool.releaseTiles(this.lookup);
3029
+ }
3793
3030
  };
3794
- var mutatorInvert = ((writer, deps = defaults20) => {
3795
- const {
3796
- invertPixelData: invertPixelData2 = defaults20.invertPixelData
3797
- } = deps;
3798
- return {
3799
- invert(opts = {}) {
3800
- const target = writer.config.target;
3801
- const {
3802
- x = 0,
3803
- y = 0,
3804
- w = target.width,
3805
- h = target.height
3806
- } = opts;
3807
- const didChange = writer.accumulator.storeRegionBeforeState(x, y, w, h);
3808
- return didChange(invertPixelData2(target, opts));
3809
- }
3810
- };
3811
- });
3812
-
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
3031
 
3840
3032
  // src/PixelTile/PixelTile.ts
3841
3033
  var PixelTile = class {
@@ -3896,34 +3088,334 @@ var PixelWriter = class {
3896
3088
  accumulator;
3897
3089
  historyActionFactory;
3898
3090
  config;
3091
+ pixelTilePool;
3092
+ paintBuffer;
3899
3093
  mutator;
3094
+ blendPixelDataOpts = {
3095
+ alpha: 255,
3096
+ blendFn: sourceOverPerfect,
3097
+ x: 0,
3098
+ y: 0,
3099
+ w: 0,
3100
+ h: 0
3101
+ };
3102
+ _inProgress = false;
3900
3103
  constructor(target, mutatorFactory, {
3901
3104
  tileSize = 256,
3902
3105
  maxHistorySteps = 50,
3903
3106
  historyManager = new HistoryManager(maxHistorySteps),
3904
3107
  historyActionFactory = makeHistoryAction,
3905
- pixelTilePool
3108
+ pixelTilePool,
3109
+ accumulator
3906
3110
  } = {}) {
3907
3111
  this.config = new PixelEngineConfig(tileSize, target);
3908
3112
  this.historyManager = historyManager;
3909
- pixelTilePool ??= new PixelTilePool(this.config);
3910
- this.accumulator = new PixelAccumulator(this.config, pixelTilePool);
3113
+ this.pixelTilePool = pixelTilePool ?? new PixelTilePool(this.config);
3114
+ this.accumulator = accumulator ?? new PixelAccumulator(this.config, this.pixelTilePool);
3911
3115
  this.historyActionFactory = historyActionFactory;
3912
3116
  this.mutator = mutatorFactory(this);
3117
+ this.paintBuffer = new PaintBuffer(this.config, this.pixelTilePool);
3913
3118
  }
3914
- withHistory(cb, after, afterUndo, afterRedo) {
3119
+ /**
3120
+ * Executes `transaction` and commits the resulting pixel changes as a single
3121
+ * undoable history action.
3122
+ *
3123
+ * - If `transaction` throws, all accumulated changes are rolled back and the error
3124
+ * is re-thrown. No action is committed.
3125
+ * - If `transaction` completes without modifying any pixels, no action is committed.
3126
+ * - `withHistory` is not re-entrant. Calling it again from inside `transaction` will
3127
+ * throw immediately to prevent silent data loss from a nested extractPatch.
3128
+ *
3129
+ * @param transaction Callback to be executed inside the transaction.
3130
+ * @param after Called after both undo and redo — use for generic change notifications.
3131
+ * @param afterUndo Called after undo only — use for dimension or state changes specific to undo.
3132
+ * @param afterRedo Called after redo only.
3133
+ */
3134
+ withHistory(transaction, after, afterUndo, afterRedo) {
3135
+ if (this._inProgress) {
3136
+ throw new Error("withHistory is not re-entrant \u2014 commit or rollback the current operation first");
3137
+ }
3138
+ this._inProgress = true;
3915
3139
  try {
3916
- cb(this.mutator);
3140
+ transaction(this.mutator);
3917
3141
  } catch (e) {
3918
- this.accumulator.rollback();
3142
+ this.accumulator.rollbackAfterError();
3919
3143
  throw e;
3144
+ } finally {
3145
+ this._inProgress = false;
3146
+ }
3147
+ if (this.accumulator.beforeTiles.length === 0) return;
3148
+ const patch = this.accumulator.extractPatch();
3149
+ const action = this.historyActionFactory(this, patch, after, afterUndo, afterRedo);
3150
+ this.historyManager.commit(action);
3151
+ }
3152
+ resize(newWidth, newHeight, offsetX = 0, offsetY = 0, after, afterUndo, afterRedo, resizeImageDataFn = resizeImageData) {
3153
+ if (this._inProgress) {
3154
+ throw new Error("Cannot resize inside a withHistory callback");
3155
+ }
3156
+ if (this.accumulator.beforeTiles.length > 0) {
3157
+ throw new Error("Cannot resize with an open accumulator \u2014 commit or rollback first");
3158
+ }
3159
+ const config = this.config;
3160
+ const target = config.target;
3161
+ const beforeImageData = target.imageData;
3162
+ const afterImageData = resizeImageDataFn(beforeImageData, newWidth, newHeight, offsetX, offsetY);
3163
+ target.set(afterImageData);
3164
+ this.historyManager.commit({
3165
+ undo: () => {
3166
+ target.set(beforeImageData);
3167
+ afterUndo?.(beforeImageData);
3168
+ after?.(beforeImageData);
3169
+ },
3170
+ redo: () => {
3171
+ target.set(afterImageData);
3172
+ afterRedo?.(afterImageData);
3173
+ after?.(afterImageData);
3174
+ }
3175
+ });
3176
+ }
3177
+ commitPaintBuffer(alpha = 255, blendFn = sourceOverPerfect, blendPixelDataFn = blendPixelData) {
3178
+ const paintBuffer = this.paintBuffer;
3179
+ const tileShift = paintBuffer.config.tileShift;
3180
+ const lookup = paintBuffer.lookup;
3181
+ const opts = this.blendPixelDataOpts;
3182
+ opts.alpha = alpha;
3183
+ opts.blendFn = blendFn;
3184
+ for (let i = 0; i < lookup.length; i++) {
3185
+ const tile = lookup[i];
3186
+ if (tile) {
3187
+ const didChange = this.accumulator.storeTileBeforeState(tile.id, tile.tx, tile.ty);
3188
+ const dx = tile.tx << tileShift;
3189
+ const dy = tile.ty << tileShift;
3190
+ opts.x = dx;
3191
+ opts.y = dy;
3192
+ opts.w = tile.width;
3193
+ opts.h = tile.height;
3194
+ didChange(blendPixelDataFn(this.config.target, tile, opts));
3195
+ }
3196
+ }
3197
+ paintBuffer.clear();
3198
+ }
3199
+ };
3200
+
3201
+ // src/PixelData/applyAlphaMaskToPixelData.ts
3202
+ function applyAlphaMaskToPixelData(dst, mask, opts = {}) {
3203
+ const {
3204
+ x: targetX = 0,
3205
+ y: targetY = 0,
3206
+ w: width = dst.width,
3207
+ h: height = dst.height,
3208
+ alpha: globalAlpha = 255,
3209
+ mx = 0,
3210
+ my = 0,
3211
+ invertMask = false
3212
+ } = opts;
3213
+ if (globalAlpha === 0) return false;
3214
+ let x = targetX;
3215
+ let y = targetY;
3216
+ let w = width;
3217
+ let h = height;
3218
+ if (x < 0) {
3219
+ w += x;
3220
+ x = 0;
3221
+ }
3222
+ if (y < 0) {
3223
+ h += y;
3224
+ y = 0;
3225
+ }
3226
+ w = Math.min(w, dst.width - x);
3227
+ h = Math.min(h, dst.height - y);
3228
+ if (w <= 0) return false;
3229
+ if (h <= 0) return false;
3230
+ const mPitch = mask.w;
3231
+ if (mPitch <= 0) return false;
3232
+ const startX = mx + (x - targetX);
3233
+ const startY = my + (y - targetY);
3234
+ const sX0 = Math.max(0, startX);
3235
+ const sY0 = Math.max(0, startY);
3236
+ const sX1 = Math.min(mPitch, startX + w);
3237
+ const sY1 = Math.min(mask.h, startY + h);
3238
+ const finalW = sX1 - sX0;
3239
+ const finalH = sY1 - sY0;
3240
+ if (finalW <= 0) return false;
3241
+ if (finalH <= 0) return false;
3242
+ const xShift = sX0 - startX;
3243
+ const yShift = sY0 - startY;
3244
+ const dst32 = dst.data32;
3245
+ const dw = dst.width;
3246
+ const dStride = dw - finalW;
3247
+ const mStride = mPitch - finalW;
3248
+ const maskData = mask.data;
3249
+ let dIdx = (y + yShift) * dw + (x + xShift);
3250
+ let mIdx = sY0 * mPitch + sX0;
3251
+ let didChange = false;
3252
+ for (let iy = 0; iy < h; iy++) {
3253
+ for (let ix = 0; ix < w; ix++) {
3254
+ const mVal = maskData[mIdx];
3255
+ const effectiveM = invertMask ? 255 - mVal : mVal;
3256
+ let weight = 0;
3257
+ if (effectiveM === 0) {
3258
+ weight = 0;
3259
+ } else if (effectiveM === 255) {
3260
+ weight = globalAlpha;
3261
+ } else if (globalAlpha === 255) {
3262
+ weight = effectiveM;
3263
+ } else {
3264
+ weight = effectiveM * globalAlpha + 128 >> 8;
3265
+ }
3266
+ if (weight === 0) {
3267
+ dst32[dIdx] = (dst32[dIdx] & 16777215) >>> 0;
3268
+ didChange = true;
3269
+ } else if (weight !== 255) {
3270
+ const d = dst32[dIdx];
3271
+ const da = d >>> 24;
3272
+ if (da !== 0) {
3273
+ const finalAlpha = da === 255 ? weight : da * weight + 128 >> 8;
3274
+ const current = dst32[dIdx];
3275
+ const next = (d & 16777215 | finalAlpha << 24) >>> 0;
3276
+ if (current !== next) {
3277
+ dst32[dIdx] = next;
3278
+ didChange = true;
3279
+ }
3280
+ }
3281
+ }
3282
+ dIdx++;
3283
+ mIdx++;
3284
+ }
3285
+ dIdx += dStride;
3286
+ mIdx += mStride;
3287
+ }
3288
+ return didChange;
3289
+ }
3290
+
3291
+ // src/History/PixelMutator/mutatorApplyAlphaMask.ts
3292
+ var defaults11 = {
3293
+ applyAlphaMaskToPixelData
3294
+ };
3295
+ var mutatorApplyAlphaMask = ((writer, deps = defaults11) => {
3296
+ const {
3297
+ applyAlphaMaskToPixelData: applyAlphaMaskToPixelData2 = defaults11.applyAlphaMaskToPixelData
3298
+ } = deps;
3299
+ return {
3300
+ applyAlphaMask(mask, opts = {}) {
3301
+ let target = writer.config.target;
3302
+ const {
3303
+ x = 0,
3304
+ y = 0,
3305
+ w = target.width,
3306
+ h = target.height
3307
+ } = opts;
3308
+ const didChange = writer.accumulator.storeRegionBeforeState(x, y, w, h);
3309
+ return didChange(applyAlphaMaskToPixelData2(target, mask, opts));
3310
+ }
3311
+ };
3312
+ });
3313
+
3314
+ // src/PixelData/applyBinaryMaskToPixelData.ts
3315
+ function applyBinaryMaskToPixelData(dst, mask, opts = {}) {
3316
+ const {
3317
+ x: targetX = 0,
3318
+ y: targetY = 0,
3319
+ w: width = dst.width,
3320
+ h: height = dst.height,
3321
+ alpha: globalAlpha = 255,
3322
+ mx = 0,
3323
+ my = 0,
3324
+ invertMask = false
3325
+ } = opts;
3326
+ if (globalAlpha === 0) return false;
3327
+ let x = targetX;
3328
+ let y = targetY;
3329
+ let w = width;
3330
+ let h = height;
3331
+ if (x < 0) {
3332
+ w += x;
3333
+ x = 0;
3334
+ }
3335
+ if (y < 0) {
3336
+ h += y;
3337
+ y = 0;
3338
+ }
3339
+ w = Math.min(w, dst.width - x);
3340
+ h = Math.min(h, dst.height - y);
3341
+ if (w <= 0 || h <= 0) return false;
3342
+ const mPitch = mask.w;
3343
+ if (mPitch <= 0) return false;
3344
+ const startX = mx + (x - targetX);
3345
+ const startY = my + (y - targetY);
3346
+ const sX0 = Math.max(0, startX);
3347
+ const sY0 = Math.max(0, startY);
3348
+ const sX1 = Math.min(mPitch, startX + w);
3349
+ const sY1 = Math.min(mask.h, startY + h);
3350
+ const finalW = sX1 - sX0;
3351
+ const finalH = sY1 - sY0;
3352
+ if (finalW <= 0 || finalH <= 0) {
3353
+ return false;
3354
+ }
3355
+ const xShift = sX0 - startX;
3356
+ const yShift = sY0 - startY;
3357
+ const dst32 = dst.data32;
3358
+ const dw = dst.width;
3359
+ const dStride = dw - finalW;
3360
+ const mStride = mPitch - finalW;
3361
+ const maskData = mask.data;
3362
+ let dIdx = (y + yShift) * dw + (x + xShift);
3363
+ let mIdx = sY0 * mPitch + sX0;
3364
+ let didChange = false;
3365
+ for (let iy = 0; iy < finalH; iy++) {
3366
+ for (let ix = 0; ix < finalW; ix++) {
3367
+ const mVal = maskData[mIdx];
3368
+ const isMaskedOut = invertMask ? mVal !== 0 : mVal === 0;
3369
+ if (isMaskedOut) {
3370
+ const current = dst32[dIdx];
3371
+ const next = (current & 16777215) >>> 0;
3372
+ if (current !== next) {
3373
+ dst32[dIdx] = next;
3374
+ didChange = true;
3375
+ }
3376
+ } else if (globalAlpha !== 255) {
3377
+ const d = dst32[dIdx];
3378
+ const da = d >>> 24;
3379
+ if (da !== 0) {
3380
+ const finalAlpha = da === 255 ? globalAlpha : da * globalAlpha + 128 >> 8;
3381
+ const next = (d & 16777215 | finalAlpha << 24) >>> 0;
3382
+ if (d !== next) {
3383
+ dst32[dIdx] = next;
3384
+ didChange = true;
3385
+ }
3386
+ }
3387
+ }
3388
+ dIdx++;
3389
+ mIdx++;
3920
3390
  }
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);
3391
+ dIdx += dStride;
3392
+ mIdx += mStride;
3925
3393
  }
3394
+ return didChange;
3395
+ }
3396
+
3397
+ // src/History/PixelMutator/mutatorApplyBinaryMask.ts
3398
+ var defaults12 = {
3399
+ applyBinaryMaskToPixelData
3926
3400
  };
3401
+ var mutatorApplyBinaryMask = ((writer, deps = defaults12) => {
3402
+ const {
3403
+ applyBinaryMaskToPixelData: applyBinaryMaskToPixelData2 = defaults12.applyBinaryMaskToPixelData
3404
+ } = deps;
3405
+ return {
3406
+ applyBinaryMask(mask, opts = {}) {
3407
+ let target = writer.config.target;
3408
+ const {
3409
+ x = 0,
3410
+ y = 0,
3411
+ w = target.width,
3412
+ h = target.height
3413
+ } = opts;
3414
+ const didChange = writer.accumulator.storeRegionBeforeState(x, y, w, h);
3415
+ return didChange(applyBinaryMaskToPixelData2(target, mask, opts));
3416
+ }
3417
+ };
3418
+ });
3927
3419
 
3928
3420
  // src/ImageData/copyImageData.ts
3929
3421
  function copyImageData({
@@ -4046,35 +3538,6 @@ function resampleImageData(source, factor) {
4046
3538
  return new ImageData(uint8ClampedArray, width, height);
4047
3539
  }
4048
3540
 
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
3541
  // src/ImageData/ReusableImageData.ts
4079
3542
  function makeReusableImageData() {
4080
3543
  let imageData = null;
@@ -4489,62 +3952,6 @@ function makeBinaryMask(w, h, data) {
4489
3952
  };
4490
3953
  }
4491
3954
 
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
3955
  // src/Mask/applyBinaryMaskToAlphaMask.ts
4549
3956
  function applyBinaryMaskToAlphaMask(alphaMaskDst, binaryMaskSrc, opts = {}) {
4550
3957
  const {
@@ -4920,85 +4327,241 @@ function mergeBinaryMaskRects(current, adding) {
4920
4327
  break;
4921
4328
  }
4922
4329
  }
4923
- if (!merged) next.push(r);
4330
+ if (!merged) next.push(r);
4331
+ }
4332
+ rects.splice(0, rects.length, ...next);
4333
+ }
4334
+ return rects;
4335
+ }
4336
+
4337
+ // src/MaskRect/subtractBinaryMaskRects.ts
4338
+ function subtractBinaryMaskRects(current, subtracting) {
4339
+ let result = [...current];
4340
+ for (const sub of subtracting) {
4341
+ const next = [];
4342
+ for (const r of result) {
4343
+ const ix = Math.max(r.x, sub.x);
4344
+ const iy = Math.max(r.y, sub.y);
4345
+ const ix2 = Math.min(r.x + r.w, sub.x + sub.w);
4346
+ const iy2 = Math.min(r.y + r.h, sub.y + sub.h);
4347
+ if (ix >= ix2 || iy >= iy2) {
4348
+ next.push(r);
4349
+ continue;
4350
+ }
4351
+ if (r.y < iy) pushPiece(next, r, r.x, r.y, r.w, iy - r.y);
4352
+ if (iy2 < r.y + r.h) pushPiece(next, r, r.x, iy2, r.w, r.y + r.h - iy2);
4353
+ if (r.x < ix) pushPiece(next, r, r.x, iy, ix - r.x, iy2 - iy);
4354
+ if (ix2 < r.x + r.w) pushPiece(next, r, ix2, iy, r.x + r.w - ix2, iy2 - iy);
4355
+ }
4356
+ result = next;
4357
+ }
4358
+ return result;
4359
+ }
4360
+ function pushPiece(dest, r, x, y, w, h) {
4361
+ if (r.data === null || r.data === void 0) {
4362
+ dest.push({
4363
+ x,
4364
+ y,
4365
+ w,
4366
+ h,
4367
+ data: null,
4368
+ type: null
4369
+ });
4370
+ return;
4371
+ }
4372
+ const lx = x - r.x;
4373
+ const ly = y - r.y;
4374
+ const data = new Uint8Array(w * h);
4375
+ for (let row = 0; row < h; row++) {
4376
+ data.set(r.data.subarray((ly + row) * r.w + lx, (ly + row) * r.w + lx + w), row * w);
4377
+ }
4378
+ dest.push({
4379
+ x,
4380
+ y,
4381
+ w,
4382
+ h,
4383
+ data,
4384
+ type: 1 /* BINARY */
4385
+ });
4386
+ }
4387
+
4388
+ // src/PixelData/PixelData.ts
4389
+ var PixelData = class {
4390
+ data32;
4391
+ imageData;
4392
+ width;
4393
+ height;
4394
+ constructor(imageData) {
4395
+ this.data32 = imageDataToUInt32Array(imageData);
4396
+ this.imageData = imageData;
4397
+ this.width = imageData.width;
4398
+ this.height = imageData.height;
4399
+ }
4400
+ set(imageData) {
4401
+ ;
4402
+ this.imageData = imageData;
4403
+ this.data32 = imageDataToUInt32Array(imageData);
4404
+ this.width = imageData.width;
4405
+ this.height = imageData.height;
4406
+ }
4407
+ };
4408
+
4409
+ // src/PixelData/blendColorPixelDataAlphaMask.ts
4410
+ function blendColorPixelDataAlphaMask(dst, color, mask, opts = {}) {
4411
+ const targetX = opts.x ?? 0;
4412
+ const targetY = opts.y ?? 0;
4413
+ const w = opts.w ?? mask.w;
4414
+ const h = opts.h ?? mask.h;
4415
+ const globalAlpha = opts.alpha ?? 255;
4416
+ const blendFn = opts.blendFn ?? sourceOverPerfect;
4417
+ const mx = opts.mx ?? 0;
4418
+ const my = opts.my ?? 0;
4419
+ const invertMask = opts.invertMask ?? false;
4420
+ if (globalAlpha === 0) return false;
4421
+ const baseSrcAlpha = color >>> 24;
4422
+ const isOverwrite = blendFn.isOverwrite || false;
4423
+ if (baseSrcAlpha === 0 && !isOverwrite) return false;
4424
+ let x = targetX;
4425
+ let y = targetY;
4426
+ let actualW = w;
4427
+ let actualH = h;
4428
+ if (x < 0) {
4429
+ actualW += x;
4430
+ x = 0;
4431
+ }
4432
+ if (y < 0) {
4433
+ actualH += y;
4434
+ y = 0;
4435
+ }
4436
+ actualW = Math.min(actualW, dst.width - x);
4437
+ actualH = Math.min(actualH, dst.height - y);
4438
+ if (actualW <= 0 || actualH <= 0) return false;
4439
+ const dx = x - targetX | 0;
4440
+ const dy = y - targetY | 0;
4441
+ const dst32 = dst.data32;
4442
+ const dw = dst.width;
4443
+ const mPitch = mask.w;
4444
+ const maskData = mask.data;
4445
+ let dIdx = y * dw + x | 0;
4446
+ let mIdx = (my + dy) * mPitch + (mx + dx) | 0;
4447
+ const dStride = dw - actualW | 0;
4448
+ const mStride = mPitch - actualW | 0;
4449
+ const isOpaque = globalAlpha === 255;
4450
+ const colorRGB = color & 16777215;
4451
+ let didChange = false;
4452
+ for (let iy = 0; iy < actualH; iy++) {
4453
+ for (let ix = 0; ix < actualW; ix++) {
4454
+ const mVal = maskData[mIdx];
4455
+ const effM = invertMask ? 255 - mVal : mVal;
4456
+ if (effM === 0) {
4457
+ dIdx++;
4458
+ mIdx++;
4459
+ continue;
4460
+ }
4461
+ let weight = globalAlpha;
4462
+ if (isOpaque) {
4463
+ weight = effM;
4464
+ } else if (effM !== 255) {
4465
+ weight = effM * globalAlpha + 128 >> 8;
4466
+ }
4467
+ if (weight === 0) {
4468
+ dIdx++;
4469
+ mIdx++;
4470
+ continue;
4471
+ }
4472
+ let finalCol = color;
4473
+ if (weight < 255) {
4474
+ const a = baseSrcAlpha * weight + 128 >> 8;
4475
+ if (a === 0 && !isOverwrite) {
4476
+ dIdx++;
4477
+ mIdx++;
4478
+ continue;
4479
+ }
4480
+ finalCol = (colorRGB | a << 24) >>> 0;
4481
+ }
4482
+ const current = dst32[dIdx];
4483
+ const next = blendFn(finalCol, current);
4484
+ if (current !== next) {
4485
+ dst32[dIdx] = next;
4486
+ didChange = true;
4487
+ }
4488
+ dIdx++;
4489
+ mIdx++;
4924
4490
  }
4925
- rects.splice(0, rects.length, ...next);
4491
+ dIdx += dStride;
4492
+ mIdx += mStride;
4926
4493
  }
4927
- return rects;
4494
+ return didChange;
4928
4495
  }
4929
4496
 
4930
- // src/MaskRect/subtractBinaryMaskRects.ts
4931
- function subtractBinaryMaskRects(current, subtracting) {
4932
- let result = [...current];
4933
- for (const sub of subtracting) {
4934
- const next = [];
4935
- for (const r of result) {
4936
- const ix = Math.max(r.x, sub.x);
4937
- const iy = Math.max(r.y, sub.y);
4938
- const ix2 = Math.min(r.x + r.w, sub.x + sub.w);
4939
- const iy2 = Math.min(r.y + r.h, sub.y + sub.h);
4940
- if (ix >= ix2 || iy >= iy2) {
4941
- next.push(r);
4497
+ // src/PixelData/blendColorPixelDataBinaryMask.ts
4498
+ function blendColorPixelDataBinaryMask(dst, color, mask, opts = {}) {
4499
+ const targetX = opts.x ?? 0;
4500
+ const targetY = opts.y ?? 0;
4501
+ let w = opts.w ?? mask.w;
4502
+ let h = opts.h ?? mask.h;
4503
+ const globalAlpha = opts.alpha ?? 255;
4504
+ const blendFn = opts.blendFn ?? sourceOverPerfect;
4505
+ const mx = opts.mx ?? 0;
4506
+ const my = opts.my ?? 0;
4507
+ const invertMask = opts.invertMask ?? false;
4508
+ if (globalAlpha === 0) return false;
4509
+ const baseSrcAlpha = color >>> 24;
4510
+ const isOverwrite = blendFn.isOverwrite || false;
4511
+ if (baseSrcAlpha === 0 && !isOverwrite) return false;
4512
+ let x = targetX;
4513
+ let y = targetY;
4514
+ if (x < 0) {
4515
+ w += x;
4516
+ x = 0;
4517
+ }
4518
+ if (y < 0) {
4519
+ h += y;
4520
+ y = 0;
4521
+ }
4522
+ const actualW = Math.min(w, dst.width - x);
4523
+ const actualH = Math.min(h, dst.height - y);
4524
+ if (actualW <= 0 || actualH <= 0) return false;
4525
+ let baseColorWithGlobalAlpha = color;
4526
+ if (globalAlpha < 255) {
4527
+ const a = baseSrcAlpha * globalAlpha + 128 >> 8;
4528
+ if (a === 0 && !isOverwrite) return false;
4529
+ baseColorWithGlobalAlpha = (color & 16777215 | a << 24) >>> 0;
4530
+ }
4531
+ const dx = x - targetX | 0;
4532
+ const dy = y - targetY | 0;
4533
+ const dst32 = dst.data32;
4534
+ const dw = dst.width;
4535
+ const mPitch = mask.w;
4536
+ const maskData = mask.data;
4537
+ let dIdx = y * dw + x | 0;
4538
+ let mIdx = (my + dy) * mPitch + (mx + dx) | 0;
4539
+ const dStride = dw - actualW | 0;
4540
+ const mStride = mPitch - actualW | 0;
4541
+ const skipVal = invertMask ? 1 : 0;
4542
+ let didChange = false;
4543
+ for (let iy = 0; iy < actualH; iy++) {
4544
+ for (let ix = 0; ix < actualW; ix++) {
4545
+ if (maskData[mIdx] === skipVal) {
4546
+ dIdx++;
4547
+ mIdx++;
4942
4548
  continue;
4943
4549
  }
4944
- if (r.y < iy) pushPiece(next, r, r.x, r.y, r.w, iy - r.y);
4945
- if (iy2 < r.y + r.h) pushPiece(next, r, r.x, iy2, r.w, r.y + r.h - iy2);
4946
- if (r.x < ix) pushPiece(next, r, r.x, iy, ix - r.x, iy2 - iy);
4947
- if (ix2 < r.x + r.w) pushPiece(next, r, ix2, iy, r.x + r.w - ix2, iy2 - iy);
4550
+ const current = dst32[dIdx];
4551
+ const next = blendFn(baseColorWithGlobalAlpha, current);
4552
+ if (current !== next) {
4553
+ dst32[dIdx] = next;
4554
+ didChange = true;
4555
+ }
4556
+ dIdx++;
4557
+ mIdx++;
4948
4558
  }
4949
- result = next;
4950
- }
4951
- return result;
4952
- }
4953
- function pushPiece(dest, r, x, y, w, h) {
4954
- if (r.data === null || r.data === void 0) {
4955
- dest.push({
4956
- x,
4957
- y,
4958
- w,
4959
- h,
4960
- data: null,
4961
- type: null
4962
- });
4963
- return;
4964
- }
4965
- const lx = x - r.x;
4966
- const ly = y - r.y;
4967
- const data = new Uint8Array(w * h);
4968
- for (let row = 0; row < h; row++) {
4969
- data.set(r.data.subarray((ly + row) * r.w + lx, (ly + row) * r.w + lx + w), row * w);
4559
+ dIdx += dStride;
4560
+ mIdx += mStride;
4970
4561
  }
4971
- dest.push({
4972
- x,
4973
- y,
4974
- w,
4975
- h,
4976
- data,
4977
- type: 1 /* BINARY */
4978
- });
4562
+ return didChange;
4979
4563
  }
4980
4564
 
4981
- // src/PixelData/PixelData.ts
4982
- var PixelData = class {
4983
- data32;
4984
- imageData;
4985
- width;
4986
- height;
4987
- constructor(imageData) {
4988
- this.data32 = imageDataToUInt32Array(imageData);
4989
- this.imageData = imageData;
4990
- this.width = imageData.width;
4991
- this.height = imageData.height;
4992
- }
4993
- set(imageData) {
4994
- ;
4995
- this.imageData = imageData;
4996
- this.data32 = imageDataToUInt32Array(imageData);
4997
- this.width = imageData.width;
4998
- this.height = imageData.height;
4999
- }
5000
- };
5001
-
5002
4565
  // src/PixelData/blendPixelDataPaintBuffer.ts
5003
4566
  var SCRATCH_OPTS = {
5004
4567
  x: 0,
@@ -5245,103 +4808,140 @@ function writePixelDataBuffer(target, data, _x, _y, _w, _h) {
5245
4808
  }
5246
4809
  }
5247
4810
 
5248
- // src/PixelTile/PaintBuffer.ts
5249
- var PaintBuffer = class {
5250
- constructor(config, tilePool) {
5251
- this.config = config;
5252
- this.tilePool = tilePool;
5253
- this.lookup = [];
4811
+ // src/PixelData/writePaintBufferToPixelData.ts
4812
+ function writePaintBufferToPixelData(target, paintBuffer, writePixelDataBufferFn = writePixelDataBuffer) {
4813
+ const tileShift = paintBuffer.config.tileShift;
4814
+ const lookup = paintBuffer.lookup;
4815
+ for (let i = 0; i < lookup.length; i++) {
4816
+ const tile = lookup[i];
4817
+ if (tile) {
4818
+ const dx = tile.tx << tileShift;
4819
+ const dy = tile.ty << tileShift;
4820
+ writePixelDataBufferFn(target, tile.data32, dx, dy, tile.width, tile.height);
4821
+ }
5254
4822
  }
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;
4823
+ }
4824
+
4825
+ // src/Paint/makeCirclePaintAlphaMask.ts
4826
+ function makeCirclePaintAlphaMask(size, fallOff = (d) => d) {
4827
+ const area = size * size;
4828
+ const data = new Uint8Array(area);
4829
+ const radius = size / 2;
4830
+ const invR = 1 / radius;
4831
+ const centerOffset = -Math.ceil(radius - 0.5);
4832
+ for (let y = 0; y < size; y++) {
4833
+ const rowOffset = y * size;
4834
+ const dy = y - radius + 0.5;
4835
+ const dy2 = dy * dy;
4836
+ for (let x = 0; x < size; x++) {
4837
+ const dx = x - radius + 0.5;
4838
+ const distSqr = dx * dx + dy2;
4839
+ if (distSqr <= radius * radius) {
4840
+ const dist = Math.sqrt(distSqr) * invR;
4841
+ const strength = fallOff(1 - dist);
4842
+ if (strength > 0) {
4843
+ const intensity = strength * 255 | 0;
4844
+ data[rowOffset + x] = Math.max(0, Math.min(255, intensity));
5274
4845
  }
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
4846
  }
5282
4847
  }
5283
4848
  }
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
- }
4849
+ return {
4850
+ type: 0 /* ALPHA */,
4851
+ data,
4852
+ w: size,
4853
+ h: size,
4854
+ centerOffsetX: centerOffset,
4855
+ centerOffsetY: centerOffset
4856
+ };
4857
+ }
4858
+
4859
+ // src/Paint/makeCirclePaintBinaryMask.ts
4860
+ function makeCirclePaintBinaryMask(size) {
4861
+ const area = size * size;
4862
+ const data = new Uint8Array(area);
4863
+ const radius = size / 2;
4864
+ const centerOffset = -Math.ceil(radius - 0.5);
4865
+ for (let y = 0; y < size; y++) {
4866
+ for (let x = 0; x < size; x++) {
4867
+ const dx = x - radius + 0.5;
4868
+ const dy = y - radius + 0.5;
4869
+ const distSqr = dx * dx + dy * dy;
4870
+ if (distSqr <= radius * radius) {
4871
+ data[y * size + x] = 1;
5306
4872
  }
5307
- });
4873
+ }
5308
4874
  }
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
- }
4875
+ return {
4876
+ type: 1 /* BINARY */,
4877
+ data,
4878
+ w: size,
4879
+ h: size,
4880
+ centerOffsetX: centerOffset,
4881
+ centerOffsetY: centerOffset
4882
+ };
4883
+ }
4884
+
4885
+ // src/Paint/makePaintMask.ts
4886
+ function makePaintBinaryMask(mask) {
4887
+ return {
4888
+ type: 1 /* BINARY */,
4889
+ data: mask.data,
4890
+ w: mask.w,
4891
+ h: mask.h,
4892
+ centerOffsetX: -(mask.w >> 1),
4893
+ centerOffsetY: -(mask.h >> 1)
4894
+ };
4895
+ }
4896
+ function makePaintAlphaMask(mask) {
4897
+ return {
4898
+ type: 0 /* ALPHA */,
4899
+ data: mask.data,
4900
+ w: mask.w,
4901
+ h: mask.h,
4902
+ centerOffsetX: -(mask.w >> 1),
4903
+ centerOffsetY: -(mask.h >> 1)
4904
+ };
4905
+ }
4906
+
4907
+ // src/Internal/helpers.ts
4908
+ var macro_halfAndFloor = (value) => value >> 1;
4909
+
4910
+ // src/Paint/makeRectFalloffPaintAlphaMask.ts
4911
+ function makeRectFalloffPaintAlphaMask(width, height, fallOff = (d) => d) {
4912
+ const fPx = Math.floor(width / 2);
4913
+ const fPy = Math.floor(height / 2);
4914
+ const invHalfW = 2 / width;
4915
+ const invHalfH = 2 / height;
4916
+ const offX = width % 2 === 0 ? 0.5 : 0;
4917
+ const offY = height % 2 === 0 ? 0.5 : 0;
4918
+ const area = width * height;
4919
+ const data = new Uint8Array(area);
4920
+ for (let y = 0; y < height; y++) {
4921
+ const dy = Math.abs(y - fPy + offY) * invHalfH;
4922
+ const rowOffset = y * width;
4923
+ for (let x = 0; x < width; x++) {
4924
+ const dx = Math.abs(x - fPx + offX) * invHalfW;
4925
+ const dist = dx > dy ? dx : dy;
4926
+ const strength = fallOff(1 - dist);
4927
+ if (strength > 0) {
4928
+ const intensity = strength * 255 | 0;
4929
+ data[rowOffset + x] = Math.max(0, Math.min(255, intensity));
5335
4930
  }
5336
- });
5337
- }
5338
- clear() {
5339
- this.tilePool.releaseTiles(this.lookup);
4931
+ }
5340
4932
  }
5341
- };
4933
+ return {
4934
+ type: 0 /* ALPHA */,
4935
+ data,
4936
+ w: width,
4937
+ h: height,
4938
+ centerOffsetX: -macro_halfAndFloor(width),
4939
+ centerOffsetY: -macro_halfAndFloor(height)
4940
+ };
4941
+ }
5342
4942
 
5343
- // src/PixelTile/PaintBufferRenderer.ts
5344
- function makePaintBufferRenderer(paintBuffer, offscreenCanvasClass = OffscreenCanvas) {
4943
+ // src/Paint/PaintBufferCanvasRenderer.ts
4944
+ function makePaintBufferCanvasRenderer(paintBuffer, offscreenCanvasClass = OffscreenCanvas) {
5345
4945
  const config = paintBuffer.config;
5346
4946
  const tileSize = config.tileSize;
5347
4947
  const tileShift = config.tileShift;
@@ -5350,16 +4950,20 @@ function makePaintBufferRenderer(paintBuffer, offscreenCanvasClass = OffscreenCa
5350
4950
  const ctx = canvas.getContext("2d");
5351
4951
  if (!ctx) throw new Error(CANVAS_CTX_FAILED);
5352
4952
  ctx.imageSmoothingEnabled = false;
5353
- return function drawPaintBuffer(target) {
4953
+ return function drawPaintBuffer(targetCtx, alpha = 255, compOperation = "source-over") {
4954
+ targetCtx.globalAlpha = alpha / 255;
4955
+ targetCtx.globalCompositeOperation = compOperation;
5354
4956
  for (let i = 0; i < lookup.length; i++) {
5355
4957
  const tile = lookup[i];
5356
4958
  if (tile) {
5357
4959
  const dx = tile.tx << tileShift;
5358
4960
  const dy = tile.ty << tileShift;
5359
4961
  ctx.putImageData(tile.imageData, 0, 0);
5360
- target.drawImage(canvas, dx, dy);
4962
+ targetCtx.drawImage(canvas, dx, dy);
5361
4963
  }
5362
4964
  }
4965
+ targetCtx.globalAlpha = 1;
4966
+ targetCtx.globalCompositeOperation = "source-over";
5363
4967
  };
5364
4968
  }
5365
4969
  export {
@@ -5384,13 +4988,11 @@ export {
5384
4988
  applyBinaryMaskToAlphaMask,
5385
4989
  applyBinaryMaskToPixelData,
5386
4990
  applyPatchTiles,
5387
- applyRectBrushToPixelData,
5388
4991
  base64DecodeArrayBuffer,
5389
4992
  base64EncodeArrayBuffer,
5390
4993
  blendColorPixelData,
5391
4994
  blendColorPixelDataAlphaMask,
5392
4995
  blendColorPixelDataBinaryMask,
5393
- blendColorPixelDataCircleMask,
5394
4996
  blendPixel,
5395
4997
  blendPixelData,
5396
4998
  blendPixelDataAlphaMask,
@@ -5432,12 +5034,8 @@ export {
5432
5034
  fillPixelDataFast,
5433
5035
  floodFillSelection,
5434
5036
  forEachLinePoint,
5435
- getCircleBrushOrPencilBounds,
5436
- getCircleBrushOrPencilStrokeBounds,
5437
5037
  getImageDataFromClipboard,
5438
5038
  getIndexedImageColorCounts,
5439
- getRectBrushOrPencilBounds,
5440
- getRectBrushOrPencilStrokeBounds,
5441
5039
  getRectsBounds,
5442
5040
  getSupportedPixelFormats,
5443
5041
  hardLightFast,
@@ -5471,15 +5069,18 @@ export {
5471
5069
  makeBinaryMask,
5472
5070
  makeBlendModeRegistry,
5473
5071
  makeCanvasFrameRenderer,
5474
- makeCircleAlphaMask,
5475
- makeCircleBinaryMask,
5072
+ makeCirclePaintAlphaMask,
5073
+ makeCirclePaintBinaryMask,
5476
5074
  makeFastBlendModeRegistry,
5477
5075
  makeFullPixelMutator,
5478
5076
  makeHistoryAction,
5479
5077
  makeImageDataLike,
5480
- makePaintBufferRenderer,
5078
+ makePaintAlphaMask,
5079
+ makePaintBinaryMask,
5080
+ makePaintBufferCanvasRenderer,
5481
5081
  makePerfectBlendModeRegistry,
5482
5082
  makePixelCanvas,
5083
+ makeRectFalloffPaintAlphaMask,
5483
5084
  makeReusableCanvas,
5484
5085
  makeReusableImageData,
5485
5086
  makeReusableOffscreenCanvas,
@@ -5491,15 +5092,7 @@ export {
5491
5092
  multiplyPerfect,
5492
5093
  mutatorApplyAlphaMask,
5493
5094
  mutatorApplyBinaryMask,
5494
- mutatorApplyCircleBrushStroke,
5495
- mutatorApplyCirclePencil,
5496
- mutatorApplyCirclePencilStroke,
5497
- mutatorApplyRectBrush,
5498
- mutatorApplyRectBrushStroke,
5499
- mutatorApplyRectPencil,
5500
- mutatorApplyRectPencilStroke,
5501
5095
  mutatorBlendColor,
5502
- mutatorBlendColorCircleMask,
5503
5096
  mutatorBlendPixel,
5504
5097
  mutatorBlendPixelData,
5505
5098
  mutatorBlendPixelDataAlphaMask,
@@ -5539,6 +5132,7 @@ export {
5539
5132
  subtractFast,
5540
5133
  subtractPerfect,
5541
5134
  toBlendModeIndexAndName,
5135
+ trimMaskRectBounds,
5542
5136
  trimRectBounds,
5543
5137
  uInt32ArrayToImageData,
5544
5138
  uInt32ArrayToImageDataLike,
@@ -5554,6 +5148,7 @@ export {
5554
5148
  writeImageDataBuffer,
5555
5149
  writeImageDataToClipboard,
5556
5150
  writeImgBlobToClipboard,
5151
+ writePaintBufferToPixelData,
5557
5152
  writePixelDataBuffer
5558
5153
  };
5559
5154
  //# sourceMappingURL=index.dev.js.map