pixel-data-js 0.20.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 (65) hide show
  1. package/dist/index.dev.cjs +722 -357
  2. package/dist/index.dev.cjs.map +1 -1
  3. package/dist/index.dev.js +709 -356
  4. package/dist/index.dev.js.map +1 -1
  5. package/dist/index.prod.cjs +722 -357
  6. package/dist/index.prod.cjs.map +1 -1
  7. package/dist/index.prod.d.ts +283 -128
  8. package/dist/index.prod.js +709 -356
  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 +11 -8
  27. package/src/History/PixelMutator/mutatorFill.ts +4 -0
  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/clearPixelData.ts +2 -2
  57. package/src/PixelData/fillPixelData.ts +15 -42
  58. package/src/PixelData/fillPixelDataBinaryMask.ts +79 -0
  59. package/src/PixelData/invertPixelData.ts +3 -3
  60. package/src/PixelData/pixelDataToAlphaMask.ts +4 -2
  61. package/src/PixelData/writePixelDataBuffer.ts +2 -3
  62. package/src/Rect/getRectsBounds.ts +22 -0
  63. package/src/Rect/trimRectBounds.ts +20 -17
  64. package/src/_types.ts +55 -29
  65. 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,12 +3115,10 @@ 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
- } = 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;
3138
3122
  writer.accumulator.storeRegionBeforeState(x, y, w, h);
3139
3123
  fillPixelData2(writer.target, 0, x, y, w, h);
3140
3124
  }
@@ -3173,7 +3157,6 @@ function invertPixelData(pixelData, opts = {}) {
3173
3157
  w: width = pixelData.width,
3174
3158
  h: height = pixelData.height,
3175
3159
  mask,
3176
- mw,
3177
3160
  mx = 0,
3178
3161
  my = 0,
3179
3162
  invertMask = false
@@ -3188,7 +3171,7 @@ function invertPixelData(pixelData, opts = {}) {
3188
3171
  } = clip;
3189
3172
  const dst32 = dst.data32;
3190
3173
  const dw = dst.width;
3191
- const mPitch = mw ?? width;
3174
+ const mPitch = mask?.w ?? width;
3192
3175
  const dx = x - targetX;
3193
3176
  const dy = y - targetY;
3194
3177
  let dIdx = y * dw + x;
@@ -3196,9 +3179,10 @@ function invertPixelData(pixelData, opts = {}) {
3196
3179
  const dStride = dw - actualW;
3197
3180
  const mStride = mPitch - actualW;
3198
3181
  if (mask) {
3182
+ const maskData = mask.data;
3199
3183
  for (let iy = 0; iy < actualH; iy++) {
3200
3184
  for (let ix = 0; ix < actualW; ix++) {
3201
- const mVal = mask[mIdx];
3185
+ const mVal = maskData[mIdx];
3202
3186
  const isHit = invertMask ? mVal === 0 : mVal === 1;
3203
3187
  if (isHit) {
3204
3188
  dst32[dIdx] = dst32[dIdx] ^ 16777215;
@@ -3309,14 +3293,12 @@ var PixelWriter = class {
3309
3293
  // src/History/PixelMutator/mutatorApplyCirclePencil.ts
3310
3294
  var defaults15 = {
3311
3295
  applyCircleBrushToPixelData,
3312
- getCircleBrushOrPencilBounds,
3313
- fallOff: () => 1
3296
+ getCircleBrushOrPencilBounds
3314
3297
  };
3315
3298
  var mutatorApplyCirclePencil = ((writer, deps = defaults15) => {
3316
3299
  const {
3317
3300
  applyCircleBrushToPixelData: applyCircleBrushToPixelData2 = defaults15.applyCircleBrushToPixelData,
3318
- getCircleBrushOrPencilBounds: getCircleBrushOrPencilBounds2 = defaults15.getCircleBrushOrPencilBounds,
3319
- fallOff = defaults15.fallOff
3301
+ getCircleBrushOrPencilBounds: getCircleBrushOrPencilBounds2 = defaults15.getCircleBrushOrPencilBounds
3320
3302
  } = deps;
3321
3303
  const boundsOut = {
3322
3304
  x: 0,
@@ -3325,8 +3307,8 @@ var mutatorApplyCirclePencil = ((writer, deps = defaults15) => {
3325
3307
  h: 0
3326
3308
  };
3327
3309
  return {
3328
- applyCirclePencil(color, centerX, centerY, brushSize, alpha = 255, blendFn) {
3329
- 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);
3330
3312
  const {
3331
3313
  x,
3332
3314
  y,
@@ -3334,7 +3316,63 @@ var mutatorApplyCirclePencil = ((writer, deps = defaults15) => {
3334
3316
  h
3335
3317
  } = bounds;
3336
3318
  writer.accumulator.storeRegionBeforeState(x, y, w, h);
3337
- 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);
3338
3376
  }
3339
3377
  };
3340
3378
  });
@@ -3370,8 +3408,8 @@ function makeImageDataLike(width, height, data) {
3370
3408
  };
3371
3409
  }
3372
3410
 
3373
- // src/ImageData/imageDataToAlphaMask.ts
3374
- function imageDataToAlphaMask(imageData) {
3411
+ // src/ImageData/imageDataToAlphaMaskBuffer.ts
3412
+ function imageDataToAlphaMaskBuffer(imageData) {
3375
3413
  const {
3376
3414
  width,
3377
3415
  height,
@@ -3461,13 +3499,13 @@ function resampleImageData(source, factor) {
3461
3499
  }
3462
3500
 
3463
3501
  // src/ImageData/resizeImageData.ts
3464
- function resizeImageData(current, newWidth, newHeight, offsetX = 0, offsetY = 0) {
3502
+ function resizeImageData(target, newWidth, newHeight, offsetX = 0, offsetY = 0) {
3465
3503
  const result = new ImageData(newWidth, newHeight);
3466
3504
  const {
3467
3505
  width: oldW,
3468
3506
  height: oldH,
3469
3507
  data: oldData
3470
- } = current;
3508
+ } = target;
3471
3509
  const newData = result.data;
3472
3510
  const x0 = Math.max(0, offsetX);
3473
3511
  const y0 = Math.max(0, offsetY);
@@ -3622,7 +3660,7 @@ function writeImageData(target, source, x, y, sx = 0, sy = 0, sw = source.width,
3622
3660
 
3623
3661
  // src/ImageData/writeImageDataBuffer.ts
3624
3662
  var SCRATCH_BLIT3 = makeClippedBlit();
3625
- function writeImageDataBuffer(imageData, data, _x, _y, _w, _h) {
3663
+ function writeImageDataBuffer(target, data, _x, _y, _w, _h) {
3626
3664
  const {
3627
3665
  x,
3628
3666
  y,
@@ -3638,7 +3676,7 @@ function writeImageDataBuffer(imageData, data, _x, _y, _w, _h) {
3638
3676
  width: dstW,
3639
3677
  height: dstH,
3640
3678
  data: dst
3641
- } = imageData;
3679
+ } = target;
3642
3680
  const clip = resolveBlitClipping(x, y, 0, 0, w, h, dstW, dstH, w, h, SCRATCH_BLIT3);
3643
3681
  if (!clip.inBounds) return;
3644
3682
  const {
@@ -3884,8 +3922,84 @@ async function getSupportedPixelFormats(rasterMimes = defaultRasterMimes) {
3884
3922
  return formatsPromise;
3885
3923
  }
3886
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
+
3887
4001
  // src/Mask/applyBinaryMaskToAlphaMask.ts
3888
- function applyBinaryMaskToAlphaMask(alphaMaskDst, dstWidth, binaryMaskSrc, srcWidth, opts = {}) {
4002
+ function applyBinaryMaskToAlphaMask(alphaMaskDst, binaryMaskSrc, opts = {}) {
3889
4003
  const {
3890
4004
  x: targetX = 0,
3891
4005
  y: targetY = 0,
@@ -3895,11 +4009,13 @@ function applyBinaryMaskToAlphaMask(alphaMaskDst, dstWidth, binaryMaskSrc, srcWi
3895
4009
  my = 0,
3896
4010
  invertMask = false
3897
4011
  } = opts;
4012
+ const dstWidth = alphaMaskDst.w;
3898
4013
  if (dstWidth <= 0) return;
3899
- if (binaryMaskSrc.length === 0) return;
4014
+ if (binaryMaskSrc.data.length === 0) return;
4015
+ const srcWidth = binaryMaskSrc.w;
3900
4016
  if (srcWidth <= 0) return;
3901
- const dstHeight = alphaMaskDst.length / dstWidth | 0;
3902
- const srcHeight = binaryMaskSrc.length / srcWidth | 0;
4017
+ const dstHeight = alphaMaskDst.data.length / dstWidth | 0;
4018
+ const srcHeight = binaryMaskSrc.data.length / srcWidth | 0;
3903
4019
  if (dstHeight <= 0) return;
3904
4020
  if (srcHeight <= 0) return;
3905
4021
  const dstX0 = Math.max(0, targetX);
@@ -3916,6 +4032,8 @@ function applyBinaryMaskToAlphaMask(alphaMaskDst, dstWidth, binaryMaskSrc, srcWi
3916
4032
  if (srcY0 + (dstY1 - dstY0) <= 0) return;
3917
4033
  const iterW = Math.min(dstX1 - dstX0, srcWidth - srcX0);
3918
4034
  const iterH = Math.min(dstY1 - dstY0, srcHeight - srcY0);
4035
+ const srcData = binaryMaskSrc.data;
4036
+ const dstData = alphaMaskDst.data;
3919
4037
  let dstIdx = dstY0 * dstWidth + dstX0;
3920
4038
  let srcIdx = srcY0 * srcWidth + srcX0;
3921
4039
  if (invertMask) {
@@ -3924,8 +4042,8 @@ function applyBinaryMaskToAlphaMask(alphaMaskDst, dstWidth, binaryMaskSrc, srcWi
3924
4042
  let d = dstIdx;
3925
4043
  let s = srcIdx;
3926
4044
  while (d < dstEnd) {
3927
- if (binaryMaskSrc[s] !== 0) {
3928
- alphaMaskDst[d] = 0;
4045
+ if (srcData[s] !== 0) {
4046
+ dstData[d] = 0;
3929
4047
  }
3930
4048
  d++;
3931
4049
  s++;
@@ -3939,8 +4057,8 @@ function applyBinaryMaskToAlphaMask(alphaMaskDst, dstWidth, binaryMaskSrc, srcWi
3939
4057
  let d = dstIdx;
3940
4058
  let s = srcIdx;
3941
4059
  while (d < dstEnd) {
3942
- if (binaryMaskSrc[s] === 0) {
3943
- alphaMaskDst[d] = 0;
4060
+ if (srcData[s] === 0) {
4061
+ dstData[d] = 0;
3944
4062
  }
3945
4063
  d++;
3946
4064
  s++;
@@ -3953,25 +4071,72 @@ function applyBinaryMaskToAlphaMask(alphaMaskDst, dstWidth, binaryMaskSrc, srcWi
3953
4071
 
3954
4072
  // src/Mask/copyMask.ts
3955
4073
  function copyMask(src) {
3956
- 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;
3957
4120
  }
3958
4121
 
3959
4122
  // src/Mask/invertMask.ts
3960
4123
  function invertBinaryMask(dst) {
3961
- const len = dst.length;
4124
+ const data = dst.data;
4125
+ const len = data.length;
3962
4126
  for (let i = 0; i < len; i++) {
3963
- dst[i] = dst[i] === 0 ? 1 : 0;
4127
+ data[i] = data[i] === 0 ? 1 : 0;
3964
4128
  }
3965
4129
  }
3966
4130
  function invertAlphaMask(dst) {
3967
- const len = dst.length;
4131
+ const data = dst.data;
4132
+ const len = data.length;
3968
4133
  for (let i = 0; i < len; i++) {
3969
- dst[i] = 255 - dst[i];
4134
+ data[i] = 255 - data[i];
3970
4135
  }
3971
4136
  }
3972
4137
 
3973
4138
  // src/Mask/mergeAlphaMasks.ts
3974
- function mergeAlphaMasks(dst, dstWidth, src, srcWidth, opts) {
4139
+ function mergeAlphaMasks(dst, src, opts) {
3975
4140
  const {
3976
4141
  x: targetX = 0,
3977
4142
  y: targetY = 0,
@@ -3982,15 +4147,17 @@ function mergeAlphaMasks(dst, dstWidth, src, srcWidth, opts) {
3982
4147
  my = 0,
3983
4148
  invertMask = false
3984
4149
  } = opts;
3985
- const dstHeight = dst.length / dstWidth | 0;
3986
- const srcHeight = src.length / srcWidth | 0;
3987
4150
  if (width <= 0) return;
3988
4151
  if (height <= 0) return;
3989
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;
3990
4157
  const startX = Math.max(0, -targetX, -mx);
3991
4158
  const startY = Math.max(0, -targetY, -my);
3992
4159
  const endX = Math.min(width, dstWidth - targetX, srcWidth - mx);
3993
- const endY = Math.min(height, dstHeight - targetY, srcHeight - my);
4160
+ const endY = Math.min(height, dst.h - targetY, src.h - my);
3994
4161
  if (startX >= endX) return;
3995
4162
  if (startY >= endY) return;
3996
4163
  for (let iy = startY; iy < endY; iy++) {
@@ -3999,7 +4166,7 @@ function mergeAlphaMasks(dst, dstWidth, src, srcWidth, opts) {
3999
4166
  let dIdx = dy * dstWidth + targetX + startX;
4000
4167
  let sIdx = sy * srcWidth + mx + startX;
4001
4168
  for (let ix = startX; ix < endX; ix++) {
4002
- const rawM = src[sIdx];
4169
+ const rawM = srcData[sIdx];
4003
4170
  const effectiveM = invertMask ? 255 - rawM : rawM;
4004
4171
  let weight = 0;
4005
4172
  if (effectiveM === 0) {
@@ -4013,13 +4180,13 @@ function mergeAlphaMasks(dst, dstWidth, src, srcWidth, opts) {
4013
4180
  }
4014
4181
  if (weight !== 255) {
4015
4182
  if (weight === 0) {
4016
- dst[dIdx] = 0;
4183
+ dstData[dIdx] = 0;
4017
4184
  } else {
4018
- const da = dst[dIdx];
4185
+ const da = dstData[dIdx];
4019
4186
  if (da === 255) {
4020
- dst[dIdx] = weight;
4187
+ dstData[dIdx] = weight;
4021
4188
  } else if (da !== 0) {
4022
- dst[dIdx] = da * weight + 128 >> 8;
4189
+ dstData[dIdx] = da * weight + 128 >> 8;
4023
4190
  }
4024
4191
  }
4025
4192
  }
@@ -4030,7 +4197,7 @@ function mergeAlphaMasks(dst, dstWidth, src, srcWidth, opts) {
4030
4197
  }
4031
4198
 
4032
4199
  // src/Mask/mergeBinaryMasks.ts
4033
- function mergeBinaryMasks(dst, dstWidth, src, srcWidth, opts) {
4200
+ function mergeBinaryMasks(dst, src, opts) {
4034
4201
  const {
4035
4202
  x: targetX = 0,
4036
4203
  y: targetY = 0,
@@ -4040,10 +4207,12 @@ function mergeBinaryMasks(dst, dstWidth, src, srcWidth, opts) {
4040
4207
  my = 0,
4041
4208
  invertMask = false
4042
4209
  } = opts;
4210
+ const dstData = dst.data;
4211
+ const srcData = src.data;
4212
+ const srcWidth = src.w;
4213
+ const dstWidth = dst.w;
4043
4214
  if (dstWidth <= 0) return;
4044
4215
  if (srcWidth <= 0) return;
4045
- const dstHeight = dst.length / dstWidth | 0;
4046
- const srcHeight = src.length / srcWidth | 0;
4047
4216
  let x = targetX;
4048
4217
  let y = targetY;
4049
4218
  let w = width;
@@ -4057,7 +4226,7 @@ function mergeBinaryMasks(dst, dstWidth, src, srcWidth, opts) {
4057
4226
  y = 0;
4058
4227
  }
4059
4228
  w = Math.min(w, dstWidth - x);
4060
- h = Math.min(h, dstHeight - y);
4229
+ h = Math.min(h, dst.h - y);
4061
4230
  if (w <= 0) return;
4062
4231
  if (h <= 0) return;
4063
4232
  const startX = mx + (x - targetX);
@@ -4065,7 +4234,7 @@ function mergeBinaryMasks(dst, dstWidth, src, srcWidth, opts) {
4065
4234
  const sX0 = Math.max(0, startX);
4066
4235
  const sY0 = Math.max(0, startY);
4067
4236
  const sX1 = Math.min(srcWidth, startX + w);
4068
- const sY1 = Math.min(srcHeight, startY + h);
4237
+ const sY1 = Math.min(src.h, startY + h);
4069
4238
  const finalW = sX1 - sX0;
4070
4239
  const finalH = sY1 - sY0;
4071
4240
  if (finalW <= 0) return;
@@ -4078,10 +4247,10 @@ function mergeBinaryMasks(dst, dstWidth, src, srcWidth, opts) {
4078
4247
  let sIdx = sY0 * srcWidth + sX0;
4079
4248
  for (let iy = 0; iy < finalH; iy++) {
4080
4249
  for (let ix = 0; ix < finalW; ix++) {
4081
- const mVal = src[sIdx];
4250
+ const mVal = srcData[sIdx];
4082
4251
  const isMaskedOut = invertMask ? mVal !== 0 : mVal === 0;
4083
4252
  if (isMaskedOut) {
4084
- dst[dIdx] = 0;
4253
+ dstData[dIdx] = 0;
4085
4254
  }
4086
4255
  dIdx++;
4087
4256
  sIdx++;
@@ -4091,6 +4260,177 @@ function mergeBinaryMasks(dst, dstWidth, src, srcWidth, opts) {
4091
4260
  }
4092
4261
  }
4093
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
+
4094
4434
  // src/PixelData/PixelData.ts
4095
4435
  var PixelData = class _PixelData {
4096
4436
  data32;
@@ -4142,7 +4482,6 @@ function blendPixelDataAlphaMask(dst, src, alphaMask, opts = {}) {
4142
4482
  h: height = src.height,
4143
4483
  alpha: globalAlpha = 255,
4144
4484
  blendFn = sourceOverPerfect,
4145
- mw = src.width,
4146
4485
  mx = 0,
4147
4486
  my = 0,
4148
4487
  invertMask = false
@@ -4181,7 +4520,8 @@ function blendPixelDataAlphaMask(dst, src, alphaMask, opts = {}) {
4181
4520
  if (actualW <= 0 || actualH <= 0) return;
4182
4521
  const dw = dst.width;
4183
4522
  const sw = src.width;
4184
- const mPitch = mw;
4523
+ const mPitch = alphaMask.w;
4524
+ const maskData = alphaMask.data;
4185
4525
  const dx = x - targetX | 0;
4186
4526
  const dy = y - targetY | 0;
4187
4527
  const dst32 = dst.data32;
@@ -4196,7 +4536,7 @@ function blendPixelDataAlphaMask(dst, src, alphaMask, opts = {}) {
4196
4536
  const isOverwrite = blendFn.isOverwrite || false;
4197
4537
  for (let iy = 0; iy < actualH; iy++) {
4198
4538
  for (let ix = 0; ix < actualW; ix++) {
4199
- const mVal = alphaMask[mIdx];
4539
+ const mVal = maskData[mIdx];
4200
4540
  const effM = invertMask ? 255 - mVal : mVal;
4201
4541
  if (effM === 0) {
4202
4542
  dIdx++;
@@ -4257,7 +4597,6 @@ function blendPixelDataBinaryMask(dst, src, binaryMask, opts) {
4257
4597
  h: height = src.height,
4258
4598
  alpha: globalAlpha = 255,
4259
4599
  blendFn = sourceOverPerfect,
4260
- mw = src.width,
4261
4600
  mx = 0,
4262
4601
  my = 0,
4263
4602
  invertMask = false
@@ -4300,7 +4639,8 @@ function blendPixelDataBinaryMask(dst, src, binaryMask, opts) {
4300
4639
  const src32 = src.data32;
4301
4640
  const dw = dst.width;
4302
4641
  const sw = src.width;
4303
- const mPitch = mw;
4642
+ const mPitch = binaryMask.w;
4643
+ const maskData = binaryMask.data;
4304
4644
  let dIdx = y * dw + x | 0;
4305
4645
  let sIdx = sy * sw + sx | 0;
4306
4646
  let mIdx = (my + dy) * mPitch + (mx + dx) | 0;
@@ -4312,7 +4652,7 @@ function blendPixelDataBinaryMask(dst, src, binaryMask, opts) {
4312
4652
  const isOverwrite = blendFn.isOverwrite || false;
4313
4653
  for (let iy = 0; iy < actualH; iy++) {
4314
4654
  for (let ix = 0; ix < actualW; ix++) {
4315
- if (binaryMask[mIdx] === skipVal) {
4655
+ if (maskData[mIdx] === skipVal) {
4316
4656
  dIdx++;
4317
4657
  sIdx++;
4318
4658
  mIdx++;
@@ -4440,10 +4780,11 @@ function pixelDataToAlphaMask(pixelData) {
4440
4780
  height
4441
4781
  } = pixelData;
4442
4782
  const len = data32.length;
4443
- const mask = new Uint8Array(width * height);
4783
+ const mask = makeAlphaMask(width, height);
4784
+ const maskData = mask.data;
4444
4785
  for (let i = 0; i < len; i++) {
4445
4786
  const val = data32[i];
4446
- mask[i] = val >>> 24 & 255;
4787
+ maskData[i] = val >>> 24 & 255;
4447
4788
  }
4448
4789
  return mask;
4449
4790
  }
@@ -4624,11 +4965,13 @@ export {
4624
4965
  exclusionPerfect,
4625
4966
  extractImageDataBuffer,
4626
4967
  extractMask,
4968
+ extractMaskBuffer,
4627
4969
  extractPixelData,
4628
4970
  extractPixelDataBuffer,
4629
4971
  fileInputChangeToImageData,
4630
4972
  fileToImageData,
4631
4973
  fillPixelData,
4974
+ fillPixelDataBinaryMask,
4632
4975
  floodFillSelection,
4633
4976
  forEachLinePoint,
4634
4977
  getCircleBrushOrPencilBounds,
@@ -4637,12 +4980,13 @@ export {
4637
4980
  getIndexedImageColorCounts,
4638
4981
  getRectBrushOrPencilBounds,
4639
4982
  getRectBrushOrPencilStrokeBounds,
4983
+ getRectsBounds,
4640
4984
  getSupportedPixelFormats,
4641
4985
  hardLightFast,
4642
4986
  hardLightPerfect,
4643
4987
  hardMixFast,
4644
4988
  hardMixPerfect,
4645
- imageDataToAlphaMask,
4989
+ imageDataToAlphaMaskBuffer,
4646
4990
  imageDataToDataUrl,
4647
4991
  imageDataToImgBlob,
4648
4992
  imageDataToUInt32Array,
@@ -4665,7 +5009,11 @@ export {
4665
5009
  linearDodgePerfect,
4666
5010
  linearLightFast,
4667
5011
  linearLightPerfect,
5012
+ makeAlphaMask,
5013
+ makeBinaryMask,
4668
5014
  makeBlendModeRegistry,
5015
+ makeCircleBrushAlphaMask,
5016
+ makeCircleBrushBinaryMask,
4669
5017
  makeFastBlendModeRegistry,
4670
5018
  makeFullPixelMutator,
4671
5019
  makeImageDataLike,
@@ -4673,7 +5021,9 @@ export {
4673
5021
  makePixelCanvas,
4674
5022
  makeReusableCanvas,
4675
5023
  makeReusableImageData,
5024
+ merge2BinaryMaskRects,
4676
5025
  mergeAlphaMasks,
5026
+ mergeBinaryMaskRects,
4677
5027
  mergeBinaryMasks,
4678
5028
  multiplyFast,
4679
5029
  multiplyPerfect,
@@ -4692,6 +5042,7 @@ export {
4692
5042
  mutatorBlendPixelData,
4693
5043
  mutatorClear,
4694
5044
  mutatorFill,
5045
+ mutatorFillBinaryMask,
4695
5046
  mutatorInvert,
4696
5047
  overlayFast,
4697
5048
  overlayPerfect,
@@ -4714,10 +5065,12 @@ export {
4714
5065
  screenPerfect,
4715
5066
  serializeImageData,
4716
5067
  serializeNullableImageData,
5068
+ setMaskData,
4717
5069
  softLightFast,
4718
5070
  softLightPerfect,
4719
5071
  sourceOverFast,
4720
5072
  sourceOverPerfect,
5073
+ subtractBinaryMaskRects,
4721
5074
  subtractFast,
4722
5075
  subtractPerfect,
4723
5076
  toBlendModeIndexAndName,