pixel-data-js 0.21.0 → 0.22.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/dist/index.dev.cjs +725 -362
  2. package/dist/index.dev.cjs.map +1 -1
  3. package/dist/index.dev.js +712 -361
  4. package/dist/index.dev.js.map +1 -1
  5. package/dist/index.prod.cjs +725 -362
  6. package/dist/index.prod.cjs.map +1 -1
  7. package/dist/index.prod.d.ts +282 -127
  8. package/dist/index.prod.js +712 -361
  9. package/dist/index.prod.js.map +1 -1
  10. package/package.json +1 -1
  11. package/src/Algorithm/floodFillSelection.ts +12 -14
  12. package/src/BlendModes/toBlendModeIndexAndName.ts +0 -7
  13. package/src/Clipboard/writeImgBlobToClipboard.ts +1 -1
  14. package/src/History/PixelMutator/mutatorApplyAlphaMask.ts +3 -0
  15. package/src/History/PixelMutator/mutatorApplyBinaryMask.ts +3 -0
  16. package/src/History/PixelMutator/mutatorApplyCircleBrush.ts +25 -6
  17. package/src/History/PixelMutator/mutatorApplyCircleBrushStroke.ts +89 -46
  18. package/src/History/PixelMutator/mutatorApplyCirclePencil.ts +7 -7
  19. package/src/History/PixelMutator/mutatorApplyCirclePencilStroke.ts +81 -41
  20. package/src/History/PixelMutator/mutatorApplyRectBrush.ts +3 -0
  21. package/src/History/PixelMutator/mutatorApplyRectBrushStroke.ts +18 -5
  22. package/src/History/PixelMutator/mutatorApplyRectPencil.ts +3 -0
  23. package/src/History/PixelMutator/mutatorApplyRectPencilStroke.ts +19 -4
  24. package/src/History/PixelMutator/mutatorBlendColor.ts +4 -0
  25. package/src/History/PixelMutator/mutatorBlendPixelData.ts +4 -0
  26. package/src/History/PixelMutator/mutatorClear.ts +10 -8
  27. package/src/History/PixelMutator/mutatorFill.ts +7 -4
  28. package/src/History/PixelMutator/mutatorFillBinaryMask.ts +28 -0
  29. package/src/History/PixelMutator/mutatorInvert.ts +3 -0
  30. package/src/ImageData/extractImageDataBuffer.ts +3 -3
  31. package/src/ImageData/{imageDataToAlphaMask.ts → imageDataToAlphaMaskBuffer.ts} +3 -4
  32. package/src/ImageData/resizeImageData.ts +3 -5
  33. package/src/ImageData/writeImageDataBuffer.ts +7 -7
  34. package/src/Mask/AlphaMask.ts +16 -0
  35. package/src/Mask/BinaryMask.ts +16 -0
  36. package/src/Mask/CircleBrushAlphaMask.ts +32 -0
  37. package/src/Mask/CircleBrushBinaryMask.ts +30 -0
  38. package/src/Mask/applyBinaryMaskToAlphaMask.ts +12 -9
  39. package/src/Mask/copyMask.ts +9 -3
  40. package/src/Mask/extractMask.ts +33 -31
  41. package/src/Mask/extractMaskBuffer.ts +87 -0
  42. package/src/Mask/invertMask.ts +6 -4
  43. package/src/Mask/mergeAlphaMasks.ts +11 -10
  44. package/src/Mask/mergeBinaryMasks.ts +10 -9
  45. package/src/Mask/setMaskData.ts +7 -0
  46. package/src/MaskRect/merge2BinaryMaskRects.ts +81 -0
  47. package/src/MaskRect/mergeBinaryMaskRects.ts +39 -0
  48. package/src/MaskRect/subtractBinaryMaskRects.ts +80 -0
  49. package/src/PixelData/applyAlphaMaskToPixelData.ts +8 -5
  50. package/src/PixelData/applyBinaryMaskToPixelData.ts +8 -9
  51. package/src/PixelData/applyCircleBrushToPixelData.ts +54 -62
  52. package/src/PixelData/blendColorPixelDataAlphaMask.ts +35 -25
  53. package/src/PixelData/blendColorPixelDataBinaryMask.ts +26 -19
  54. package/src/PixelData/blendPixelDataAlphaMask.ts +3 -3
  55. package/src/PixelData/blendPixelDataBinaryMask.ts +3 -3
  56. package/src/PixelData/fillPixelData.ts +15 -42
  57. package/src/PixelData/fillPixelDataBinaryMask.ts +79 -0
  58. package/src/PixelData/invertPixelData.ts +3 -3
  59. package/src/PixelData/pixelDataToAlphaMask.ts +4 -2
  60. package/src/PixelData/writePixelDataBuffer.ts +2 -3
  61. package/src/Rect/getRectsBounds.ts +22 -0
  62. package/src/Rect/trimRectBounds.ts +20 -17
  63. package/src/_types.ts +55 -29
  64. package/src/index.ts +16 -1
@@ -196,8 +196,8 @@ function extractImageDataBuffer(imageData, _x, _y, _w, _h) {
196
196
  return out;
197
197
  }
198
198
 
199
- // src/Mask/extractMask.ts
200
- function extractMask(mask, maskWidth, xOrRect, y, w, h) {
199
+ // src/Mask/extractMaskBuffer.ts
200
+ function extractMaskBuffer(maskBuffer, maskWidth, xOrRect, y, w, h) {
201
201
  let finalX;
202
202
  let finalY;
203
203
  let finalW;
@@ -214,7 +214,7 @@ function extractMask(mask, maskWidth, xOrRect, y, w, h) {
214
214
  finalH = h;
215
215
  }
216
216
  const out = new Uint8Array(finalW * finalH);
217
- const srcH = mask.length / maskWidth;
217
+ const srcH = maskBuffer.length / maskWidth;
218
218
  for (let row = 0; row < finalH; row++) {
219
219
  const currentSrcY = finalY + row;
220
220
  if (currentSrcY < 0 || currentSrcY >= srcH) {
@@ -226,7 +226,7 @@ function extractMask(mask, maskWidth, xOrRect, y, w, h) {
226
226
  const srcOffset = currentSrcY * maskWidth + start;
227
227
  const dstOffset = row * finalW + (start - finalX);
228
228
  const count = end - start;
229
- out.set(mask.subarray(srcOffset, srcOffset + count), dstOffset);
229
+ out.set(maskBuffer.subarray(srcOffset, srcOffset + count), dstOffset);
230
230
  }
231
231
  }
232
232
  return out;
@@ -244,8 +244,8 @@ function trimRectBounds(target, bounds) {
244
244
  if (intersectedMaxX <= intersectedX || intersectedMaxY <= intersectedY) {
245
245
  target.w = 0;
246
246
  target.h = 0;
247
- if ("mask" in target && target.mask) {
248
- target.mask = new Uint8Array(0);
247
+ if ("data" in target && target.data) {
248
+ target.data = new Uint8Array(0);
249
249
  }
250
250
  return;
251
251
  }
@@ -257,15 +257,15 @@ function trimRectBounds(target, bounds) {
257
257
  target.y = intersectedY;
258
258
  target.w = intersectedW;
259
259
  target.h = intersectedH;
260
- if ("mask" in target && target.mask) {
261
- const currentMask = extractMask(target.mask, originalW, offsetX, offsetY, intersectedW, intersectedH);
260
+ if ("data" in target && target.data) {
261
+ const currentMaskBuffer = extractMaskBuffer(target.data, originalW, offsetX, offsetY, intersectedW, intersectedH);
262
262
  let minX = intersectedW;
263
263
  let maxX = -1;
264
264
  let minY = intersectedH;
265
265
  let maxY = -1;
266
266
  for (let y = 0; y < intersectedH; y++) {
267
267
  for (let x = 0; x < intersectedW; x++) {
268
- if (currentMask[y * intersectedW + x] !== 0) {
268
+ if (currentMaskBuffer[y * intersectedW + x] !== 0) {
269
269
  if (x < minX) minX = x;
270
270
  if (x > maxX) maxX = x;
271
271
  if (y < minY) minY = y;
@@ -276,19 +276,22 @@ function trimRectBounds(target, bounds) {
276
276
  if (maxX === -1) {
277
277
  target.w = 0;
278
278
  target.h = 0;
279
- target.mask = new Uint8Array(0);
279
+ target.data = new Uint8Array(0);
280
280
  return;
281
281
  }
282
282
  const finalW = maxX - minX + 1;
283
283
  const finalH = maxY - minY + 1;
284
284
  if (finalW !== intersectedW || finalH !== intersectedH) {
285
- target.mask = extractMask(currentMask, intersectedW, minX, minY, finalW, finalH);
285
+ const newMaskBuffer = extractMaskBuffer(currentMaskBuffer, intersectedW, minX, minY, finalW, finalH);
286
286
  target.x += minX;
287
287
  target.y += minY;
288
288
  target.w = finalW;
289
289
  target.h = finalH;
290
+ target.data = newMaskBuffer;
290
291
  } else {
291
- target.mask = currentMask;
292
+ target.w = finalW;
293
+ target.h = finalH;
294
+ target.data = currentMaskBuffer;
292
295
  }
293
296
  }
294
297
  }
@@ -398,17 +401,19 @@ function floodFillSelection(img, startX, startY, {
398
401
  if (matchCount === 0) {
399
402
  return null;
400
403
  }
404
+ const w = maxX - minX + 1;
405
+ const h = maxY - minY + 1;
401
406
  const selectionRect = {
402
407
  x: minX,
403
408
  y: minY,
404
- w: maxX - minX + 1,
405
- h: maxY - minY + 1,
406
- mask: new Uint8Array((maxX - minX + 1) * (maxY - minY + 1)),
407
- maskType: 1 /* BINARY */
409
+ w,
410
+ h,
411
+ data: new Uint8Array(w * h),
412
+ type: 1 /* BINARY */
408
413
  };
409
414
  const sw = selectionRect.w;
410
415
  const sh = selectionRect.h;
411
- const finalMask = selectionRect.mask;
416
+ const finalMask = selectionRect.data;
412
417
  for (let i = 0; i < matchCount; i++) {
413
418
  const mx = matchX[i] - selectionRect.x;
414
419
  const my = matchY[i] - selectionRect.y;
@@ -1483,16 +1488,7 @@ function toBlendModeIndexAndName(input) {
1483
1488
  const num = Number(trimmed);
1484
1489
  const isNumeric = trimmed !== "" && !Number.isNaN(num);
1485
1490
  if (isNumeric && Number.isInteger(num)) {
1486
- console.log({
1487
- trimmed,
1488
- num,
1489
- isNumeric,
1490
- isInt: Number.isInteger(num)
1491
- });
1492
1491
  const name = getKeyByValue(BaseBlendMode, num);
1493
- console.log({
1494
- name
1495
- });
1496
1492
  if (name === void 0) throw new Error(`Invalid index: ${num}`);
1497
1493
  return {
1498
1494
  blendIndex: num,
@@ -1866,7 +1862,6 @@ function applyAlphaMaskToPixelData(dst, mask, opts = {}) {
1866
1862
  w: width = dst.width,
1867
1863
  h: height = dst.height,
1868
1864
  alpha: globalAlpha = 255,
1869
- mw,
1870
1865
  mx = 0,
1871
1866
  my = 0,
1872
1867
  invertMask = false
@@ -1888,15 +1883,14 @@ function applyAlphaMaskToPixelData(dst, mask, opts = {}) {
1888
1883
  h = Math.min(h, dst.height - y);
1889
1884
  if (w <= 0) return;
1890
1885
  if (h <= 0) return;
1891
- const mPitch = mw ?? width;
1886
+ const mPitch = mask.w;
1892
1887
  if (mPitch <= 0) return;
1893
- const maskHeight = mask.length / mPitch | 0;
1894
1888
  const startX = mx + (x - targetX);
1895
1889
  const startY = my + (y - targetY);
1896
1890
  const sX0 = Math.max(0, startX);
1897
1891
  const sY0 = Math.max(0, startY);
1898
1892
  const sX1 = Math.min(mPitch, startX + w);
1899
- const sY1 = Math.min(maskHeight, startY + h);
1893
+ const sY1 = Math.min(mask.h, startY + h);
1900
1894
  const finalW = sX1 - sX0;
1901
1895
  const finalH = sY1 - sY0;
1902
1896
  if (finalW <= 0) return;
@@ -1907,11 +1901,12 @@ function applyAlphaMaskToPixelData(dst, mask, opts = {}) {
1907
1901
  const dw = dst.width;
1908
1902
  const dStride = dw - finalW;
1909
1903
  const mStride = mPitch - finalW;
1904
+ const maskData = mask.data;
1910
1905
  let dIdx = (y + yShift) * dw + (x + xShift);
1911
1906
  let mIdx = sY0 * mPitch + sX0;
1912
1907
  for (let iy = 0; iy < h; iy++) {
1913
1908
  for (let ix = 0; ix < w; ix++) {
1914
- const mVal = mask[mIdx];
1909
+ const mVal = maskData[mIdx];
1915
1910
  const effectiveM = invertMask ? 255 - mVal : mVal;
1916
1911
  let weight = 0;
1917
1912
  if (effectiveM === 0) {
@@ -1971,13 +1966,12 @@ function applyBinaryMaskToPixelData(dst, mask, opts = {}) {
1971
1966
  y: targetY = 0,
1972
1967
  w: width = dst.width,
1973
1968
  h: height = dst.height,
1974
- alpha = 255,
1975
- mw,
1969
+ alpha: globalAlpha = 255,
1976
1970
  mx = 0,
1977
1971
  my = 0,
1978
1972
  invertMask = false
1979
1973
  } = opts;
1980
- if (alpha === 0) return;
1974
+ if (globalAlpha === 0) return;
1981
1975
  let x = targetX;
1982
1976
  let y = targetY;
1983
1977
  let w = width;
@@ -1994,15 +1988,14 @@ function applyBinaryMaskToPixelData(dst, mask, opts = {}) {
1994
1988
  h = Math.min(h, dst.height - y);
1995
1989
  if (w <= 0) return;
1996
1990
  if (h <= 0) return;
1997
- const mPitch = mw ?? width;
1991
+ const mPitch = mask.w;
1998
1992
  if (mPitch <= 0) return;
1999
- const maskHeight = mask.length / mPitch | 0;
2000
1993
  const startX = mx + (x - targetX);
2001
1994
  const startY = my + (y - targetY);
2002
1995
  const sX0 = Math.max(0, startX);
2003
1996
  const sY0 = Math.max(0, startY);
2004
1997
  const sX1 = Math.min(mPitch, startX + w);
2005
- const sY1 = Math.min(maskHeight, startY + h);
1998
+ const sY1 = Math.min(mask.h, startY + h);
2006
1999
  const finalW = sX1 - sX0;
2007
2000
  const finalH = sY1 - sY0;
2008
2001
  if (finalW <= 0) return;
@@ -2013,19 +2006,20 @@ function applyBinaryMaskToPixelData(dst, mask, opts = {}) {
2013
2006
  const dw = dst.width;
2014
2007
  const dStride = dw - finalW;
2015
2008
  const mStride = mPitch - finalW;
2009
+ const maskData = mask.data;
2016
2010
  let dIdx = (y + yShift) * dw + (x + xShift);
2017
2011
  let mIdx = sY0 * mPitch + sX0;
2018
2012
  for (let iy = 0; iy < h; iy++) {
2019
2013
  for (let ix = 0; ix < w; ix++) {
2020
- const mVal = mask[mIdx];
2014
+ const mVal = maskData[mIdx];
2021
2015
  const isMaskedOut = invertMask ? mVal !== 0 : mVal === 0;
2022
2016
  if (isMaskedOut) {
2023
2017
  dst32[dIdx] = (dst32[dIdx] & 16777215) >>> 0;
2024
- } else if (alpha !== 255) {
2018
+ } else if (globalAlpha !== 255) {
2025
2019
  const d = dst32[dIdx];
2026
2020
  const da = d >>> 24;
2027
2021
  if (da !== 0) {
2028
- const finalAlpha = da === 255 ? alpha : da * alpha + 128 >> 8;
2022
+ const finalAlpha = da === 255 ? globalAlpha : da * globalAlpha + 128 >> 8;
2029
2023
  dst32[dIdx] = (d & 16777215 | finalAlpha << 24) >>> 0;
2030
2024
  }
2031
2025
  }
@@ -2088,144 +2082,42 @@ function getCircleBrushOrPencilBounds(centerX, centerY, brushSize, targetWidth,
2088
2082
  return res;
2089
2083
  }
2090
2084
 
2091
- // src/PixelData/applyCircleBrushToPixelData.ts
2092
- function applyCircleBrushToPixelData(target, color, centerX, centerY, brushSize, alpha = 255, fallOff, blendFn = sourceOverPerfect, bounds) {
2093
- const targetWidth = target.width;
2094
- const targetHeight = target.height;
2095
- const b = bounds ?? getCircleBrushOrPencilBounds(centerX, centerY, brushSize, targetWidth, targetHeight);
2096
- if (b.w <= 0 || b.h <= 0) return;
2097
- const data32 = target.data32;
2098
- const r = brushSize / 2;
2099
- const rSqr = r * r;
2100
- const invR = 1 / r;
2101
- const centerOffset = brushSize % 2 === 0 ? 0.5 : 0;
2102
- const endX = b.x + b.w;
2103
- const endY = b.y + b.h;
2104
- const fCenterX = Math.floor(centerX);
2105
- const fCenterY = Math.floor(centerY);
2106
- const baseSrcAlpha = color >>> 24;
2107
- const colorRGB = color & 16777215;
2108
- const isOpaque = alpha === 255;
2109
- const isOverwrite = blendFn.isOverwrite;
2110
- for (let cy = b.y; cy < endY; cy++) {
2111
- const relY = cy - fCenterY + centerOffset;
2112
- const dySqr = relY * relY;
2113
- const rowOffset = cy * targetWidth;
2114
- for (let cx = b.x; cx < endX; cx++) {
2115
- const relX = cx - fCenterX + centerOffset;
2116
- const dSqr = relX * relX + dySqr;
2117
- if (dSqr <= rSqr) {
2118
- const idx = rowOffset + cx;
2119
- let weight = alpha;
2120
- const strength = fallOff(1 - Math.sqrt(dSqr) * invR);
2121
- const maskVal = strength * 255 | 0;
2122
- if (maskVal === 0) continue;
2123
- if (isOpaque) {
2124
- weight = maskVal;
2125
- } else if (maskVal !== 255) {
2126
- weight = maskVal * alpha + 128 >> 8;
2127
- }
2128
- let finalCol = color;
2129
- if (weight < 255) {
2130
- const a = baseSrcAlpha * weight + 128 >> 8;
2131
- if (a === 0 && !isOverwrite) continue;
2132
- finalCol = (colorRGB | a << 24) >>> 0;
2133
- }
2134
- data32[idx] = blendFn(finalCol, data32[idx]);
2135
- }
2136
- }
2137
- }
2138
- }
2139
-
2140
- // src/History/PixelMutator/mutatorApplyCircleBrush.ts
2141
- var defaults3 = {
2142
- applyCircleBrushToPixelData,
2143
- getCircleBrushOrPencilBounds
2144
- };
2145
- var mutatorApplyCircleBrush = ((writer, deps = defaults3) => {
2146
- const {
2147
- applyCircleBrushToPixelData: applyCircleBrushToPixelData2 = defaults3.applyCircleBrushToPixelData,
2148
- getCircleBrushOrPencilBounds: getCircleBrushOrPencilBounds2 = defaults3.getCircleBrushOrPencilBounds
2149
- } = deps;
2150
- const boundsOut = {
2151
- x: 0,
2152
- y: 0,
2153
- w: 0,
2154
- h: 0
2155
- };
2156
- return {
2157
- applyCircleBrush(color, centerX, centerY, brushSize, alpha = 255, fallOff, blendFn) {
2158
- const bounds = getCircleBrushOrPencilBounds2(centerX, centerY, brushSize, writer.target.width, writer.target.height, boundsOut);
2159
- const {
2160
- x,
2161
- y,
2162
- w,
2163
- h
2164
- } = bounds;
2165
- writer.accumulator.storeRegionBeforeState(x, y, w, h);
2166
- applyCircleBrushToPixelData2(writer.target, color, centerX, centerY, brushSize, alpha, fallOff, blendFn, bounds);
2167
- }
2168
- };
2169
- });
2170
-
2171
- // src/Algorithm/forEachLinePoint.ts
2172
- function forEachLinePoint(x0, y0, x1, y1, callback) {
2173
- const dx = x1 - x0;
2174
- const dy = y1 - y0;
2175
- const steps = Math.max(Math.abs(dx), Math.abs(dy));
2176
- if (steps === 0) {
2177
- callback(x0, y0);
2178
- return;
2179
- }
2180
- const xInc = dx / steps;
2181
- const yInc = dy / steps;
2182
- let curX = x0;
2183
- let curY = y0;
2184
- for (let i = 0; i <= steps; i++) {
2185
- callback(curX, curY);
2186
- curX += xInc;
2187
- curY += yInc;
2188
- }
2189
- }
2190
-
2191
2085
  // src/PixelData/blendColorPixelDataAlphaMask.ts
2192
- function blendColorPixelDataAlphaMask(dst, color, mask, opts) {
2193
- const {
2194
- x: targetX = 0,
2195
- y: targetY = 0,
2196
- w: width = dst.width,
2197
- h: height = dst.height,
2198
- alpha: globalAlpha = 255,
2199
- blendFn = sourceOverPerfect,
2200
- mw = width,
2201
- mx = 0,
2202
- my = 0,
2203
- invertMask = false
2204
- } = opts;
2205
- if (globalAlpha === 0 || !mask) return;
2086
+ function blendColorPixelDataAlphaMask(dst, color, mask, opts = {}) {
2087
+ const targetX = opts.x ?? 0;
2088
+ const targetY = opts.y ?? 0;
2089
+ const w = opts.w ?? mask.w;
2090
+ const h = opts.h ?? mask.h;
2091
+ const globalAlpha = opts.alpha ?? 255;
2092
+ const blendFn = opts.blendFn ?? sourceOverPerfect;
2093
+ const mx = opts.mx ?? 0;
2094
+ const my = opts.my ?? 0;
2095
+ const invertMask = opts.invertMask ?? false;
2096
+ if (globalAlpha === 0) return;
2206
2097
  const baseSrcAlpha = color >>> 24;
2207
2098
  const isOverwrite = blendFn.isOverwrite || false;
2208
2099
  if (baseSrcAlpha === 0 && !isOverwrite) return;
2209
2100
  let x = targetX;
2210
2101
  let y = targetY;
2211
- let w = width;
2212
- let h = height;
2102
+ let actualW = w;
2103
+ let actualH = h;
2213
2104
  if (x < 0) {
2214
- w += x;
2105
+ actualW += x;
2215
2106
  x = 0;
2216
2107
  }
2217
2108
  if (y < 0) {
2218
- h += y;
2109
+ actualH += y;
2219
2110
  y = 0;
2220
2111
  }
2221
- const actualW = Math.min(w, dst.width - x);
2222
- const actualH = Math.min(h, dst.height - y);
2112
+ actualW = Math.min(actualW, dst.width - x);
2113
+ actualH = Math.min(actualH, dst.height - y);
2223
2114
  if (actualW <= 0 || actualH <= 0) return;
2224
2115
  const dx = x - targetX | 0;
2225
2116
  const dy = y - targetY | 0;
2226
2117
  const dst32 = dst.data32;
2227
2118
  const dw = dst.width;
2228
- const mPitch = mw;
2119
+ const mPitch = mask.w;
2120
+ const maskData = mask.data;
2229
2121
  let dIdx = y * dw + x | 0;
2230
2122
  let mIdx = (my + dy) * mPitch + (mx + dx) | 0;
2231
2123
  const dStride = dw - actualW | 0;
@@ -2234,7 +2126,7 @@ function blendColorPixelDataAlphaMask(dst, color, mask, opts) {
2234
2126
  const colorRGB = color & 16777215;
2235
2127
  for (let iy = 0; iy < actualH; iy++) {
2236
2128
  for (let ix = 0; ix < actualW; ix++) {
2237
- const mVal = mask[mIdx];
2129
+ const mVal = maskData[mIdx];
2238
2130
  const effM = invertMask ? 255 - mVal : mVal;
2239
2131
  if (effM === 0) {
2240
2132
  dIdx++;
@@ -2271,6 +2163,155 @@ function blendColorPixelDataAlphaMask(dst, color, mask, opts) {
2271
2163
  }
2272
2164
  }
2273
2165
 
2166
+ // src/PixelData/blendColorPixelDataBinaryMask.ts
2167
+ function blendColorPixelDataBinaryMask(dst, color, mask, opts = {}) {
2168
+ const targetX = opts.x ?? 0;
2169
+ const targetY = opts.y ?? 0;
2170
+ let w = opts.w ?? mask.w;
2171
+ let h = opts.h ?? mask.h;
2172
+ const globalAlpha = opts.alpha ?? 255;
2173
+ const blendFn = opts.blendFn ?? sourceOverPerfect;
2174
+ const mx = opts.mx ?? 0;
2175
+ const my = opts.my ?? 0;
2176
+ const invertMask = opts.invertMask ?? false;
2177
+ if (globalAlpha === 0) return;
2178
+ const baseSrcAlpha = color >>> 24;
2179
+ const isOverwrite = blendFn.isOverwrite || false;
2180
+ if (baseSrcAlpha === 0 && !isOverwrite) return;
2181
+ let x = targetX;
2182
+ let y = targetY;
2183
+ if (x < 0) {
2184
+ w += x;
2185
+ x = 0;
2186
+ }
2187
+ if (y < 0) {
2188
+ h += y;
2189
+ y = 0;
2190
+ }
2191
+ const actualW = Math.min(w, dst.width - x);
2192
+ const actualH = Math.min(h, dst.height - y);
2193
+ if (actualW <= 0 || actualH <= 0) return;
2194
+ let baseColorWithGlobalAlpha = color;
2195
+ if (globalAlpha < 255) {
2196
+ const a = baseSrcAlpha * globalAlpha + 128 >> 8;
2197
+ if (a === 0 && !isOverwrite) return;
2198
+ baseColorWithGlobalAlpha = (color & 16777215 | a << 24) >>> 0;
2199
+ }
2200
+ const dx = x - targetX | 0;
2201
+ const dy = y - targetY | 0;
2202
+ const dst32 = dst.data32;
2203
+ const dw = dst.width;
2204
+ const mPitch = mask.w;
2205
+ const maskData = mask.data;
2206
+ let dIdx = y * dw + x | 0;
2207
+ let mIdx = (my + dy) * mPitch + (mx + dx) | 0;
2208
+ const dStride = dw - actualW | 0;
2209
+ const mStride = mPitch - actualW | 0;
2210
+ const skipVal = invertMask ? 1 : 0;
2211
+ for (let iy = 0; iy < actualH; iy++) {
2212
+ for (let ix = 0; ix < actualW; ix++) {
2213
+ if (maskData[mIdx] === skipVal) {
2214
+ dIdx++;
2215
+ mIdx++;
2216
+ continue;
2217
+ }
2218
+ dst32[dIdx] = blendFn(baseColorWithGlobalAlpha, dst32[dIdx]);
2219
+ dIdx++;
2220
+ mIdx++;
2221
+ }
2222
+ dIdx += dStride;
2223
+ mIdx += mStride;
2224
+ }
2225
+ }
2226
+
2227
+ // src/PixelData/applyCircleBrushToPixelData.ts
2228
+ function applyCircleBrushToPixelData(target, color, centerX, centerY, brush, alpha = 255, blendFn = sourceOverPerfect, scratchOptions = {}, bounds) {
2229
+ const b = bounds ?? getCircleBrushOrPencilBounds(centerX, centerY, brush.size, target.width, target.height);
2230
+ if (b.w <= 0 || b.h <= 0) return;
2231
+ const unclippedStartX = Math.floor(centerX + brush.minOffset);
2232
+ const unclippedStartY = Math.floor(centerY + brush.minOffset);
2233
+ const ix = Math.max(unclippedStartX, b.x);
2234
+ const iy = Math.max(unclippedStartY, b.y);
2235
+ const ir = Math.min(unclippedStartX + brush.w, b.x + b.w);
2236
+ const ib = Math.min(unclippedStartY + brush.h, b.y + b.h);
2237
+ const iw = ir - ix;
2238
+ const ih = ib - iy;
2239
+ if (iw <= 0 || ih <= 0) return;
2240
+ scratchOptions.x = ix;
2241
+ scratchOptions.y = iy;
2242
+ scratchOptions.w = iw;
2243
+ scratchOptions.h = ih;
2244
+ scratchOptions.mx = ix - unclippedStartX;
2245
+ scratchOptions.my = iy - unclippedStartY;
2246
+ scratchOptions.alpha = alpha;
2247
+ scratchOptions.blendFn = blendFn;
2248
+ if (brush.type === 0 /* ALPHA */) {
2249
+ blendColorPixelDataAlphaMask(target, color, brush, scratchOptions);
2250
+ }
2251
+ if (brush.type === 1 /* BINARY */) {
2252
+ blendColorPixelDataBinaryMask(target, color, brush, scratchOptions);
2253
+ }
2254
+ }
2255
+
2256
+ // src/History/PixelMutator/mutatorApplyCircleBrush.ts
2257
+ var defaults3 = {
2258
+ applyCircleBrushToPixelData,
2259
+ getCircleBrushOrPencilBounds
2260
+ };
2261
+ var mutatorApplyCircleBrush = ((writer, deps = defaults3) => {
2262
+ const {
2263
+ applyCircleBrushToPixelData: applyCircleBrushToPixelData2 = defaults3.applyCircleBrushToPixelData,
2264
+ getCircleBrushOrPencilBounds: getCircleBrushOrPencilBounds2 = defaults3.getCircleBrushOrPencilBounds
2265
+ } = deps;
2266
+ const boundsOut = {
2267
+ x: 0,
2268
+ y: 0,
2269
+ w: 0,
2270
+ h: 0
2271
+ };
2272
+ const blendColorPixelOptions = {
2273
+ alpha: 255,
2274
+ blendFn: sourceOverPerfect,
2275
+ x: 0,
2276
+ y: 0,
2277
+ w: 0,
2278
+ h: 0
2279
+ };
2280
+ return {
2281
+ applyCircleBrush(color, centerX, centerY, brush, alpha = 255, blendFn) {
2282
+ const bounds = getCircleBrushOrPencilBounds2(centerX, centerY, brush.size, writer.target.width, writer.target.height, boundsOut);
2283
+ const {
2284
+ x,
2285
+ y,
2286
+ w,
2287
+ h
2288
+ } = bounds;
2289
+ writer.accumulator.storeRegionBeforeState(x, y, w, h);
2290
+ applyCircleBrushToPixelData2(writer.target, color, centerX, centerY, brush, alpha, blendFn, blendColorPixelOptions, bounds);
2291
+ }
2292
+ };
2293
+ });
2294
+
2295
+ // src/Algorithm/forEachLinePoint.ts
2296
+ function forEachLinePoint(x0, y0, x1, y1, callback) {
2297
+ const dx = x1 - x0;
2298
+ const dy = y1 - y0;
2299
+ const steps = Math.max(Math.abs(dx), Math.abs(dy));
2300
+ if (steps === 0) {
2301
+ callback(x0, y0);
2302
+ return;
2303
+ }
2304
+ const xInc = dx / steps;
2305
+ const yInc = dy / steps;
2306
+ let curX = x0;
2307
+ let curY = y0;
2308
+ for (let i = 0; i <= steps; i++) {
2309
+ callback(curX, curY);
2310
+ curX += xInc;
2311
+ curY += yInc;
2312
+ }
2313
+ }
2314
+
2274
2315
  // src/Rect/getCircleBrushOrPencilStrokeBounds.ts
2275
2316
  function getCircleBrushOrPencilStrokeBounds(x0, y0, x1, y1, brushSize, result) {
2276
2317
  const r = Math.ceil(brushSize / 2);
@@ -2319,8 +2360,15 @@ var mutatorApplyCircleBrushStroke = ((writer, deps = defaults4) => {
2319
2360
  w: 0,
2320
2361
  h: 0
2321
2362
  };
2363
+ const mask = {
2364
+ type: 0 /* ALPHA */,
2365
+ data: null,
2366
+ w: 0,
2367
+ h: 0
2368
+ };
2322
2369
  return {
2323
- applyCircleBrushStroke(color, x0, y0, x1, y1, brushSize, alpha = 255, fallOff, blendFn = sourceOverPerfect) {
2370
+ applyCircleBrushStroke(color, x0, y0, x1, y1, brush, alpha = 255, blendFn = sourceOverPerfect) {
2371
+ const brushSize = brush.size;
2324
2372
  const {
2325
2373
  x: bx,
2326
2374
  y: by,
@@ -2328,11 +2376,12 @@ var mutatorApplyCircleBrushStroke = ((writer, deps = defaults4) => {
2328
2376
  h: bh
2329
2377
  } = getCircleBrushOrPencilStrokeBounds2(x0, y0, x1, y1, brushSize, strokeBoundsOut);
2330
2378
  if (bw <= 0 || bh <= 0) return;
2331
- const mask = new Uint8Array(bw * bh);
2332
- const r = brushSize / 2;
2333
- const rSqr = r * r;
2334
- const invR = 1 / r;
2335
- const centerOffset = brushSize % 2 === 0 ? 0.5 : 0;
2379
+ mask.data = new Uint8Array(bw * bh);
2380
+ mask.w = bw;
2381
+ mask.h = bh;
2382
+ const maskData = mask.data;
2383
+ const brushData = brush.data;
2384
+ const minOffset = brush.minOffset;
2336
2385
  const targetWidth = writer.target.width;
2337
2386
  const targetHeight = writer.target.height;
2338
2387
  forEachLinePoint2(x0, y0, x1, y1, (px, py) => {
@@ -2347,21 +2396,20 @@ var mutatorApplyCircleBrushStroke = ((writer, deps = defaults4) => {
2347
2396
  const startY = Math.max(by, cby);
2348
2397
  const endX = Math.min(bx + bw, cbx + cbw);
2349
2398
  const endY = Math.min(by + bh, cby + cbh);
2350
- const fPx = Math.floor(px);
2351
- const fPy = Math.floor(py);
2399
+ const unclippedStartX = Math.floor(px + minOffset);
2400
+ const unclippedStartY = Math.floor(py + minOffset);
2352
2401
  for (let my = startY; my < endY; my++) {
2353
- const dy = my - fPy + centerOffset;
2354
- const dySqr = dy * dy;
2355
- const maskRowOffset = (my - by) * bw;
2402
+ const strokeMaskY = my - by;
2403
+ const strokeMaskRowOffset = strokeMaskY * bw;
2404
+ const brushY = my - unclippedStartY;
2405
+ const brushRowOffset = brushY * brushSize;
2356
2406
  for (let mx = startX; mx < endX; mx++) {
2357
- const dx = mx - fPx + centerOffset;
2358
- const dSqr = dx * dx + dySqr;
2359
- if (dSqr <= rSqr) {
2360
- const maskIdx = maskRowOffset + (mx - bx);
2361
- const dist = Math.sqrt(dSqr) * invR;
2362
- const intensity = fallOff(1 - dist) * 255 | 0;
2363
- if (intensity > mask[maskIdx]) {
2364
- mask[maskIdx] = intensity;
2407
+ const brushX = mx - unclippedStartX;
2408
+ const brushVal = brushData[brushRowOffset + brushX];
2409
+ if (brushVal > 0) {
2410
+ const strokeMaskIdx = strokeMaskRowOffset + (mx - bx);
2411
+ if (brushVal > maskData[strokeMaskIdx]) {
2412
+ maskData[strokeMaskIdx] = brushVal;
2365
2413
  }
2366
2414
  }
2367
2415
  }
@@ -2378,71 +2426,6 @@ var mutatorApplyCircleBrushStroke = ((writer, deps = defaults4) => {
2378
2426
  };
2379
2427
  });
2380
2428
 
2381
- // src/PixelData/blendColorPixelDataBinaryMask.ts
2382
- function blendColorPixelDataBinaryMask(dst, color, mask, opts = {}) {
2383
- const {
2384
- x: targetX = 0,
2385
- y: targetY = 0,
2386
- w: width = dst.width,
2387
- h: height = dst.height,
2388
- alpha: globalAlpha = 255,
2389
- blendFn = sourceOverPerfect,
2390
- mw = width,
2391
- mx = 0,
2392
- my = 0,
2393
- invertMask = false
2394
- } = opts;
2395
- if (globalAlpha === 0 || !mask) return;
2396
- const baseSrcAlpha = color >>> 24;
2397
- const isOverwrite = blendFn.isOverwrite || false;
2398
- if (baseSrcAlpha === 0 && !isOverwrite) return;
2399
- let x = targetX;
2400
- let y = targetY;
2401
- let w = width;
2402
- let h = height;
2403
- if (x < 0) {
2404
- w += x;
2405
- x = 0;
2406
- }
2407
- if (y < 0) {
2408
- h += y;
2409
- y = 0;
2410
- }
2411
- const actualW = Math.min(w, dst.width - x);
2412
- const actualH = Math.min(h, dst.height - y);
2413
- if (actualW <= 0 || actualH <= 0) return;
2414
- let baseColorWithGlobalAlpha = color;
2415
- if (globalAlpha < 255) {
2416
- const a = baseSrcAlpha * globalAlpha + 128 >> 8;
2417
- if (a === 0 && !isOverwrite) return;
2418
- baseColorWithGlobalAlpha = (color & 16777215 | a << 24) >>> 0;
2419
- }
2420
- const dx = x - targetX | 0;
2421
- const dy = y - targetY | 0;
2422
- const dst32 = dst.data32;
2423
- const dw = dst.width;
2424
- const mPitch = mw;
2425
- let dIdx = y * dw + x | 0;
2426
- let mIdx = (my + dy) * mPitch + (mx + dx) | 0;
2427
- const dStride = dw - actualW | 0;
2428
- const mStride = mPitch - actualW | 0;
2429
- const skipVal = invertMask ? 1 : 0;
2430
- for (let iy = 0; iy < actualH; iy++) {
2431
- for (let ix = 0; ix < actualW; ix++) {
2432
- if (mask[mIdx] === skipVal) {
2433
- dIdx++;
2434
- mIdx++;
2435
- continue;
2436
- }
2437
- dst32[dIdx] = blendFn(baseColorWithGlobalAlpha, dst32[dIdx]);
2438
- dIdx++;
2439
- mIdx++;
2440
- }
2441
- dIdx += dStride;
2442
- mIdx += mStride;
2443
- }
2444
- }
2445
-
2446
2429
  // src/History/PixelMutator/mutatorApplyCirclePencilStroke.ts
2447
2430
  var defaults5 = {
2448
2431
  forEachLinePoint,
@@ -2477,19 +2460,25 @@ var mutatorApplyCirclePencilStroke = ((writer, deps = defaults5) => {
2477
2460
  w: 0,
2478
2461
  h: 0
2479
2462
  };
2463
+ const mask = {
2464
+ type: 1 /* BINARY */,
2465
+ data: null,
2466
+ w: 0,
2467
+ h: 0
2468
+ };
2480
2469
  return {
2481
- applyCirclePencilStroke(color, x0, y0, x1, y1, brushSize, alpha = 255, blendFn = sourceOverPerfect) {
2470
+ applyCirclePencilStroke(color, x0, y0, x1, y1, brush, alpha = 255, blendFn = sourceOverPerfect) {
2482
2471
  const {
2483
2472
  x: bx,
2484
2473
  y: by,
2485
2474
  w: bw,
2486
2475
  h: bh
2487
- } = getCircleBrushOrPencilStrokeBounds2(x0, y0, x1, y1, brushSize, strokeBoundsOut);
2476
+ } = getCircleBrushOrPencilStrokeBounds2(x0, y0, x1, y1, brush.size, strokeBoundsOut);
2488
2477
  if (bw <= 0 || bh <= 0) return;
2489
- const mask = new Uint8Array(bw * bh);
2490
- const r = brushSize / 2;
2491
- const rSqr = r * r;
2492
- const centerOffset = brushSize % 2 === 0 ? 0.5 : 0;
2478
+ mask.data = new Uint8Array(bw * bh);
2479
+ mask.w = bw;
2480
+ mask.h = bh;
2481
+ const maskData = mask.data;
2493
2482
  const targetWidth = writer.target.width;
2494
2483
  const targetHeight = writer.target.height;
2495
2484
  forEachLinePoint2(x0, y0, x1, y1, (px, py) => {
@@ -2498,24 +2487,24 @@ var mutatorApplyCirclePencilStroke = ((writer, deps = defaults5) => {
2498
2487
  y: cby,
2499
2488
  w: cbw,
2500
2489
  h: cbh
2501
- } = getCircleBrushOrPencilBounds2(px, py, brushSize, targetWidth, targetHeight, circlePencilBounds);
2490
+ } = getCircleBrushOrPencilBounds2(px, py, brush.size, targetWidth, targetHeight, circlePencilBounds);
2502
2491
  writer.accumulator.storeRegionBeforeState(cbx, cby, cbw, cbh);
2503
- const startX = Math.max(bx, cbx);
2504
- const startY = Math.max(by, cby);
2505
- const endX = Math.min(bx + bw, cbx + cbw);
2506
- const endY = Math.min(by + bh, cby + cbh);
2507
- const fPx = Math.floor(px);
2508
- const fPy = Math.floor(py);
2492
+ const unclippedStartX = Math.floor(px + brush.minOffset);
2493
+ const unclippedStartY = Math.floor(py + brush.minOffset);
2494
+ const startX = Math.max(bx, unclippedStartX);
2495
+ const startY = Math.max(by, unclippedStartY);
2496
+ const endX = Math.min(bx + bw, unclippedStartX + brush.w);
2497
+ const endY = Math.min(by + bh, unclippedStartY + brush.h);
2509
2498
  for (let my = startY; my < endY; my++) {
2510
- const dy = my - fPy + centerOffset;
2511
- const dySqr = dy * dy;
2499
+ const brushY = my - unclippedStartY;
2512
2500
  const maskRowOffset = (my - by) * bw;
2501
+ const brushRowOffset = brushY * brush.w;
2513
2502
  for (let mx = startX; mx < endX; mx++) {
2514
- const dx = mx - fPx + centerOffset;
2515
- const dSqr = dx * dx + dySqr;
2516
- if (dSqr <= rSqr) {
2503
+ const brushX = mx - unclippedStartX;
2504
+ const brushAlpha = brush.data[brushRowOffset + brushX];
2505
+ if (brushAlpha > 0) {
2517
2506
  const maskIdx = maskRowOffset + (mx - bx);
2518
- mask[maskIdx] = 1;
2507
+ maskData[maskIdx] = brushAlpha;
2519
2508
  }
2520
2509
  }
2521
2510
  }
@@ -2682,6 +2671,12 @@ var mutatorApplyRectBrushStroke = ((writer, deps = defaults7) => {
2682
2671
  w: 0,
2683
2672
  h: 0
2684
2673
  };
2674
+ const mask = {
2675
+ type: 0 /* ALPHA */,
2676
+ data: null,
2677
+ w: 0,
2678
+ h: 0
2679
+ };
2685
2680
  return {
2686
2681
  applyRectBrushStroke(color, x0, y0, x1, y1, brushWidth, brushHeight, alpha = 255, fallOff, blendFn = sourceOverPerfect) {
2687
2682
  const {
@@ -2691,7 +2686,10 @@ var mutatorApplyRectBrushStroke = ((writer, deps = defaults7) => {
2691
2686
  h: bh
2692
2687
  } = getRectBrushOrPencilStrokeBounds2(x0, y0, x1, y1, brushWidth, brushHeight, strokeBoundsOut);
2693
2688
  if (bw <= 0 || bh <= 0) return;
2694
- const mask = new Uint8Array(bw * bh);
2689
+ mask.data = new Uint8Array(bw * bh);
2690
+ mask.w = bw;
2691
+ mask.h = bh;
2692
+ const maskData = mask.data;
2695
2693
  const halfW = brushWidth / 2;
2696
2694
  const halfH = brushHeight / 2;
2697
2695
  const invHalfW = 1 / halfW;
@@ -2724,8 +2722,8 @@ var mutatorApplyRectBrushStroke = ((writer, deps = defaults7) => {
2724
2722
  const strength = fallOff(dist);
2725
2723
  if (strength > 0) {
2726
2724
  const intensity = strength * 255 | 0;
2727
- if (intensity > mask[maskIdx]) {
2728
- mask[maskIdx] = intensity;
2725
+ if (intensity > maskData[maskIdx]) {
2726
+ maskData[maskIdx] = intensity;
2729
2727
  }
2730
2728
  }
2731
2729
  }
@@ -2809,6 +2807,12 @@ var mutatorApplyRectPencilStroke = ((writer, deps = defaults9) => {
2809
2807
  w: 0,
2810
2808
  h: 0
2811
2809
  };
2810
+ const mask = {
2811
+ type: 1 /* BINARY */,
2812
+ data: null,
2813
+ w: 0,
2814
+ h: 0
2815
+ };
2812
2816
  return {
2813
2817
  applyRectPencilStroke(color, x0, y0, x1, y1, brushWidth, brushHeight, alpha = 255, blendFn = sourceOverPerfect) {
2814
2818
  const {
@@ -2818,7 +2822,10 @@ var mutatorApplyRectPencilStroke = ((writer, deps = defaults9) => {
2818
2822
  h: bh
2819
2823
  } = getRectBrushOrPencilStrokeBounds2(x0, y0, x1, y1, brushWidth, brushHeight, strokeBoundsOut);
2820
2824
  if (bw <= 0 || bh <= 0) return;
2821
- const mask = new Uint8Array(bw * bh);
2825
+ mask.data = new Uint8Array(bw * bh);
2826
+ mask.w = bw;
2827
+ mask.h = bh;
2828
+ const maskData = mask.data;
2822
2829
  const halfW = brushWidth / 2;
2823
2830
  const halfH = brushHeight / 2;
2824
2831
  const centerOffset = brushWidth % 2 === 0 ? 0.5 : 0;
@@ -2845,7 +2852,7 @@ var mutatorApplyRectPencilStroke = ((writer, deps = defaults9) => {
2845
2852
  const dx = Math.abs(mx - fPx + centerOffset);
2846
2853
  const maskIdx = maskRowOffset + (mx - bx);
2847
2854
  if (dx <= halfW && dy <= halfH) {
2848
- mask[maskIdx] = 1;
2855
+ maskData[maskIdx] = 1;
2849
2856
  }
2850
2857
  }
2851
2858
  }
@@ -3056,24 +3063,21 @@ var mutatorBlendPixelData = ((writer, deps = defaults11) => {
3056
3063
 
3057
3064
  // src/PixelData/fillPixelData.ts
3058
3065
  var SCRATCH_RECT = makeClippedRect();
3059
- function fillPixelData(dst, color, _x, _y, _w, _h, _mask) {
3066
+ function fillPixelData(dst, color, _x, _y, _w, _h) {
3060
3067
  let x;
3061
3068
  let y;
3062
3069
  let w;
3063
3070
  let h;
3064
- let mask;
3065
3071
  if (typeof _x === "object") {
3066
3072
  x = _x.x ?? 0;
3067
3073
  y = _x.y ?? 0;
3068
3074
  w = _x.w ?? dst.width;
3069
3075
  h = _x.h ?? dst.height;
3070
- mask = _x.mask;
3071
3076
  } else if (typeof _x === "number") {
3072
3077
  x = _x;
3073
3078
  y = _y;
3074
3079
  w = _w;
3075
3080
  h = _h;
3076
- mask = _mask;
3077
3081
  } else {
3078
3082
  x = 0;
3079
3083
  y = 0;
@@ -3094,28 +3098,10 @@ function fillPixelData(dst, color, _x, _y, _w, _h, _mask) {
3094
3098
  dst32.fill(color);
3095
3099
  return;
3096
3100
  }
3097
- if (mask) {
3098
- for (let iy = 0; iy < actualH; iy++) {
3099
- const currentY = finalY + iy;
3100
- const maskY = currentY - y;
3101
- const maskOffset = maskY * w;
3102
- for (let ix = 0; ix < actualW; ix++) {
3103
- const currentX = finalX + ix;
3104
- const maskX = currentX - x;
3105
- const maskIndex = maskOffset + maskX;
3106
- const isMasked = mask[maskIndex];
3107
- if (isMasked) {
3108
- const dstIndex = currentY * dw + currentX;
3109
- dst32[dstIndex] = color;
3110
- }
3111
- }
3112
- }
3113
- } else {
3114
- for (let iy = 0; iy < actualH; iy++) {
3115
- const start = (finalY + iy) * dw + finalX;
3116
- const end = start + actualW;
3117
- dst32.fill(color, start, end);
3118
- }
3101
+ for (let iy = 0; iy < actualH; iy++) {
3102
+ const start = (finalY + iy) * dw + finalX;
3103
+ const end = start + actualW;
3104
+ dst32.fill(color, start, end);
3119
3105
  }
3120
3106
  }
3121
3107
 
@@ -3129,15 +3115,12 @@ var mutatorClear = ((writer, deps = defaults12) => {
3129
3115
  } = deps;
3130
3116
  return {
3131
3117
  clear(rect = {}) {
3132
- const {
3133
- x = 0,
3134
- y = 0,
3135
- w = writer.target.width,
3136
- h = writer.target.height,
3137
- mask = void 0
3138
- } = rect;
3118
+ const x = rect.x ?? 0;
3119
+ const y = rect.y ?? 0;
3120
+ const w = rect.w ?? writer.target.width;
3121
+ const h = rect.h ?? writer.target.height;
3139
3122
  writer.accumulator.storeRegionBeforeState(x, y, w, h);
3140
- fillPixelData2(writer.target, 0, x, y, w, h, mask);
3123
+ fillPixelData2(writer.target, 0, x, y, w, h);
3141
3124
  }
3142
3125
  };
3143
3126
  });
@@ -3156,11 +3139,10 @@ var mutatorFill = ((writer, deps = defaults13) => {
3156
3139
  x = 0,
3157
3140
  y = 0,
3158
3141
  w = writer.target.width,
3159
- h = writer.target.height,
3160
- mask = void 0
3142
+ h = writer.target.height
3161
3143
  } = rect;
3162
3144
  writer.accumulator.storeRegionBeforeState(x, y, w, h);
3163
- fillPixelData2(writer.target, color, x, y, w, h, mask);
3145
+ fillPixelData2(writer.target, color, x, y, w, h);
3164
3146
  }
3165
3147
  };
3166
3148
  });
@@ -3175,7 +3157,6 @@ function invertPixelData(pixelData, opts = {}) {
3175
3157
  w: width = pixelData.width,
3176
3158
  h: height = pixelData.height,
3177
3159
  mask,
3178
- mw,
3179
3160
  mx = 0,
3180
3161
  my = 0,
3181
3162
  invertMask = false
@@ -3190,7 +3171,7 @@ function invertPixelData(pixelData, opts = {}) {
3190
3171
  } = clip;
3191
3172
  const dst32 = dst.data32;
3192
3173
  const dw = dst.width;
3193
- const mPitch = mw ?? width;
3174
+ const mPitch = mask?.w ?? width;
3194
3175
  const dx = x - targetX;
3195
3176
  const dy = y - targetY;
3196
3177
  let dIdx = y * dw + x;
@@ -3198,9 +3179,10 @@ function invertPixelData(pixelData, opts = {}) {
3198
3179
  const dStride = dw - actualW;
3199
3180
  const mStride = mPitch - actualW;
3200
3181
  if (mask) {
3182
+ const maskData = mask.data;
3201
3183
  for (let iy = 0; iy < actualH; iy++) {
3202
3184
  for (let ix = 0; ix < actualW; ix++) {
3203
- const mVal = mask[mIdx];
3185
+ const mVal = maskData[mIdx];
3204
3186
  const isHit = invertMask ? mVal === 0 : mVal === 1;
3205
3187
  if (isHit) {
3206
3188
  dst32[dIdx] = dst32[dIdx] ^ 16777215;
@@ -3311,14 +3293,12 @@ var PixelWriter = class {
3311
3293
  // src/History/PixelMutator/mutatorApplyCirclePencil.ts
3312
3294
  var defaults15 = {
3313
3295
  applyCircleBrushToPixelData,
3314
- getCircleBrushOrPencilBounds,
3315
- fallOff: () => 1
3296
+ getCircleBrushOrPencilBounds
3316
3297
  };
3317
3298
  var mutatorApplyCirclePencil = ((writer, deps = defaults15) => {
3318
3299
  const {
3319
3300
  applyCircleBrushToPixelData: applyCircleBrushToPixelData2 = defaults15.applyCircleBrushToPixelData,
3320
- getCircleBrushOrPencilBounds: getCircleBrushOrPencilBounds2 = defaults15.getCircleBrushOrPencilBounds,
3321
- fallOff = defaults15.fallOff
3301
+ getCircleBrushOrPencilBounds: getCircleBrushOrPencilBounds2 = defaults15.getCircleBrushOrPencilBounds
3322
3302
  } = deps;
3323
3303
  const boundsOut = {
3324
3304
  x: 0,
@@ -3327,8 +3307,8 @@ var mutatorApplyCirclePencil = ((writer, deps = defaults15) => {
3327
3307
  h: 0
3328
3308
  };
3329
3309
  return {
3330
- applyCirclePencil(color, centerX, centerY, brushSize, alpha = 255, blendFn) {
3331
- const bounds = getCircleBrushOrPencilBounds2(centerX, centerY, brushSize, writer.target.width, writer.target.height, boundsOut);
3310
+ applyCirclePencil(color, centerX, centerY, brush, alpha = 255, blendFn) {
3311
+ const bounds = getCircleBrushOrPencilBounds2(centerX, centerY, brush.size, writer.target.width, writer.target.height, boundsOut);
3332
3312
  const {
3333
3313
  x,
3334
3314
  y,
@@ -3336,7 +3316,63 @@ var mutatorApplyCirclePencil = ((writer, deps = defaults15) => {
3336
3316
  h
3337
3317
  } = bounds;
3338
3318
  writer.accumulator.storeRegionBeforeState(x, y, w, h);
3339
- applyCircleBrushToPixelData2(writer.target, color, centerX, centerY, brushSize, alpha, fallOff, blendFn, bounds);
3319
+ applyCircleBrushToPixelData2(writer.target, color, centerX, centerY, brush, alpha, blendFn, bounds);
3320
+ }
3321
+ };
3322
+ });
3323
+
3324
+ // src/PixelData/fillPixelDataBinaryMask.ts
3325
+ var SCRATCH_RECT3 = makeClippedRect();
3326
+ function fillPixelDataBinaryMask(dst, color, mask, alpha = 255, x = 0, y = 0) {
3327
+ if (alpha === 0) return;
3328
+ const maskW = mask.w;
3329
+ const maskH = mask.h;
3330
+ const clip = resolveRectClipping(x, y, maskW, maskH, dst.width, dst.height, SCRATCH_RECT3);
3331
+ if (!clip.inBounds) return;
3332
+ const {
3333
+ x: finalX,
3334
+ y: finalY,
3335
+ w: actualW,
3336
+ h: actualH
3337
+ } = clip;
3338
+ const maskData = mask.data;
3339
+ const dst32 = dst.data32;
3340
+ const dw = dst.width;
3341
+ let finalCol = color;
3342
+ if (alpha < 255) {
3343
+ const baseSrcAlpha = color >>> 24;
3344
+ const colorRGB = color & 16777215;
3345
+ const a = baseSrcAlpha * alpha + 128 >> 8;
3346
+ finalCol = (colorRGB | a << 24) >>> 0;
3347
+ }
3348
+ for (let iy = 0; iy < actualH; iy++) {
3349
+ const currentY = finalY + iy;
3350
+ const maskY = currentY - y;
3351
+ const maskOffset = maskY * maskW;
3352
+ const dstRowOffset = currentY * dw;
3353
+ for (let ix = 0; ix < actualW; ix++) {
3354
+ const currentX = finalX + ix;
3355
+ const maskX = currentX - x;
3356
+ const maskIndex = maskOffset + maskX;
3357
+ if (maskData[maskIndex]) {
3358
+ dst32[dstRowOffset + currentX] = finalCol;
3359
+ }
3360
+ }
3361
+ }
3362
+ }
3363
+
3364
+ // src/History/PixelMutator/mutatorFillBinaryMask.ts
3365
+ var defaults16 = {
3366
+ fillPixelDataBinaryMask
3367
+ };
3368
+ var mutatorFillBinaryMask = ((writer, deps = defaults16) => {
3369
+ const {
3370
+ fillPixelDataBinaryMask: fillPixelDataBinaryMask2 = defaults16.fillPixelDataBinaryMask
3371
+ } = deps;
3372
+ return {
3373
+ fillBinaryMask(color, mask, alpha = 255, x = 0, y = 0) {
3374
+ writer.accumulator.storeRegionBeforeState(x, y, mask.w, mask.h);
3375
+ fillPixelDataBinaryMask2(writer.target, color, mask, alpha, x, y);
3340
3376
  }
3341
3377
  };
3342
3378
  });
@@ -3372,8 +3408,8 @@ function makeImageDataLike(width, height, data) {
3372
3408
  };
3373
3409
  }
3374
3410
 
3375
- // src/ImageData/imageDataToAlphaMask.ts
3376
- function imageDataToAlphaMask(imageData) {
3411
+ // src/ImageData/imageDataToAlphaMaskBuffer.ts
3412
+ function imageDataToAlphaMaskBuffer(imageData) {
3377
3413
  const {
3378
3414
  width,
3379
3415
  height,
@@ -3463,13 +3499,13 @@ function resampleImageData(source, factor) {
3463
3499
  }
3464
3500
 
3465
3501
  // src/ImageData/resizeImageData.ts
3466
- function resizeImageData(current, newWidth, newHeight, offsetX = 0, offsetY = 0) {
3502
+ function resizeImageData(target, newWidth, newHeight, offsetX = 0, offsetY = 0) {
3467
3503
  const result = new ImageData(newWidth, newHeight);
3468
3504
  const {
3469
3505
  width: oldW,
3470
3506
  height: oldH,
3471
3507
  data: oldData
3472
- } = current;
3508
+ } = target;
3473
3509
  const newData = result.data;
3474
3510
  const x0 = Math.max(0, offsetX);
3475
3511
  const y0 = Math.max(0, offsetY);
@@ -3624,7 +3660,7 @@ function writeImageData(target, source, x, y, sx = 0, sy = 0, sw = source.width,
3624
3660
 
3625
3661
  // src/ImageData/writeImageDataBuffer.ts
3626
3662
  var SCRATCH_BLIT3 = makeClippedBlit();
3627
- function writeImageDataBuffer(imageData, data, _x, _y, _w, _h) {
3663
+ function writeImageDataBuffer(target, data, _x, _y, _w, _h) {
3628
3664
  const {
3629
3665
  x,
3630
3666
  y,
@@ -3640,7 +3676,7 @@ function writeImageDataBuffer(imageData, data, _x, _y, _w, _h) {
3640
3676
  width: dstW,
3641
3677
  height: dstH,
3642
3678
  data: dst
3643
- } = imageData;
3679
+ } = target;
3644
3680
  const clip = resolveBlitClipping(x, y, 0, 0, w, h, dstW, dstH, w, h, SCRATCH_BLIT3);
3645
3681
  if (!clip.inBounds) return;
3646
3682
  const {
@@ -3886,8 +3922,84 @@ async function getSupportedPixelFormats(rasterMimes = defaultRasterMimes) {
3886
3922
  return formatsPromise;
3887
3923
  }
3888
3924
 
3925
+ // src/Mask/AlphaMask.ts
3926
+ function makeAlphaMask(w, h, data) {
3927
+ return {
3928
+ type: 0 /* ALPHA */,
3929
+ data: data ?? new Uint8Array(w * h),
3930
+ w,
3931
+ h
3932
+ };
3933
+ }
3934
+
3935
+ // src/Mask/BinaryMask.ts
3936
+ function makeBinaryMask(w, h, data) {
3937
+ return {
3938
+ type: 1 /* BINARY */,
3939
+ data: data ?? new Uint8Array(w * h),
3940
+ w,
3941
+ h
3942
+ };
3943
+ }
3944
+
3945
+ // src/Mask/CircleBrushAlphaMask.ts
3946
+ function makeCircleBrushAlphaMask(size, fallOff = () => 1) {
3947
+ const area = size * size;
3948
+ const data = new Uint8Array(area);
3949
+ const radius = size / 2;
3950
+ const invR = 1 / radius;
3951
+ const minOffset = -Math.ceil(radius - 0.5);
3952
+ for (let y = 0; y < size; y++) {
3953
+ for (let x = 0; x < size; x++) {
3954
+ const dx = x - radius + 0.5;
3955
+ const dy = y - radius + 0.5;
3956
+ const distSqr = dx * dx + dy * dy;
3957
+ if (distSqr <= radius * radius) {
3958
+ const dist = Math.sqrt(distSqr);
3959
+ data[y * size + x] = fallOff(1 - dist * invR) * 255 | 0;
3960
+ }
3961
+ }
3962
+ }
3963
+ return {
3964
+ type: 0 /* ALPHA */,
3965
+ data,
3966
+ w: size,
3967
+ h: size,
3968
+ radius,
3969
+ size,
3970
+ minOffset
3971
+ };
3972
+ }
3973
+
3974
+ // src/Mask/CircleBrushBinaryMask.ts
3975
+ function makeCircleBrushBinaryMask(size) {
3976
+ const area = size * size;
3977
+ const data = new Uint8Array(area);
3978
+ const radius = size / 2;
3979
+ const minOffset = -Math.ceil(radius - 0.5);
3980
+ for (let y = 0; y < size; y++) {
3981
+ for (let x = 0; x < size; x++) {
3982
+ const dx = x - radius + 0.5;
3983
+ const dy = y - radius + 0.5;
3984
+ const distSqr = dx * dx + dy * dy;
3985
+ if (distSqr <= radius * radius) {
3986
+ data[y * size + x] = 1;
3987
+ }
3988
+ }
3989
+ }
3990
+ return {
3991
+ type: 1 /* BINARY */,
3992
+ data,
3993
+ w: size,
3994
+ h: size,
3995
+ radius,
3996
+ size,
3997
+ minOffset
3998
+ };
3999
+ }
4000
+
3889
4001
  // src/Mask/applyBinaryMaskToAlphaMask.ts
3890
- function applyBinaryMaskToAlphaMask(alphaMaskDst, dstWidth, binaryMaskSrc, srcWidth, opts = {}) {
4002
+ function applyBinaryMaskToAlphaMask(alphaMaskDst, binaryMaskSrc, opts = {}) {
3891
4003
  const {
3892
4004
  x: targetX = 0,
3893
4005
  y: targetY = 0,
@@ -3897,11 +4009,13 @@ function applyBinaryMaskToAlphaMask(alphaMaskDst, dstWidth, binaryMaskSrc, srcWi
3897
4009
  my = 0,
3898
4010
  invertMask = false
3899
4011
  } = opts;
4012
+ const dstWidth = alphaMaskDst.w;
3900
4013
  if (dstWidth <= 0) return;
3901
- if (binaryMaskSrc.length === 0) return;
4014
+ if (binaryMaskSrc.data.length === 0) return;
4015
+ const srcWidth = binaryMaskSrc.w;
3902
4016
  if (srcWidth <= 0) return;
3903
- const dstHeight = alphaMaskDst.length / dstWidth | 0;
3904
- const srcHeight = binaryMaskSrc.length / srcWidth | 0;
4017
+ const dstHeight = alphaMaskDst.data.length / dstWidth | 0;
4018
+ const srcHeight = binaryMaskSrc.data.length / srcWidth | 0;
3905
4019
  if (dstHeight <= 0) return;
3906
4020
  if (srcHeight <= 0) return;
3907
4021
  const dstX0 = Math.max(0, targetX);
@@ -3918,6 +4032,8 @@ function applyBinaryMaskToAlphaMask(alphaMaskDst, dstWidth, binaryMaskSrc, srcWi
3918
4032
  if (srcY0 + (dstY1 - dstY0) <= 0) return;
3919
4033
  const iterW = Math.min(dstX1 - dstX0, srcWidth - srcX0);
3920
4034
  const iterH = Math.min(dstY1 - dstY0, srcHeight - srcY0);
4035
+ const srcData = binaryMaskSrc.data;
4036
+ const dstData = alphaMaskDst.data;
3921
4037
  let dstIdx = dstY0 * dstWidth + dstX0;
3922
4038
  let srcIdx = srcY0 * srcWidth + srcX0;
3923
4039
  if (invertMask) {
@@ -3926,8 +4042,8 @@ function applyBinaryMaskToAlphaMask(alphaMaskDst, dstWidth, binaryMaskSrc, srcWi
3926
4042
  let d = dstIdx;
3927
4043
  let s = srcIdx;
3928
4044
  while (d < dstEnd) {
3929
- if (binaryMaskSrc[s] !== 0) {
3930
- alphaMaskDst[d] = 0;
4045
+ if (srcData[s] !== 0) {
4046
+ dstData[d] = 0;
3931
4047
  }
3932
4048
  d++;
3933
4049
  s++;
@@ -3941,8 +4057,8 @@ function applyBinaryMaskToAlphaMask(alphaMaskDst, dstWidth, binaryMaskSrc, srcWi
3941
4057
  let d = dstIdx;
3942
4058
  let s = srcIdx;
3943
4059
  while (d < dstEnd) {
3944
- if (binaryMaskSrc[s] === 0) {
3945
- alphaMaskDst[d] = 0;
4060
+ if (srcData[s] === 0) {
4061
+ dstData[d] = 0;
3946
4062
  }
3947
4063
  d++;
3948
4064
  s++;
@@ -3955,25 +4071,72 @@ function applyBinaryMaskToAlphaMask(alphaMaskDst, dstWidth, binaryMaskSrc, srcWi
3955
4071
 
3956
4072
  // src/Mask/copyMask.ts
3957
4073
  function copyMask(src) {
3958
- return src.slice();
4074
+ return {
4075
+ type: src.type,
4076
+ data: src.data.slice(),
4077
+ w: src.w,
4078
+ h: src.h
4079
+ };
4080
+ }
4081
+
4082
+ // src/Mask/extractMask.ts
4083
+ function extractMask(mask, xOrRect, y, w, h) {
4084
+ let finalX;
4085
+ let finalY;
4086
+ let finalW;
4087
+ let finalH;
4088
+ if (typeof xOrRect === "object") {
4089
+ finalX = xOrRect.x;
4090
+ finalY = xOrRect.y;
4091
+ finalW = xOrRect.w;
4092
+ finalH = xOrRect.h;
4093
+ } else {
4094
+ finalX = xOrRect;
4095
+ finalY = y;
4096
+ finalW = w;
4097
+ finalH = h;
4098
+ }
4099
+ const out = {
4100
+ type: mask.type,
4101
+ w: finalW,
4102
+ h: finalH,
4103
+ data: new Uint8Array(finalW * finalH)
4104
+ };
4105
+ const srcH = mask.h;
4106
+ const stride = mask.w;
4107
+ for (let row = 0; row < finalH; row++) {
4108
+ const currentSrcY = finalY + row;
4109
+ if (currentSrcY < 0 || currentSrcY >= srcH) continue;
4110
+ const start = Math.max(0, finalX);
4111
+ const end = Math.min(stride, finalX + finalW);
4112
+ if (start < end) {
4113
+ const srcOffset = currentSrcY * stride + start;
4114
+ const dstOffset = row * finalW + (start - finalX);
4115
+ const count = end - start;
4116
+ out.data.set(mask.data.subarray(srcOffset, srcOffset + count), dstOffset);
4117
+ }
4118
+ }
4119
+ return out;
3959
4120
  }
3960
4121
 
3961
4122
  // src/Mask/invertMask.ts
3962
4123
  function invertBinaryMask(dst) {
3963
- const len = dst.length;
4124
+ const data = dst.data;
4125
+ const len = data.length;
3964
4126
  for (let i = 0; i < len; i++) {
3965
- dst[i] = dst[i] === 0 ? 1 : 0;
4127
+ data[i] = data[i] === 0 ? 1 : 0;
3966
4128
  }
3967
4129
  }
3968
4130
  function invertAlphaMask(dst) {
3969
- const len = dst.length;
4131
+ const data = dst.data;
4132
+ const len = data.length;
3970
4133
  for (let i = 0; i < len; i++) {
3971
- dst[i] = 255 - dst[i];
4134
+ data[i] = 255 - data[i];
3972
4135
  }
3973
4136
  }
3974
4137
 
3975
4138
  // src/Mask/mergeAlphaMasks.ts
3976
- function mergeAlphaMasks(dst, dstWidth, src, srcWidth, opts) {
4139
+ function mergeAlphaMasks(dst, src, opts) {
3977
4140
  const {
3978
4141
  x: targetX = 0,
3979
4142
  y: targetY = 0,
@@ -3984,15 +4147,17 @@ function mergeAlphaMasks(dst, dstWidth, src, srcWidth, opts) {
3984
4147
  my = 0,
3985
4148
  invertMask = false
3986
4149
  } = opts;
3987
- const dstHeight = dst.length / dstWidth | 0;
3988
- const srcHeight = src.length / srcWidth | 0;
3989
4150
  if (width <= 0) return;
3990
4151
  if (height <= 0) return;
3991
4152
  if (globalAlpha === 0) return;
4153
+ const dstData = dst.data;
4154
+ const srcData = src.data;
4155
+ const srcWidth = src.w;
4156
+ const dstWidth = dst.w;
3992
4157
  const startX = Math.max(0, -targetX, -mx);
3993
4158
  const startY = Math.max(0, -targetY, -my);
3994
4159
  const endX = Math.min(width, dstWidth - targetX, srcWidth - mx);
3995
- const endY = Math.min(height, dstHeight - targetY, srcHeight - my);
4160
+ const endY = Math.min(height, dst.h - targetY, src.h - my);
3996
4161
  if (startX >= endX) return;
3997
4162
  if (startY >= endY) return;
3998
4163
  for (let iy = startY; iy < endY; iy++) {
@@ -4001,7 +4166,7 @@ function mergeAlphaMasks(dst, dstWidth, src, srcWidth, opts) {
4001
4166
  let dIdx = dy * dstWidth + targetX + startX;
4002
4167
  let sIdx = sy * srcWidth + mx + startX;
4003
4168
  for (let ix = startX; ix < endX; ix++) {
4004
- const rawM = src[sIdx];
4169
+ const rawM = srcData[sIdx];
4005
4170
  const effectiveM = invertMask ? 255 - rawM : rawM;
4006
4171
  let weight = 0;
4007
4172
  if (effectiveM === 0) {
@@ -4015,13 +4180,13 @@ function mergeAlphaMasks(dst, dstWidth, src, srcWidth, opts) {
4015
4180
  }
4016
4181
  if (weight !== 255) {
4017
4182
  if (weight === 0) {
4018
- dst[dIdx] = 0;
4183
+ dstData[dIdx] = 0;
4019
4184
  } else {
4020
- const da = dst[dIdx];
4185
+ const da = dstData[dIdx];
4021
4186
  if (da === 255) {
4022
- dst[dIdx] = weight;
4187
+ dstData[dIdx] = weight;
4023
4188
  } else if (da !== 0) {
4024
- dst[dIdx] = da * weight + 128 >> 8;
4189
+ dstData[dIdx] = da * weight + 128 >> 8;
4025
4190
  }
4026
4191
  }
4027
4192
  }
@@ -4032,7 +4197,7 @@ function mergeAlphaMasks(dst, dstWidth, src, srcWidth, opts) {
4032
4197
  }
4033
4198
 
4034
4199
  // src/Mask/mergeBinaryMasks.ts
4035
- function mergeBinaryMasks(dst, dstWidth, src, srcWidth, opts) {
4200
+ function mergeBinaryMasks(dst, src, opts) {
4036
4201
  const {
4037
4202
  x: targetX = 0,
4038
4203
  y: targetY = 0,
@@ -4042,10 +4207,12 @@ function mergeBinaryMasks(dst, dstWidth, src, srcWidth, opts) {
4042
4207
  my = 0,
4043
4208
  invertMask = false
4044
4209
  } = opts;
4210
+ const dstData = dst.data;
4211
+ const srcData = src.data;
4212
+ const srcWidth = src.w;
4213
+ const dstWidth = dst.w;
4045
4214
  if (dstWidth <= 0) return;
4046
4215
  if (srcWidth <= 0) return;
4047
- const dstHeight = dst.length / dstWidth | 0;
4048
- const srcHeight = src.length / srcWidth | 0;
4049
4216
  let x = targetX;
4050
4217
  let y = targetY;
4051
4218
  let w = width;
@@ -4059,7 +4226,7 @@ function mergeBinaryMasks(dst, dstWidth, src, srcWidth, opts) {
4059
4226
  y = 0;
4060
4227
  }
4061
4228
  w = Math.min(w, dstWidth - x);
4062
- h = Math.min(h, dstHeight - y);
4229
+ h = Math.min(h, dst.h - y);
4063
4230
  if (w <= 0) return;
4064
4231
  if (h <= 0) return;
4065
4232
  const startX = mx + (x - targetX);
@@ -4067,7 +4234,7 @@ function mergeBinaryMasks(dst, dstWidth, src, srcWidth, opts) {
4067
4234
  const sX0 = Math.max(0, startX);
4068
4235
  const sY0 = Math.max(0, startY);
4069
4236
  const sX1 = Math.min(srcWidth, startX + w);
4070
- const sY1 = Math.min(srcHeight, startY + h);
4237
+ const sY1 = Math.min(src.h, startY + h);
4071
4238
  const finalW = sX1 - sX0;
4072
4239
  const finalH = sY1 - sY0;
4073
4240
  if (finalW <= 0) return;
@@ -4080,10 +4247,10 @@ function mergeBinaryMasks(dst, dstWidth, src, srcWidth, opts) {
4080
4247
  let sIdx = sY0 * srcWidth + sX0;
4081
4248
  for (let iy = 0; iy < finalH; iy++) {
4082
4249
  for (let ix = 0; ix < finalW; ix++) {
4083
- const mVal = src[sIdx];
4250
+ const mVal = srcData[sIdx];
4084
4251
  const isMaskedOut = invertMask ? mVal !== 0 : mVal === 0;
4085
4252
  if (isMaskedOut) {
4086
- dst[dIdx] = 0;
4253
+ dstData[dIdx] = 0;
4087
4254
  }
4088
4255
  dIdx++;
4089
4256
  sIdx++;
@@ -4093,6 +4260,177 @@ function mergeBinaryMasks(dst, dstWidth, src, srcWidth, opts) {
4093
4260
  }
4094
4261
  }
4095
4262
 
4263
+ // src/Mask/setMaskData.ts
4264
+ function setMaskData(mask, width, height, data) {
4265
+ ;
4266
+ mask.w = width;
4267
+ mask.h = height;
4268
+ mask.data = data;
4269
+ }
4270
+
4271
+ // src/MaskRect/subtractBinaryMaskRects.ts
4272
+ function subtractBinaryMaskRects(current, subtracting) {
4273
+ let result = [...current];
4274
+ for (const sub of subtracting) {
4275
+ const next = [];
4276
+ for (const r of result) {
4277
+ const ix = Math.max(r.x, sub.x);
4278
+ const iy = Math.max(r.y, sub.y);
4279
+ const ix2 = Math.min(r.x + r.w, sub.x + sub.w);
4280
+ const iy2 = Math.min(r.y + r.h, sub.y + sub.h);
4281
+ if (ix >= ix2 || iy >= iy2) {
4282
+ next.push(r);
4283
+ continue;
4284
+ }
4285
+ if (r.y < iy) pushPiece(next, r, r.x, r.y, r.w, iy - r.y);
4286
+ if (iy2 < r.y + r.h) pushPiece(next, r, r.x, iy2, r.w, r.y + r.h - iy2);
4287
+ if (r.x < ix) pushPiece(next, r, r.x, iy, ix - r.x, iy2 - iy);
4288
+ if (ix2 < r.x + r.w) pushPiece(next, r, ix2, iy, r.x + r.w - ix2, iy2 - iy);
4289
+ }
4290
+ result = next;
4291
+ }
4292
+ return result;
4293
+ }
4294
+ function pushPiece(dest, r, x, y, w, h) {
4295
+ if (r.data === null || r.data === void 0) {
4296
+ dest.push({
4297
+ x,
4298
+ y,
4299
+ w,
4300
+ h,
4301
+ data: null,
4302
+ type: null
4303
+ });
4304
+ return;
4305
+ }
4306
+ const lx = x - r.x;
4307
+ const ly = y - r.y;
4308
+ const data = new Uint8Array(w * h);
4309
+ for (let row = 0; row < h; row++) {
4310
+ data.set(r.data.subarray((ly + row) * r.w + lx, (ly + row) * r.w + lx + w), row * w);
4311
+ }
4312
+ dest.push({
4313
+ x,
4314
+ y,
4315
+ w,
4316
+ h,
4317
+ data,
4318
+ type: 1 /* BINARY */
4319
+ });
4320
+ }
4321
+
4322
+ // src/Rect/getRectsBounds.ts
4323
+ function getRectsBounds(rects) {
4324
+ if (rects.length === 1) return {
4325
+ ...rects[0]
4326
+ };
4327
+ let minX = Infinity, minY = Infinity;
4328
+ let maxX = -Infinity, maxY = -Infinity;
4329
+ for (let i = 0; i < rects.length; i++) {
4330
+ const r = rects[i];
4331
+ const x1 = r.x;
4332
+ const y1 = r.y;
4333
+ const x2 = x1 + r.w;
4334
+ const y2 = y1 + r.h;
4335
+ if (x1 < minX) minX = x1;
4336
+ if (y1 < minY) minY = y1;
4337
+ if (x2 > maxX) maxX = x2;
4338
+ if (y2 > maxY) maxY = y2;
4339
+ }
4340
+ return {
4341
+ x: minX,
4342
+ y: minY,
4343
+ w: maxX - minX,
4344
+ h: maxY - minY
4345
+ };
4346
+ }
4347
+
4348
+ // src/MaskRect/merge2BinaryMaskRects.ts
4349
+ function merge2BinaryMaskRects(a, b) {
4350
+ const bounds = getRectsBounds([a, b]);
4351
+ if ((a.data === null || a.data === void 0) && (b.data === null || b.data === void 0)) {
4352
+ const ix = Math.max(a.x, b.x);
4353
+ const iy = Math.max(a.y, b.y);
4354
+ const ir = Math.min(a.x + a.w, b.x + b.w);
4355
+ const ib = Math.min(a.y + a.h, b.y + b.h);
4356
+ const iw = Math.max(0, ir - ix);
4357
+ const ih = Math.max(0, ib - iy);
4358
+ const intersectionArea = iw * ih;
4359
+ const areaA = a.w * a.h;
4360
+ const areaB = b.w * b.h;
4361
+ const boundsArea = bounds.w * bounds.h;
4362
+ if (boundsArea === areaA + areaB - intersectionArea) {
4363
+ return {
4364
+ ...bounds,
4365
+ data: null,
4366
+ type: null
4367
+ };
4368
+ }
4369
+ }
4370
+ const maskData = new Uint8Array(bounds.w * bounds.h);
4371
+ const aOffY = a.y - bounds.y;
4372
+ const aOffX = a.x - bounds.x;
4373
+ if (a.data === void 0 || a.data === null) {
4374
+ for (let ay = 0; ay < a.h; ay++) {
4375
+ const destRow = (aOffY + ay) * bounds.w + aOffX;
4376
+ maskData.fill(1, destRow, destRow + a.w);
4377
+ }
4378
+ } else {
4379
+ for (let ay = 0; ay < a.h; ay++) {
4380
+ const srcRow = ay * a.w;
4381
+ const destRow = (aOffY + ay) * bounds.w + aOffX;
4382
+ maskData.set(a.data.subarray(srcRow, srcRow + a.w), destRow);
4383
+ }
4384
+ }
4385
+ const bOffY = b.y - bounds.y;
4386
+ const bOffX = b.x - bounds.x;
4387
+ if (b.data === void 0 || b.data === null) {
4388
+ for (let by = 0; by < b.h; by++) {
4389
+ const destRow = (bOffY + by) * bounds.w + bOffX;
4390
+ maskData.fill(1, destRow, destRow + b.w);
4391
+ }
4392
+ } else {
4393
+ for (let by = 0; by < b.h; by++) {
4394
+ const srcRow = by * b.w;
4395
+ const destRow = (bOffY + by) * bounds.w + bOffX;
4396
+ for (let bx = 0; bx < b.w; bx++) {
4397
+ maskData[destRow + bx] |= b.data[srcRow + bx];
4398
+ }
4399
+ }
4400
+ }
4401
+ return {
4402
+ ...bounds,
4403
+ data: maskData,
4404
+ type: 1 /* BINARY */
4405
+ };
4406
+ }
4407
+
4408
+ // src/MaskRect/mergeBinaryMaskRects.ts
4409
+ function mergeBinaryMaskRects(current, adding) {
4410
+ const rects = [...current, ...adding];
4411
+ let changed = true;
4412
+ while (changed) {
4413
+ changed = false;
4414
+ const next = [];
4415
+ for (const r of rects) {
4416
+ let merged = false;
4417
+ for (let i = 0; i < next.length; i++) {
4418
+ const n = next[i];
4419
+ const overlap = r.x <= n.x + n.w && r.x + r.w >= n.x && r.y <= n.y + n.h && r.y + r.h >= n.y;
4420
+ if (overlap) {
4421
+ next[i] = merge2BinaryMaskRects(n, r);
4422
+ merged = true;
4423
+ changed = true;
4424
+ break;
4425
+ }
4426
+ }
4427
+ if (!merged) next.push(r);
4428
+ }
4429
+ rects.splice(0, rects.length, ...next);
4430
+ }
4431
+ return rects;
4432
+ }
4433
+
4096
4434
  // src/PixelData/PixelData.ts
4097
4435
  var PixelData = class _PixelData {
4098
4436
  data32;
@@ -4144,7 +4482,6 @@ function blendPixelDataAlphaMask(dst, src, alphaMask, opts = {}) {
4144
4482
  h: height = src.height,
4145
4483
  alpha: globalAlpha = 255,
4146
4484
  blendFn = sourceOverPerfect,
4147
- mw = src.width,
4148
4485
  mx = 0,
4149
4486
  my = 0,
4150
4487
  invertMask = false
@@ -4183,7 +4520,8 @@ function blendPixelDataAlphaMask(dst, src, alphaMask, opts = {}) {
4183
4520
  if (actualW <= 0 || actualH <= 0) return;
4184
4521
  const dw = dst.width;
4185
4522
  const sw = src.width;
4186
- const mPitch = mw;
4523
+ const mPitch = alphaMask.w;
4524
+ const maskData = alphaMask.data;
4187
4525
  const dx = x - targetX | 0;
4188
4526
  const dy = y - targetY | 0;
4189
4527
  const dst32 = dst.data32;
@@ -4198,7 +4536,7 @@ function blendPixelDataAlphaMask(dst, src, alphaMask, opts = {}) {
4198
4536
  const isOverwrite = blendFn.isOverwrite || false;
4199
4537
  for (let iy = 0; iy < actualH; iy++) {
4200
4538
  for (let ix = 0; ix < actualW; ix++) {
4201
- const mVal = alphaMask[mIdx];
4539
+ const mVal = maskData[mIdx];
4202
4540
  const effM = invertMask ? 255 - mVal : mVal;
4203
4541
  if (effM === 0) {
4204
4542
  dIdx++;
@@ -4259,7 +4597,6 @@ function blendPixelDataBinaryMask(dst, src, binaryMask, opts) {
4259
4597
  h: height = src.height,
4260
4598
  alpha: globalAlpha = 255,
4261
4599
  blendFn = sourceOverPerfect,
4262
- mw = src.width,
4263
4600
  mx = 0,
4264
4601
  my = 0,
4265
4602
  invertMask = false
@@ -4302,7 +4639,8 @@ function blendPixelDataBinaryMask(dst, src, binaryMask, opts) {
4302
4639
  const src32 = src.data32;
4303
4640
  const dw = dst.width;
4304
4641
  const sw = src.width;
4305
- const mPitch = mw;
4642
+ const mPitch = binaryMask.w;
4643
+ const maskData = binaryMask.data;
4306
4644
  let dIdx = y * dw + x | 0;
4307
4645
  let sIdx = sy * sw + sx | 0;
4308
4646
  let mIdx = (my + dy) * mPitch + (mx + dx) | 0;
@@ -4314,7 +4652,7 @@ function blendPixelDataBinaryMask(dst, src, binaryMask, opts) {
4314
4652
  const isOverwrite = blendFn.isOverwrite || false;
4315
4653
  for (let iy = 0; iy < actualH; iy++) {
4316
4654
  for (let ix = 0; ix < actualW; ix++) {
4317
- if (binaryMask[mIdx] === skipVal) {
4655
+ if (maskData[mIdx] === skipVal) {
4318
4656
  dIdx++;
4319
4657
  sIdx++;
4320
4658
  mIdx++;
@@ -4442,10 +4780,11 @@ function pixelDataToAlphaMask(pixelData) {
4442
4780
  height
4443
4781
  } = pixelData;
4444
4782
  const len = data32.length;
4445
- const mask = new Uint8Array(width * height);
4783
+ const mask = makeAlphaMask(width, height);
4784
+ const maskData = mask.data;
4446
4785
  for (let i = 0; i < len; i++) {
4447
4786
  const val = data32[i];
4448
- mask[i] = val >>> 24 & 255;
4787
+ maskData[i] = val >>> 24 & 255;
4449
4788
  }
4450
4789
  return mask;
4451
4790
  }
@@ -4626,11 +4965,13 @@ export {
4626
4965
  exclusionPerfect,
4627
4966
  extractImageDataBuffer,
4628
4967
  extractMask,
4968
+ extractMaskBuffer,
4629
4969
  extractPixelData,
4630
4970
  extractPixelDataBuffer,
4631
4971
  fileInputChangeToImageData,
4632
4972
  fileToImageData,
4633
4973
  fillPixelData,
4974
+ fillPixelDataBinaryMask,
4634
4975
  floodFillSelection,
4635
4976
  forEachLinePoint,
4636
4977
  getCircleBrushOrPencilBounds,
@@ -4639,12 +4980,13 @@ export {
4639
4980
  getIndexedImageColorCounts,
4640
4981
  getRectBrushOrPencilBounds,
4641
4982
  getRectBrushOrPencilStrokeBounds,
4983
+ getRectsBounds,
4642
4984
  getSupportedPixelFormats,
4643
4985
  hardLightFast,
4644
4986
  hardLightPerfect,
4645
4987
  hardMixFast,
4646
4988
  hardMixPerfect,
4647
- imageDataToAlphaMask,
4989
+ imageDataToAlphaMaskBuffer,
4648
4990
  imageDataToDataUrl,
4649
4991
  imageDataToImgBlob,
4650
4992
  imageDataToUInt32Array,
@@ -4667,7 +5009,11 @@ export {
4667
5009
  linearDodgePerfect,
4668
5010
  linearLightFast,
4669
5011
  linearLightPerfect,
5012
+ makeAlphaMask,
5013
+ makeBinaryMask,
4670
5014
  makeBlendModeRegistry,
5015
+ makeCircleBrushAlphaMask,
5016
+ makeCircleBrushBinaryMask,
4671
5017
  makeFastBlendModeRegistry,
4672
5018
  makeFullPixelMutator,
4673
5019
  makeImageDataLike,
@@ -4675,7 +5021,9 @@ export {
4675
5021
  makePixelCanvas,
4676
5022
  makeReusableCanvas,
4677
5023
  makeReusableImageData,
5024
+ merge2BinaryMaskRects,
4678
5025
  mergeAlphaMasks,
5026
+ mergeBinaryMaskRects,
4679
5027
  mergeBinaryMasks,
4680
5028
  multiplyFast,
4681
5029
  multiplyPerfect,
@@ -4694,6 +5042,7 @@ export {
4694
5042
  mutatorBlendPixelData,
4695
5043
  mutatorClear,
4696
5044
  mutatorFill,
5045
+ mutatorFillBinaryMask,
4697
5046
  mutatorInvert,
4698
5047
  overlayFast,
4699
5048
  overlayPerfect,
@@ -4716,10 +5065,12 @@ export {
4716
5065
  screenPerfect,
4717
5066
  serializeImageData,
4718
5067
  serializeNullableImageData,
5068
+ setMaskData,
4719
5069
  softLightFast,
4720
5070
  softLightPerfect,
4721
5071
  sourceOverFast,
4722
5072
  sourceOverPerfect,
5073
+ subtractBinaryMaskRects,
4723
5074
  subtractFast,
4724
5075
  subtractPerfect,
4725
5076
  toBlendModeIndexAndName,