pixel-data-js 0.21.0 → 0.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/dist/index.dev.cjs +1129 -717
  2. package/dist/index.dev.cjs.map +1 -1
  3. package/dist/index.dev.js +1114 -716
  4. package/dist/index.dev.js.map +1 -1
  5. package/dist/index.prod.cjs +1129 -717
  6. package/dist/index.prod.cjs.map +1 -1
  7. package/dist/index.prod.d.ts +317 -136
  8. package/dist/index.prod.js +1114 -716
  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 +5 -1
  26. package/src/History/PixelMutator/mutatorBlendPixelDataAlphaMask.ts +33 -0
  27. package/src/History/PixelMutator/mutatorBlendPixelDataBinaryMask.ts +33 -0
  28. package/src/History/PixelMutator/mutatorClear.ts +12 -10
  29. package/src/History/PixelMutator/mutatorFill.ts +7 -4
  30. package/src/History/PixelMutator/mutatorFillBinaryMask.ts +28 -0
  31. package/src/History/PixelMutator/mutatorInvert.ts +3 -0
  32. package/src/History/PixelMutator.ts +14 -11
  33. package/src/ImageData/extractImageDataBuffer.ts +3 -3
  34. package/src/ImageData/{imageDataToAlphaMask.ts → imageDataToAlphaMaskBuffer.ts} +3 -4
  35. package/src/ImageData/resizeImageData.ts +3 -5
  36. package/src/ImageData/writeImageDataBuffer.ts +7 -7
  37. package/src/Mask/AlphaMask.ts +16 -0
  38. package/src/Mask/BinaryMask.ts +16 -0
  39. package/src/Mask/CircleBrushAlphaMask.ts +32 -0
  40. package/src/Mask/CircleBrushBinaryMask.ts +30 -0
  41. package/src/Mask/applyBinaryMaskToAlphaMask.ts +12 -9
  42. package/src/Mask/copyMask.ts +9 -3
  43. package/src/Mask/extractMask.ts +33 -31
  44. package/src/Mask/extractMaskBuffer.ts +87 -0
  45. package/src/Mask/invertMask.ts +6 -4
  46. package/src/Mask/mergeAlphaMasks.ts +11 -10
  47. package/src/Mask/mergeBinaryMasks.ts +10 -9
  48. package/src/Mask/setMaskData.ts +7 -0
  49. package/src/MaskRect/merge2BinaryMaskRects.ts +81 -0
  50. package/src/MaskRect/mergeBinaryMaskRects.ts +39 -0
  51. package/src/MaskRect/subtractBinaryMaskRects.ts +80 -0
  52. package/src/PixelData/applyAlphaMaskToPixelData.ts +8 -5
  53. package/src/PixelData/applyBinaryMaskToPixelData.ts +8 -9
  54. package/src/PixelData/applyCircleBrushToPixelData.ts +54 -62
  55. package/src/PixelData/blendColorPixelDataAlphaMask.ts +35 -25
  56. package/src/PixelData/blendColorPixelDataBinaryMask.ts +26 -19
  57. package/src/PixelData/blendPixelData.ts +1 -1
  58. package/src/PixelData/blendPixelDataAlphaMask.ts +3 -3
  59. package/src/PixelData/blendPixelDataBinaryMask.ts +4 -4
  60. package/src/PixelData/fillPixelData.ts +15 -42
  61. package/src/PixelData/fillPixelDataBinaryMask.ts +79 -0
  62. package/src/PixelData/invertPixelData.ts +3 -3
  63. package/src/PixelData/pixelDataToAlphaMask.ts +4 -2
  64. package/src/PixelData/writePixelDataBuffer.ts +2 -3
  65. package/src/Rect/getRectsBounds.ts +22 -0
  66. package/src/Rect/trimRectBounds.ts +20 -17
  67. package/src/_types.ts +55 -29
  68. package/src/index.ts +18 -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,84 +2426,50 @@ var mutatorApplyCircleBrushStroke = ((writer, deps = defaults4) => {
2378
2426
  };
2379
2427
  });
2380
2428
 
2381
- // src/PixelData/blendColorPixelDataBinaryMask.ts
2382
- function blendColorPixelDataBinaryMask(dst, color, mask, opts = {}) {
2429
+ // src/History/PixelMutator/mutatorApplyCirclePencil.ts
2430
+ var defaults5 = {
2431
+ applyCircleBrushToPixelData,
2432
+ getCircleBrushOrPencilBounds
2433
+ };
2434
+ var mutatorApplyCirclePencil = ((writer, deps = defaults5) => {
2383
2435
  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++;
2436
+ applyCircleBrushToPixelData: applyCircleBrushToPixelData2 = defaults5.applyCircleBrushToPixelData,
2437
+ getCircleBrushOrPencilBounds: getCircleBrushOrPencilBounds2 = defaults5.getCircleBrushOrPencilBounds
2438
+ } = deps;
2439
+ const boundsOut = {
2440
+ x: 0,
2441
+ y: 0,
2442
+ w: 0,
2443
+ h: 0
2444
+ };
2445
+ return {
2446
+ applyCirclePencil(color, centerX, centerY, brush, alpha = 255, blendFn) {
2447
+ const bounds = getCircleBrushOrPencilBounds2(centerX, centerY, brush.size, writer.target.width, writer.target.height, boundsOut);
2448
+ const {
2449
+ x,
2450
+ y,
2451
+ w,
2452
+ h
2453
+ } = bounds;
2454
+ writer.accumulator.storeRegionBeforeState(x, y, w, h);
2455
+ applyCircleBrushToPixelData2(writer.target, color, centerX, centerY, brush, alpha, blendFn, bounds);
2440
2456
  }
2441
- dIdx += dStride;
2442
- mIdx += mStride;
2443
- }
2444
- }
2457
+ };
2458
+ });
2445
2459
 
2446
2460
  // src/History/PixelMutator/mutatorApplyCirclePencilStroke.ts
2447
- var defaults5 = {
2461
+ var defaults6 = {
2448
2462
  forEachLinePoint,
2449
2463
  blendColorPixelDataBinaryMask,
2450
2464
  getCircleBrushOrPencilBounds,
2451
2465
  getCircleBrushOrPencilStrokeBounds
2452
2466
  };
2453
- var mutatorApplyCirclePencilStroke = ((writer, deps = defaults5) => {
2467
+ var mutatorApplyCirclePencilStroke = ((writer, deps = defaults6) => {
2454
2468
  const {
2455
- forEachLinePoint: forEachLinePoint2 = defaults5.forEachLinePoint,
2456
- blendColorPixelDataBinaryMask: blendColorPixelDataBinaryMask2 = defaults5.blendColorPixelDataBinaryMask,
2457
- getCircleBrushOrPencilStrokeBounds: getCircleBrushOrPencilStrokeBounds2 = defaults5.getCircleBrushOrPencilStrokeBounds,
2458
- getCircleBrushOrPencilBounds: getCircleBrushOrPencilBounds2 = defaults5.getCircleBrushOrPencilBounds
2469
+ forEachLinePoint: forEachLinePoint2 = defaults6.forEachLinePoint,
2470
+ blendColorPixelDataBinaryMask: blendColorPixelDataBinaryMask2 = defaults6.blendColorPixelDataBinaryMask,
2471
+ getCircleBrushOrPencilStrokeBounds: getCircleBrushOrPencilStrokeBounds2 = defaults6.getCircleBrushOrPencilStrokeBounds,
2472
+ getCircleBrushOrPencilBounds: getCircleBrushOrPencilBounds2 = defaults6.getCircleBrushOrPencilBounds
2459
2473
  } = deps;
2460
2474
  const strokeBoundsOut = {
2461
2475
  x: 0,
@@ -2477,19 +2491,25 @@ var mutatorApplyCirclePencilStroke = ((writer, deps = defaults5) => {
2477
2491
  w: 0,
2478
2492
  h: 0
2479
2493
  };
2494
+ const mask = {
2495
+ type: 1 /* BINARY */,
2496
+ data: null,
2497
+ w: 0,
2498
+ h: 0
2499
+ };
2480
2500
  return {
2481
- applyCirclePencilStroke(color, x0, y0, x1, y1, brushSize, alpha = 255, blendFn = sourceOverPerfect) {
2501
+ applyCirclePencilStroke(color, x0, y0, x1, y1, brush, alpha = 255, blendFn = sourceOverPerfect) {
2482
2502
  const {
2483
2503
  x: bx,
2484
2504
  y: by,
2485
2505
  w: bw,
2486
2506
  h: bh
2487
- } = getCircleBrushOrPencilStrokeBounds2(x0, y0, x1, y1, brushSize, strokeBoundsOut);
2507
+ } = getCircleBrushOrPencilStrokeBounds2(x0, y0, x1, y1, brush.size, strokeBoundsOut);
2488
2508
  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;
2509
+ mask.data = new Uint8Array(bw * bh);
2510
+ mask.w = bw;
2511
+ mask.h = bh;
2512
+ const maskData = mask.data;
2493
2513
  const targetWidth = writer.target.width;
2494
2514
  const targetHeight = writer.target.height;
2495
2515
  forEachLinePoint2(x0, y0, x1, y1, (px, py) => {
@@ -2498,24 +2518,24 @@ var mutatorApplyCirclePencilStroke = ((writer, deps = defaults5) => {
2498
2518
  y: cby,
2499
2519
  w: cbw,
2500
2520
  h: cbh
2501
- } = getCircleBrushOrPencilBounds2(px, py, brushSize, targetWidth, targetHeight, circlePencilBounds);
2521
+ } = getCircleBrushOrPencilBounds2(px, py, brush.size, targetWidth, targetHeight, circlePencilBounds);
2502
2522
  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);
2523
+ const unclippedStartX = Math.floor(px + brush.minOffset);
2524
+ const unclippedStartY = Math.floor(py + brush.minOffset);
2525
+ const startX = Math.max(bx, unclippedStartX);
2526
+ const startY = Math.max(by, unclippedStartY);
2527
+ const endX = Math.min(bx + bw, unclippedStartX + brush.w);
2528
+ const endY = Math.min(by + bh, unclippedStartY + brush.h);
2509
2529
  for (let my = startY; my < endY; my++) {
2510
- const dy = my - fPy + centerOffset;
2511
- const dySqr = dy * dy;
2530
+ const brushY = my - unclippedStartY;
2512
2531
  const maskRowOffset = (my - by) * bw;
2532
+ const brushRowOffset = brushY * brush.w;
2513
2533
  for (let mx = startX; mx < endX; mx++) {
2514
- const dx = mx - fPx + centerOffset;
2515
- const dSqr = dx * dx + dySqr;
2516
- if (dSqr <= rSqr) {
2534
+ const brushX = mx - unclippedStartX;
2535
+ const brushAlpha = brush.data[brushRowOffset + brushX];
2536
+ if (brushAlpha > 0) {
2517
2537
  const maskIdx = maskRowOffset + (mx - bx);
2518
- mask[maskIdx] = 1;
2538
+ maskData[maskIdx] = brushAlpha;
2519
2539
  }
2520
2540
  }
2521
2541
  }
@@ -2603,14 +2623,14 @@ function applyRectBrushToPixelData(target, color, centerX, centerY, brushWidth,
2603
2623
  }
2604
2624
 
2605
2625
  // src/History/PixelMutator/mutatorApplyRectBrush.ts
2606
- var defaults6 = {
2626
+ var defaults7 = {
2607
2627
  applyRectBrushToPixelData,
2608
2628
  getRectBrushOrPencilBounds
2609
2629
  };
2610
- var mutatorApplyRectBrush = ((writer, deps = defaults6) => {
2630
+ var mutatorApplyRectBrush = ((writer, deps = defaults7) => {
2611
2631
  const {
2612
- applyRectBrushToPixelData: applyRectBrushToPixelData2 = defaults6.applyRectBrushToPixelData,
2613
- getRectBrushOrPencilBounds: getRectBrushOrPencilBounds2 = defaults6.getRectBrushOrPencilBounds
2632
+ applyRectBrushToPixelData: applyRectBrushToPixelData2 = defaults7.applyRectBrushToPixelData,
2633
+ getRectBrushOrPencilBounds: getRectBrushOrPencilBounds2 = defaults7.getRectBrushOrPencilBounds
2614
2634
  } = deps;
2615
2635
  const boundsOut = {
2616
2636
  x: 0,
@@ -2649,18 +2669,18 @@ function getRectBrushOrPencilStrokeBounds(x0, y0, x1, y1, brushWidth, brushHeigh
2649
2669
  }
2650
2670
 
2651
2671
  // src/History/PixelMutator/mutatorApplyRectBrushStroke.ts
2652
- var defaults7 = {
2672
+ var defaults8 = {
2653
2673
  forEachLinePoint,
2654
2674
  blendColorPixelDataAlphaMask,
2655
2675
  getRectBrushOrPencilBounds,
2656
2676
  getRectBrushOrPencilStrokeBounds
2657
2677
  };
2658
- var mutatorApplyRectBrushStroke = ((writer, deps = defaults7) => {
2678
+ var mutatorApplyRectBrushStroke = ((writer, deps = defaults8) => {
2659
2679
  const {
2660
- forEachLinePoint: forEachLinePoint2 = defaults7.forEachLinePoint,
2661
- blendColorPixelDataAlphaMask: blendColorPixelDataAlphaMask2 = defaults7.blendColorPixelDataAlphaMask,
2662
- getRectBrushOrPencilBounds: getRectBrushOrPencilBounds2 = defaults7.getRectBrushOrPencilBounds,
2663
- getRectBrushOrPencilStrokeBounds: getRectBrushOrPencilStrokeBounds2 = defaults7.getRectBrushOrPencilStrokeBounds
2680
+ forEachLinePoint: forEachLinePoint2 = defaults8.forEachLinePoint,
2681
+ blendColorPixelDataAlphaMask: blendColorPixelDataAlphaMask2 = defaults8.blendColorPixelDataAlphaMask,
2682
+ getRectBrushOrPencilBounds: getRectBrushOrPencilBounds2 = defaults8.getRectBrushOrPencilBounds,
2683
+ getRectBrushOrPencilStrokeBounds: getRectBrushOrPencilStrokeBounds2 = defaults8.getRectBrushOrPencilStrokeBounds
2664
2684
  } = deps;
2665
2685
  const strokeBoundsOut = {
2666
2686
  x: 0,
@@ -2682,6 +2702,12 @@ var mutatorApplyRectBrushStroke = ((writer, deps = defaults7) => {
2682
2702
  w: 0,
2683
2703
  h: 0
2684
2704
  };
2705
+ const mask = {
2706
+ type: 0 /* ALPHA */,
2707
+ data: null,
2708
+ w: 0,
2709
+ h: 0
2710
+ };
2685
2711
  return {
2686
2712
  applyRectBrushStroke(color, x0, y0, x1, y1, brushWidth, brushHeight, alpha = 255, fallOff, blendFn = sourceOverPerfect) {
2687
2713
  const {
@@ -2691,7 +2717,10 @@ var mutatorApplyRectBrushStroke = ((writer, deps = defaults7) => {
2691
2717
  h: bh
2692
2718
  } = getRectBrushOrPencilStrokeBounds2(x0, y0, x1, y1, brushWidth, brushHeight, strokeBoundsOut);
2693
2719
  if (bw <= 0 || bh <= 0) return;
2694
- const mask = new Uint8Array(bw * bh);
2720
+ mask.data = new Uint8Array(bw * bh);
2721
+ mask.w = bw;
2722
+ mask.h = bh;
2723
+ const maskData = mask.data;
2695
2724
  const halfW = brushWidth / 2;
2696
2725
  const halfH = brushHeight / 2;
2697
2726
  const invHalfW = 1 / halfW;
@@ -2724,8 +2753,8 @@ var mutatorApplyRectBrushStroke = ((writer, deps = defaults7) => {
2724
2753
  const strength = fallOff(dist);
2725
2754
  if (strength > 0) {
2726
2755
  const intensity = strength * 255 | 0;
2727
- if (intensity > mask[maskIdx]) {
2728
- mask[maskIdx] = intensity;
2756
+ if (intensity > maskData[maskIdx]) {
2757
+ maskData[maskIdx] = intensity;
2729
2758
  }
2730
2759
  }
2731
2760
  }
@@ -2743,16 +2772,16 @@ var mutatorApplyRectBrushStroke = ((writer, deps = defaults7) => {
2743
2772
  });
2744
2773
 
2745
2774
  // src/History/PixelMutator/mutatorApplyRectPencil.ts
2746
- var defaults8 = {
2775
+ var defaults9 = {
2747
2776
  applyRectBrushToPixelData,
2748
2777
  getRectBrushOrPencilBounds,
2749
2778
  fallOff: () => 1
2750
2779
  };
2751
- var mutatorApplyRectPencil = ((writer, deps = defaults8) => {
2780
+ var mutatorApplyRectPencil = ((writer, deps = defaults9) => {
2752
2781
  const {
2753
- applyRectBrushToPixelData: applyRectBrushToPixelData2 = defaults8.applyRectBrushToPixelData,
2754
- getRectBrushOrPencilBounds: getRectBrushOrPencilBounds2 = defaults8.getRectBrushOrPencilBounds,
2755
- fallOff = defaults8.fallOff
2782
+ applyRectBrushToPixelData: applyRectBrushToPixelData2 = defaults9.applyRectBrushToPixelData,
2783
+ getRectBrushOrPencilBounds: getRectBrushOrPencilBounds2 = defaults9.getRectBrushOrPencilBounds,
2784
+ fallOff = defaults9.fallOff
2756
2785
  } = deps;
2757
2786
  const boundsOut = {
2758
2787
  x: 0,
@@ -2776,18 +2805,18 @@ var mutatorApplyRectPencil = ((writer, deps = defaults8) => {
2776
2805
  });
2777
2806
 
2778
2807
  // src/History/PixelMutator/mutatorApplyRectPencilStroke.ts
2779
- var defaults9 = {
2808
+ var defaults10 = {
2780
2809
  forEachLinePoint,
2781
2810
  getRectBrushOrPencilBounds,
2782
2811
  getRectBrushOrPencilStrokeBounds,
2783
2812
  blendColorPixelDataBinaryMask
2784
2813
  };
2785
- var mutatorApplyRectPencilStroke = ((writer, deps = defaults9) => {
2814
+ var mutatorApplyRectPencilStroke = ((writer, deps = defaults10) => {
2786
2815
  const {
2787
- forEachLinePoint: forEachLinePoint2 = defaults9.forEachLinePoint,
2788
- blendColorPixelDataBinaryMask: blendColorPixelDataBinaryMask2 = defaults9.blendColorPixelDataBinaryMask,
2789
- getRectBrushOrPencilBounds: getRectBrushOrPencilBounds2 = defaults9.getRectBrushOrPencilBounds,
2790
- getRectBrushOrPencilStrokeBounds: getRectBrushOrPencilStrokeBounds2 = defaults9.getRectBrushOrPencilStrokeBounds
2816
+ forEachLinePoint: forEachLinePoint2 = defaults10.forEachLinePoint,
2817
+ blendColorPixelDataBinaryMask: blendColorPixelDataBinaryMask2 = defaults10.blendColorPixelDataBinaryMask,
2818
+ getRectBrushOrPencilBounds: getRectBrushOrPencilBounds2 = defaults10.getRectBrushOrPencilBounds,
2819
+ getRectBrushOrPencilStrokeBounds: getRectBrushOrPencilStrokeBounds2 = defaults10.getRectBrushOrPencilStrokeBounds
2791
2820
  } = deps;
2792
2821
  const strokeBoundsOut = {
2793
2822
  x: 0,
@@ -2809,6 +2838,12 @@ var mutatorApplyRectPencilStroke = ((writer, deps = defaults9) => {
2809
2838
  w: 0,
2810
2839
  h: 0
2811
2840
  };
2841
+ const mask = {
2842
+ type: 1 /* BINARY */,
2843
+ data: null,
2844
+ w: 0,
2845
+ h: 0
2846
+ };
2812
2847
  return {
2813
2848
  applyRectPencilStroke(color, x0, y0, x1, y1, brushWidth, brushHeight, alpha = 255, blendFn = sourceOverPerfect) {
2814
2849
  const {
@@ -2818,7 +2853,10 @@ var mutatorApplyRectPencilStroke = ((writer, deps = defaults9) => {
2818
2853
  h: bh
2819
2854
  } = getRectBrushOrPencilStrokeBounds2(x0, y0, x1, y1, brushWidth, brushHeight, strokeBoundsOut);
2820
2855
  if (bw <= 0 || bh <= 0) return;
2821
- const mask = new Uint8Array(bw * bh);
2856
+ mask.data = new Uint8Array(bw * bh);
2857
+ mask.w = bw;
2858
+ mask.h = bh;
2859
+ const maskData = mask.data;
2822
2860
  const halfW = brushWidth / 2;
2823
2861
  const halfH = brushHeight / 2;
2824
2862
  const centerOffset = brushWidth % 2 === 0 ? 0.5 : 0;
@@ -2845,7 +2883,7 @@ var mutatorApplyRectPencilStroke = ((writer, deps = defaults9) => {
2845
2883
  const dx = Math.abs(mx - fPx + centerOffset);
2846
2884
  const maskIdx = maskRowOffset + (mx - bx);
2847
2885
  if (dx <= halfW && dy <= halfH) {
2848
- mask[maskIdx] = 1;
2886
+ maskData[maskIdx] = 1;
2849
2887
  }
2850
2888
  }
2851
2889
  }
@@ -2907,12 +2945,12 @@ function blendColorPixelData(dst, color, opts = {}) {
2907
2945
  }
2908
2946
 
2909
2947
  // src/History/PixelMutator/mutatorBlendColor.ts
2910
- var defaults10 = {
2948
+ var defaults11 = {
2911
2949
  blendColorPixelData
2912
2950
  };
2913
- var mutatorBlendColor = ((writer, deps = defaults10) => {
2951
+ var mutatorBlendColor = ((writer, deps = defaults11) => {
2914
2952
  const {
2915
- blendColorPixelData: blendColorPixelData2 = defaults10.blendColorPixelData
2953
+ blendColorPixelData: blendColorPixelData2 = defaults11.blendColorPixelData
2916
2954
  } = deps;
2917
2955
  return {
2918
2956
  blendColor(color, opts = {}) {
@@ -2951,7 +2989,7 @@ function mutatorBlendPixel(writer) {
2951
2989
  }
2952
2990
 
2953
2991
  // src/PixelData/blendPixelData.ts
2954
- function blendPixelData(dst, src, opts) {
2992
+ function blendPixelData(dst, src, opts = {}) {
2955
2993
  const {
2956
2994
  x: targetX = 0,
2957
2995
  y: targetY = 0,
@@ -3033,15 +3071,15 @@ function blendPixelData(dst, src, opts) {
3033
3071
  }
3034
3072
 
3035
3073
  // src/History/PixelMutator/mutatorBlendPixelData.ts
3036
- var defaults11 = {
3074
+ var defaults12 = {
3037
3075
  blendPixelData
3038
3076
  };
3039
- var mutatorBlendPixelData = ((writer, deps = defaults11) => {
3077
+ var mutatorBlendPixelData = ((writer, deps = defaults12) => {
3040
3078
  const {
3041
- blendPixelData: blendPixelData2 = defaults11.blendPixelData
3079
+ blendPixelData: blendPixelData2 = defaults12.blendPixelData
3042
3080
  } = deps;
3043
3081
  return {
3044
- blendPixelData(src, opts) {
3082
+ blendPixelData(src, opts = {}) {
3045
3083
  const {
3046
3084
  x = 0,
3047
3085
  y = 0,
@@ -3054,143 +3092,430 @@ var mutatorBlendPixelData = ((writer, deps = defaults11) => {
3054
3092
  };
3055
3093
  });
3056
3094
 
3057
- // src/PixelData/fillPixelData.ts
3058
- var SCRATCH_RECT = makeClippedRect();
3059
- function fillPixelData(dst, color, _x, _y, _w, _h, _mask) {
3060
- let x;
3061
- let y;
3062
- let w;
3063
- let h;
3064
- let mask;
3065
- if (typeof _x === "object") {
3066
- x = _x.x ?? 0;
3067
- y = _x.y ?? 0;
3068
- w = _x.w ?? dst.width;
3069
- h = _x.h ?? dst.height;
3070
- mask = _x.mask;
3071
- } else if (typeof _x === "number") {
3072
- x = _x;
3073
- y = _y;
3074
- w = _w;
3075
- h = _h;
3076
- mask = _mask;
3077
- } else {
3095
+ // src/PixelData/blendPixelDataAlphaMask.ts
3096
+ function blendPixelDataAlphaMask(dst, src, alphaMask, opts = {}) {
3097
+ const {
3098
+ x: targetX = 0,
3099
+ y: targetY = 0,
3100
+ sx: sourceX = 0,
3101
+ sy: sourceY = 0,
3102
+ w: width = src.width,
3103
+ h: height = src.height,
3104
+ alpha: globalAlpha = 255,
3105
+ blendFn = sourceOverPerfect,
3106
+ mx = 0,
3107
+ my = 0,
3108
+ invertMask = false
3109
+ } = opts;
3110
+ if (globalAlpha === 0) return;
3111
+ let x = targetX;
3112
+ let y = targetY;
3113
+ let sx = sourceX;
3114
+ let sy = sourceY;
3115
+ let w = width;
3116
+ let h = height;
3117
+ if (sx < 0) {
3118
+ x -= sx;
3119
+ w += sx;
3120
+ sx = 0;
3121
+ }
3122
+ if (sy < 0) {
3123
+ y -= sy;
3124
+ h += sy;
3125
+ sy = 0;
3126
+ }
3127
+ w = Math.min(w, src.width - sx);
3128
+ h = Math.min(h, src.height - sy);
3129
+ if (x < 0) {
3130
+ sx -= x;
3131
+ w += x;
3078
3132
  x = 0;
3133
+ }
3134
+ if (y < 0) {
3135
+ sy -= y;
3136
+ h += y;
3079
3137
  y = 0;
3080
- w = dst.width;
3081
- h = dst.height;
3082
3138
  }
3083
- const clip = resolveRectClipping(x, y, w, h, dst.width, dst.height, SCRATCH_RECT);
3084
- if (!clip.inBounds) return;
3085
- const {
3086
- x: finalX,
3087
- y: finalY,
3088
- w: actualW,
3089
- h: actualH
3090
- } = clip;
3091
- const dst32 = dst.data32;
3139
+ const actualW = Math.min(w, dst.width - x);
3140
+ const actualH = Math.min(h, dst.height - y);
3141
+ if (actualW <= 0 || actualH <= 0) return;
3092
3142
  const dw = dst.width;
3093
- if (actualW === dw && actualH === dst.height && finalX === 0 && finalY === 0) {
3094
- dst32.fill(color);
3095
- return;
3096
- }
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;
3143
+ const sw = src.width;
3144
+ const mPitch = alphaMask.w;
3145
+ const maskData = alphaMask.data;
3146
+ const dx = x - targetX | 0;
3147
+ const dy = y - targetY | 0;
3148
+ const dst32 = dst.data32;
3149
+ const src32 = src.data32;
3150
+ let dIdx = y * dw + x | 0;
3151
+ let sIdx = sy * sw + sx | 0;
3152
+ let mIdx = (my + dy) * mPitch + (mx + dx) | 0;
3153
+ const dStride = dw - actualW | 0;
3154
+ const sStride = sw - actualW | 0;
3155
+ const mStride = mPitch - actualW | 0;
3156
+ const isOpaque = globalAlpha === 255;
3157
+ const isOverwrite = blendFn.isOverwrite || false;
3158
+ for (let iy = 0; iy < actualH; iy++) {
3159
+ for (let ix = 0; ix < actualW; ix++) {
3160
+ const mVal = maskData[mIdx];
3161
+ const effM = invertMask ? 255 - mVal : mVal;
3162
+ if (effM === 0) {
3163
+ dIdx++;
3164
+ sIdx++;
3165
+ mIdx++;
3166
+ continue;
3167
+ }
3168
+ const srcCol = src32[sIdx];
3169
+ const srcAlpha = srcCol >>> 24;
3170
+ if (srcAlpha === 0 && !isOverwrite) {
3171
+ dIdx++;
3172
+ sIdx++;
3173
+ mIdx++;
3174
+ continue;
3175
+ }
3176
+ let weight = globalAlpha;
3177
+ if (isOpaque) {
3178
+ weight = effM;
3179
+ } else if (effM !== 255) {
3180
+ weight = effM * globalAlpha + 128 >> 8;
3181
+ }
3182
+ if (weight === 0) {
3183
+ dIdx++;
3184
+ sIdx++;
3185
+ mIdx++;
3186
+ continue;
3187
+ }
3188
+ let finalCol = srcCol;
3189
+ if (weight < 255) {
3190
+ const a = srcAlpha * weight + 128 >> 8;
3191
+ if (a === 0 && !isOverwrite) {
3192
+ dIdx++;
3193
+ sIdx++;
3194
+ mIdx++;
3195
+ continue;
3110
3196
  }
3197
+ finalCol = (srcCol & 16777215 | a << 24) >>> 0;
3111
3198
  }
3199
+ dst32[dIdx] = blendFn(finalCol, dst32[dIdx]);
3200
+ dIdx++;
3201
+ sIdx++;
3202
+ mIdx++;
3112
3203
  }
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
- }
3204
+ dIdx += dStride;
3205
+ sIdx += sStride;
3206
+ mIdx += mStride;
3119
3207
  }
3120
3208
  }
3121
3209
 
3122
- // src/History/PixelMutator/mutatorClear.ts
3123
- var defaults12 = {
3124
- fillPixelData
3125
- };
3126
- var mutatorClear = ((writer, deps = defaults12) => {
3127
- const {
3128
- fillPixelData: fillPixelData2 = defaults12.fillPixelData
3129
- } = deps;
3130
- return {
3131
- 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;
3139
- writer.accumulator.storeRegionBeforeState(x, y, w, h);
3140
- fillPixelData2(writer.target, 0, x, y, w, h, mask);
3141
- }
3142
- };
3143
- });
3144
-
3145
- // src/History/PixelMutator/mutatorFill.ts
3210
+ // src/History/PixelMutator/mutatorBlendPixelDataAlphaMask.ts
3146
3211
  var defaults13 = {
3147
- fillPixelData
3212
+ blendPixelDataAlphaMask
3148
3213
  };
3149
- var mutatorFill = ((writer, deps = defaults13) => {
3214
+ var mutatorBlendPixelDataAlphaMask = ((writer, deps = defaults13) => {
3150
3215
  const {
3151
- fillPixelData: fillPixelData2 = defaults13.fillPixelData
3216
+ blendPixelDataAlphaMask: blendPixelDataAlphaMask2 = defaults13.blendPixelDataAlphaMask
3152
3217
  } = deps;
3153
3218
  return {
3154
- fill(color, rect = {}) {
3155
- const {
3156
- x = 0,
3157
- y = 0,
3158
- w = writer.target.width,
3159
- h = writer.target.height,
3160
- mask = void 0
3161
- } = rect;
3219
+ blendPixelDataAlphaMask(src, mask, opts = {}) {
3220
+ const x = opts.x ?? 0;
3221
+ const y = opts.y ?? 0;
3222
+ const w = opts.w ?? src.width;
3223
+ const h = opts.h ?? src.height;
3162
3224
  writer.accumulator.storeRegionBeforeState(x, y, w, h);
3163
- fillPixelData2(writer.target, color, x, y, w, h, mask);
3225
+ blendPixelDataAlphaMask2(writer.target, src, mask, opts);
3164
3226
  }
3165
3227
  };
3166
3228
  });
3167
3229
 
3168
- // src/PixelData/invertPixelData.ts
3169
- var SCRATCH_RECT2 = makeClippedRect();
3170
- function invertPixelData(pixelData, opts = {}) {
3171
- const dst = pixelData;
3230
+ // src/PixelData/blendPixelDataBinaryMask.ts
3231
+ function blendPixelDataBinaryMask(dst, src, binaryMask, opts = {}) {
3172
3232
  const {
3173
3233
  x: targetX = 0,
3174
3234
  y: targetY = 0,
3175
- w: width = pixelData.width,
3176
- h: height = pixelData.height,
3177
- mask,
3178
- mw,
3235
+ sx: sourceX = 0,
3236
+ sy: sourceY = 0,
3237
+ w: width = src.width,
3238
+ h: height = src.height,
3239
+ alpha: globalAlpha = 255,
3240
+ blendFn = sourceOverPerfect,
3179
3241
  mx = 0,
3180
3242
  my = 0,
3181
3243
  invertMask = false
3182
3244
  } = opts;
3183
- const clip = resolveRectClipping(targetX, targetY, width, height, dst.width, dst.height, SCRATCH_RECT2);
3184
- if (!clip.inBounds) return;
3185
- const {
3186
- x,
3245
+ if (globalAlpha === 0) return;
3246
+ let x = targetX;
3247
+ let y = targetY;
3248
+ let sx = sourceX;
3249
+ let sy = sourceY;
3250
+ let w = width;
3251
+ let h = height;
3252
+ if (sx < 0) {
3253
+ x -= sx;
3254
+ w += sx;
3255
+ sx = 0;
3256
+ }
3257
+ if (sy < 0) {
3258
+ y -= sy;
3259
+ h += sy;
3260
+ sy = 0;
3261
+ }
3262
+ w = Math.min(w, src.width - sx);
3263
+ h = Math.min(h, src.height - sy);
3264
+ if (x < 0) {
3265
+ sx -= x;
3266
+ w += x;
3267
+ x = 0;
3268
+ }
3269
+ if (y < 0) {
3270
+ sy -= y;
3271
+ h += y;
3272
+ y = 0;
3273
+ }
3274
+ const actualW = Math.min(w, dst.width - x);
3275
+ const actualH = Math.min(h, dst.height - y);
3276
+ if (actualW <= 0 || actualH <= 0) return;
3277
+ const dx = x - targetX | 0;
3278
+ const dy = y - targetY | 0;
3279
+ const dst32 = dst.data32;
3280
+ const src32 = src.data32;
3281
+ const dw = dst.width;
3282
+ const sw = src.width;
3283
+ const mPitch = binaryMask.w;
3284
+ const maskData = binaryMask.data;
3285
+ let dIdx = y * dw + x | 0;
3286
+ let sIdx = sy * sw + sx | 0;
3287
+ let mIdx = (my + dy) * mPitch + (mx + dx) | 0;
3288
+ const dStride = dw - actualW | 0;
3289
+ const sStride = sw - actualW | 0;
3290
+ const mStride = mPitch - actualW | 0;
3291
+ const skipVal = invertMask ? 1 : 0;
3292
+ const isOpaque = globalAlpha === 255;
3293
+ const isOverwrite = blendFn.isOverwrite || false;
3294
+ for (let iy = 0; iy < actualH; iy++) {
3295
+ for (let ix = 0; ix < actualW; ix++) {
3296
+ if (maskData[mIdx] === skipVal) {
3297
+ dIdx++;
3298
+ sIdx++;
3299
+ mIdx++;
3300
+ continue;
3301
+ }
3302
+ const srcCol = src32[sIdx];
3303
+ const srcAlpha = srcCol >>> 24;
3304
+ if (srcAlpha === 0 && !isOverwrite) {
3305
+ dIdx++;
3306
+ sIdx++;
3307
+ mIdx++;
3308
+ continue;
3309
+ }
3310
+ let finalCol = srcCol;
3311
+ if (!isOpaque) {
3312
+ const a = srcAlpha * globalAlpha + 128 >> 8;
3313
+ if (a === 0 && !isOverwrite) {
3314
+ dIdx++;
3315
+ sIdx++;
3316
+ mIdx++;
3317
+ continue;
3318
+ }
3319
+ finalCol = (srcCol & 16777215 | a << 24) >>> 0;
3320
+ }
3321
+ dst32[dIdx] = blendFn(finalCol, dst32[dIdx]);
3322
+ dIdx++;
3323
+ sIdx++;
3324
+ mIdx++;
3325
+ }
3326
+ dIdx += dStride;
3327
+ sIdx += sStride;
3328
+ mIdx += mStride;
3329
+ }
3330
+ }
3331
+
3332
+ // src/History/PixelMutator/mutatorBlendPixelDataBinaryMask.ts
3333
+ var defaults14 = {
3334
+ blendPixelDataBinaryMask
3335
+ };
3336
+ var mutatorBlendPixelDataBinaryMask = ((writer, deps = defaults14) => {
3337
+ const {
3338
+ blendPixelDataBinaryMask: blendPixelDataBinaryMask2 = defaults14.blendPixelDataBinaryMask
3339
+ } = deps;
3340
+ return {
3341
+ blendPixelDataBinaryMask(src, mask, opts = {}) {
3342
+ const x = opts.x ?? 0;
3343
+ const y = opts.y ?? 0;
3344
+ const w = opts.w ?? src.width;
3345
+ const h = opts.h ?? src.height;
3346
+ writer.accumulator.storeRegionBeforeState(x, y, w, h);
3347
+ blendPixelDataBinaryMask2(writer.target, src, mask, opts);
3348
+ }
3349
+ };
3350
+ });
3351
+
3352
+ // src/PixelData/fillPixelData.ts
3353
+ var SCRATCH_RECT = makeClippedRect();
3354
+ function fillPixelData(dst, color, _x, _y, _w, _h) {
3355
+ let x;
3356
+ let y;
3357
+ let w;
3358
+ let h;
3359
+ if (typeof _x === "object") {
3360
+ x = _x.x ?? 0;
3361
+ y = _x.y ?? 0;
3362
+ w = _x.w ?? dst.width;
3363
+ h = _x.h ?? dst.height;
3364
+ } else if (typeof _x === "number") {
3365
+ x = _x;
3366
+ y = _y;
3367
+ w = _w;
3368
+ h = _h;
3369
+ } else {
3370
+ x = 0;
3371
+ y = 0;
3372
+ w = dst.width;
3373
+ h = dst.height;
3374
+ }
3375
+ const clip = resolveRectClipping(x, y, w, h, dst.width, dst.height, SCRATCH_RECT);
3376
+ if (!clip.inBounds) return;
3377
+ const {
3378
+ x: finalX,
3379
+ y: finalY,
3380
+ w: actualW,
3381
+ h: actualH
3382
+ } = clip;
3383
+ const dst32 = dst.data32;
3384
+ const dw = dst.width;
3385
+ if (actualW === dw && actualH === dst.height && finalX === 0 && finalY === 0) {
3386
+ dst32.fill(color);
3387
+ return;
3388
+ }
3389
+ for (let iy = 0; iy < actualH; iy++) {
3390
+ const start = (finalY + iy) * dw + finalX;
3391
+ const end = start + actualW;
3392
+ dst32.fill(color, start, end);
3393
+ }
3394
+ }
3395
+
3396
+ // src/History/PixelMutator/mutatorClear.ts
3397
+ var defaults15 = {
3398
+ fillPixelData
3399
+ };
3400
+ var mutatorClear = ((writer, deps = defaults15) => {
3401
+ const {
3402
+ fillPixelData: fillPixelData2 = defaults15.fillPixelData
3403
+ } = deps;
3404
+ return {
3405
+ clear(rect = {}) {
3406
+ const x = rect.x ?? 0;
3407
+ const y = rect.y ?? 0;
3408
+ const w = rect.w ?? writer.target.width;
3409
+ const h = rect.h ?? writer.target.height;
3410
+ writer.accumulator.storeRegionBeforeState(x, y, w, h);
3411
+ fillPixelData2(writer.target, 0, x, y, w, h);
3412
+ }
3413
+ };
3414
+ });
3415
+
3416
+ // src/History/PixelMutator/mutatorFill.ts
3417
+ var defaults16 = {
3418
+ fillPixelData
3419
+ };
3420
+ var mutatorFill = ((writer, deps = defaults16) => {
3421
+ const {
3422
+ fillPixelData: fillPixelData2 = defaults16.fillPixelData
3423
+ } = deps;
3424
+ return {
3425
+ fill(color, rect = {}) {
3426
+ const {
3427
+ x = 0,
3428
+ y = 0,
3429
+ w = writer.target.width,
3430
+ h = writer.target.height
3431
+ } = rect;
3432
+ writer.accumulator.storeRegionBeforeState(x, y, w, h);
3433
+ fillPixelData2(writer.target, color, x, y, w, h);
3434
+ }
3435
+ };
3436
+ });
3437
+
3438
+ // src/PixelData/fillPixelDataBinaryMask.ts
3439
+ var SCRATCH_RECT2 = makeClippedRect();
3440
+ function fillPixelDataBinaryMask(dst, color, mask, alpha = 255, x = 0, y = 0) {
3441
+ if (alpha === 0) return;
3442
+ const maskW = mask.w;
3443
+ const maskH = mask.h;
3444
+ const clip = resolveRectClipping(x, y, maskW, maskH, dst.width, dst.height, SCRATCH_RECT2);
3445
+ if (!clip.inBounds) return;
3446
+ const {
3447
+ x: finalX,
3448
+ y: finalY,
3449
+ w: actualW,
3450
+ h: actualH
3451
+ } = clip;
3452
+ const maskData = mask.data;
3453
+ const dst32 = dst.data32;
3454
+ const dw = dst.width;
3455
+ let finalCol = color;
3456
+ if (alpha < 255) {
3457
+ const baseSrcAlpha = color >>> 24;
3458
+ const colorRGB = color & 16777215;
3459
+ const a = baseSrcAlpha * alpha + 128 >> 8;
3460
+ finalCol = (colorRGB | a << 24) >>> 0;
3461
+ }
3462
+ for (let iy = 0; iy < actualH; iy++) {
3463
+ const currentY = finalY + iy;
3464
+ const maskY = currentY - y;
3465
+ const maskOffset = maskY * maskW;
3466
+ const dstRowOffset = currentY * dw;
3467
+ for (let ix = 0; ix < actualW; ix++) {
3468
+ const currentX = finalX + ix;
3469
+ const maskX = currentX - x;
3470
+ const maskIndex = maskOffset + maskX;
3471
+ if (maskData[maskIndex]) {
3472
+ dst32[dstRowOffset + currentX] = finalCol;
3473
+ }
3474
+ }
3475
+ }
3476
+ }
3477
+
3478
+ // src/History/PixelMutator/mutatorFillBinaryMask.ts
3479
+ var defaults17 = {
3480
+ fillPixelDataBinaryMask
3481
+ };
3482
+ var mutatorFillBinaryMask = ((writer, deps = defaults17) => {
3483
+ const {
3484
+ fillPixelDataBinaryMask: fillPixelDataBinaryMask2 = defaults17.fillPixelDataBinaryMask
3485
+ } = deps;
3486
+ return {
3487
+ fillBinaryMask(color, mask, alpha = 255, x = 0, y = 0) {
3488
+ writer.accumulator.storeRegionBeforeState(x, y, mask.w, mask.h);
3489
+ fillPixelDataBinaryMask2(writer.target, color, mask, alpha, x, y);
3490
+ }
3491
+ };
3492
+ });
3493
+
3494
+ // src/PixelData/invertPixelData.ts
3495
+ var SCRATCH_RECT3 = makeClippedRect();
3496
+ function invertPixelData(pixelData, opts = {}) {
3497
+ const dst = pixelData;
3498
+ const {
3499
+ x: targetX = 0,
3500
+ y: targetY = 0,
3501
+ w: width = pixelData.width,
3502
+ h: height = pixelData.height,
3503
+ mask,
3504
+ mx = 0,
3505
+ my = 0,
3506
+ invertMask = false
3507
+ } = opts;
3508
+ const clip = resolveRectClipping(targetX, targetY, width, height, dst.width, dst.height, SCRATCH_RECT3);
3509
+ if (!clip.inBounds) return;
3510
+ const {
3511
+ x,
3187
3512
  y,
3188
3513
  w: actualW,
3189
3514
  h: actualH
3190
3515
  } = clip;
3191
3516
  const dst32 = dst.data32;
3192
3517
  const dw = dst.width;
3193
- const mPitch = mw ?? width;
3518
+ const mPitch = mask?.w ?? width;
3194
3519
  const dx = x - targetX;
3195
3520
  const dy = y - targetY;
3196
3521
  let dIdx = y * dw + x;
@@ -3198,9 +3523,10 @@ function invertPixelData(pixelData, opts = {}) {
3198
3523
  const dStride = dw - actualW;
3199
3524
  const mStride = mPitch - actualW;
3200
3525
  if (mask) {
3526
+ const maskData = mask.data;
3201
3527
  for (let iy = 0; iy < actualH; iy++) {
3202
3528
  for (let ix = 0; ix < actualW; ix++) {
3203
- const mVal = mask[mIdx];
3529
+ const mVal = maskData[mIdx];
3204
3530
  const isHit = invertMask ? mVal === 0 : mVal === 1;
3205
3531
  if (isHit) {
3206
3532
  dst32[dIdx] = dst32[dIdx] ^ 16777215;
@@ -3223,12 +3549,12 @@ function invertPixelData(pixelData, opts = {}) {
3223
3549
  }
3224
3550
 
3225
3551
  // src/History/PixelMutator/mutatorInvert.ts
3226
- var defaults14 = {
3552
+ var defaults18 = {
3227
3553
  invertPixelData
3228
3554
  };
3229
- var mutatorInvert = ((writer, deps = defaults14) => {
3555
+ var mutatorInvert = ((writer, deps = defaults18) => {
3230
3556
  const {
3231
- invertPixelData: invertPixelData2 = defaults14.invertPixelData
3557
+ invertPixelData: invertPixelData2 = defaults18.invertPixelData
3232
3558
  } = deps;
3233
3559
  return {
3234
3560
  invert(opts = {}) {
@@ -3247,21 +3573,26 @@ var mutatorInvert = ((writer, deps = defaults14) => {
3247
3573
  // src/History/PixelMutator.ts
3248
3574
  function makeFullPixelMutator(writer) {
3249
3575
  return {
3576
+ // @sort
3250
3577
  ...mutatorApplyAlphaMask(writer),
3251
3578
  ...mutatorApplyBinaryMask(writer),
3252
- ...mutatorBlendPixelData(writer),
3253
- ...mutatorBlendColor(writer),
3254
- ...mutatorBlendPixel(writer),
3255
- ...mutatorFill(writer),
3256
- ...mutatorInvert(writer),
3257
3579
  ...mutatorApplyCircleBrush(writer),
3258
3580
  ...mutatorApplyCircleBrushStroke(writer),
3581
+ ...mutatorApplyCirclePencil(writer),
3259
3582
  ...mutatorApplyCirclePencilStroke(writer),
3260
3583
  ...mutatorApplyRectBrush(writer),
3261
3584
  ...mutatorApplyRectBrushStroke(writer),
3262
3585
  ...mutatorApplyRectPencil(writer),
3263
3586
  ...mutatorApplyRectPencilStroke(writer),
3264
- ...mutatorClear(writer)
3587
+ ...mutatorBlendColor(writer),
3588
+ ...mutatorBlendPixel(writer),
3589
+ ...mutatorBlendPixelData(writer),
3590
+ ...mutatorBlendPixelDataAlphaMask(writer),
3591
+ ...mutatorBlendPixelDataBinaryMask(writer),
3592
+ ...mutatorClear(writer),
3593
+ ...mutatorFill(writer),
3594
+ ...mutatorFillBinaryMask(writer),
3595
+ ...mutatorInvert(writer)
3265
3596
  };
3266
3597
  }
3267
3598
 
@@ -3308,39 +3639,6 @@ var PixelWriter = class {
3308
3639
  }
3309
3640
  };
3310
3641
 
3311
- // src/History/PixelMutator/mutatorApplyCirclePencil.ts
3312
- var defaults15 = {
3313
- applyCircleBrushToPixelData,
3314
- getCircleBrushOrPencilBounds,
3315
- fallOff: () => 1
3316
- };
3317
- var mutatorApplyCirclePencil = ((writer, deps = defaults15) => {
3318
- const {
3319
- applyCircleBrushToPixelData: applyCircleBrushToPixelData2 = defaults15.applyCircleBrushToPixelData,
3320
- getCircleBrushOrPencilBounds: getCircleBrushOrPencilBounds2 = defaults15.getCircleBrushOrPencilBounds,
3321
- fallOff = defaults15.fallOff
3322
- } = deps;
3323
- const boundsOut = {
3324
- x: 0,
3325
- y: 0,
3326
- w: 0,
3327
- h: 0
3328
- };
3329
- return {
3330
- applyCirclePencil(color, centerX, centerY, brushSize, alpha = 255, blendFn) {
3331
- const bounds = getCircleBrushOrPencilBounds2(centerX, centerY, brushSize, writer.target.width, writer.target.height, boundsOut);
3332
- const {
3333
- x,
3334
- y,
3335
- w,
3336
- h
3337
- } = bounds;
3338
- writer.accumulator.storeRegionBeforeState(x, y, w, h);
3339
- applyCircleBrushToPixelData2(writer.target, color, centerX, centerY, brushSize, alpha, fallOff, blendFn, bounds);
3340
- }
3341
- };
3342
- });
3343
-
3344
3642
  // src/ImageData/copyImageData.ts
3345
3643
  function copyImageData({
3346
3644
  data,
@@ -3372,8 +3670,8 @@ function makeImageDataLike(width, height, data) {
3372
3670
  };
3373
3671
  }
3374
3672
 
3375
- // src/ImageData/imageDataToAlphaMask.ts
3376
- function imageDataToAlphaMask(imageData) {
3673
+ // src/ImageData/imageDataToAlphaMaskBuffer.ts
3674
+ function imageDataToAlphaMaskBuffer(imageData) {
3377
3675
  const {
3378
3676
  width,
3379
3677
  height,
@@ -3463,13 +3761,13 @@ function resampleImageData(source, factor) {
3463
3761
  }
3464
3762
 
3465
3763
  // src/ImageData/resizeImageData.ts
3466
- function resizeImageData(current, newWidth, newHeight, offsetX = 0, offsetY = 0) {
3764
+ function resizeImageData(target, newWidth, newHeight, offsetX = 0, offsetY = 0) {
3467
3765
  const result = new ImageData(newWidth, newHeight);
3468
3766
  const {
3469
3767
  width: oldW,
3470
3768
  height: oldH,
3471
3769
  data: oldData
3472
- } = current;
3770
+ } = target;
3473
3771
  const newData = result.data;
3474
3772
  const x0 = Math.max(0, offsetX);
3475
3773
  const y0 = Math.max(0, offsetY);
@@ -3624,7 +3922,7 @@ function writeImageData(target, source, x, y, sx = 0, sy = 0, sw = source.width,
3624
3922
 
3625
3923
  // src/ImageData/writeImageDataBuffer.ts
3626
3924
  var SCRATCH_BLIT3 = makeClippedBlit();
3627
- function writeImageDataBuffer(imageData, data, _x, _y, _w, _h) {
3925
+ function writeImageDataBuffer(target, data, _x, _y, _w, _h) {
3628
3926
  const {
3629
3927
  x,
3630
3928
  y,
@@ -3640,7 +3938,7 @@ function writeImageDataBuffer(imageData, data, _x, _y, _w, _h) {
3640
3938
  width: dstW,
3641
3939
  height: dstH,
3642
3940
  data: dst
3643
- } = imageData;
3941
+ } = target;
3644
3942
  const clip = resolveBlitClipping(x, y, 0, 0, w, h, dstW, dstH, w, h, SCRATCH_BLIT3);
3645
3943
  if (!clip.inBounds) return;
3646
3944
  const {
@@ -3886,22 +4184,100 @@ async function getSupportedPixelFormats(rasterMimes = defaultRasterMimes) {
3886
4184
  return formatsPromise;
3887
4185
  }
3888
4186
 
3889
- // src/Mask/applyBinaryMaskToAlphaMask.ts
3890
- function applyBinaryMaskToAlphaMask(alphaMaskDst, dstWidth, binaryMaskSrc, srcWidth, opts = {}) {
3891
- const {
3892
- x: targetX = 0,
3893
- y: targetY = 0,
3894
- w: reqWidth = 0,
3895
- h: reqHeight = 0,
3896
- mx = 0,
3897
- my = 0,
3898
- invertMask = false
3899
- } = opts;
3900
- if (dstWidth <= 0) return;
3901
- if (binaryMaskSrc.length === 0) return;
3902
- if (srcWidth <= 0) return;
3903
- const dstHeight = alphaMaskDst.length / dstWidth | 0;
3904
- const srcHeight = binaryMaskSrc.length / srcWidth | 0;
4187
+ // src/Mask/AlphaMask.ts
4188
+ function makeAlphaMask(w, h, data) {
4189
+ return {
4190
+ type: 0 /* ALPHA */,
4191
+ data: data ?? new Uint8Array(w * h),
4192
+ w,
4193
+ h
4194
+ };
4195
+ }
4196
+
4197
+ // src/Mask/BinaryMask.ts
4198
+ function makeBinaryMask(w, h, data) {
4199
+ return {
4200
+ type: 1 /* BINARY */,
4201
+ data: data ?? new Uint8Array(w * h),
4202
+ w,
4203
+ h
4204
+ };
4205
+ }
4206
+
4207
+ // src/Mask/CircleBrushAlphaMask.ts
4208
+ function makeCircleBrushAlphaMask(size, fallOff = () => 1) {
4209
+ const area = size * size;
4210
+ const data = new Uint8Array(area);
4211
+ const radius = size / 2;
4212
+ const invR = 1 / radius;
4213
+ const minOffset = -Math.ceil(radius - 0.5);
4214
+ for (let y = 0; y < size; y++) {
4215
+ for (let x = 0; x < size; x++) {
4216
+ const dx = x - radius + 0.5;
4217
+ const dy = y - radius + 0.5;
4218
+ const distSqr = dx * dx + dy * dy;
4219
+ if (distSqr <= radius * radius) {
4220
+ const dist = Math.sqrt(distSqr);
4221
+ data[y * size + x] = fallOff(1 - dist * invR) * 255 | 0;
4222
+ }
4223
+ }
4224
+ }
4225
+ return {
4226
+ type: 0 /* ALPHA */,
4227
+ data,
4228
+ w: size,
4229
+ h: size,
4230
+ radius,
4231
+ size,
4232
+ minOffset
4233
+ };
4234
+ }
4235
+
4236
+ // src/Mask/CircleBrushBinaryMask.ts
4237
+ function makeCircleBrushBinaryMask(size) {
4238
+ const area = size * size;
4239
+ const data = new Uint8Array(area);
4240
+ const radius = size / 2;
4241
+ const minOffset = -Math.ceil(radius - 0.5);
4242
+ for (let y = 0; y < size; y++) {
4243
+ for (let x = 0; x < size; x++) {
4244
+ const dx = x - radius + 0.5;
4245
+ const dy = y - radius + 0.5;
4246
+ const distSqr = dx * dx + dy * dy;
4247
+ if (distSqr <= radius * radius) {
4248
+ data[y * size + x] = 1;
4249
+ }
4250
+ }
4251
+ }
4252
+ return {
4253
+ type: 1 /* BINARY */,
4254
+ data,
4255
+ w: size,
4256
+ h: size,
4257
+ radius,
4258
+ size,
4259
+ minOffset
4260
+ };
4261
+ }
4262
+
4263
+ // src/Mask/applyBinaryMaskToAlphaMask.ts
4264
+ function applyBinaryMaskToAlphaMask(alphaMaskDst, binaryMaskSrc, opts = {}) {
4265
+ const {
4266
+ x: targetX = 0,
4267
+ y: targetY = 0,
4268
+ w: reqWidth = 0,
4269
+ h: reqHeight = 0,
4270
+ mx = 0,
4271
+ my = 0,
4272
+ invertMask = false
4273
+ } = opts;
4274
+ const dstWidth = alphaMaskDst.w;
4275
+ if (dstWidth <= 0) return;
4276
+ if (binaryMaskSrc.data.length === 0) return;
4277
+ const srcWidth = binaryMaskSrc.w;
4278
+ if (srcWidth <= 0) return;
4279
+ const dstHeight = alphaMaskDst.data.length / dstWidth | 0;
4280
+ const srcHeight = binaryMaskSrc.data.length / srcWidth | 0;
3905
4281
  if (dstHeight <= 0) return;
3906
4282
  if (srcHeight <= 0) return;
3907
4283
  const dstX0 = Math.max(0, targetX);
@@ -3918,6 +4294,8 @@ function applyBinaryMaskToAlphaMask(alphaMaskDst, dstWidth, binaryMaskSrc, srcWi
3918
4294
  if (srcY0 + (dstY1 - dstY0) <= 0) return;
3919
4295
  const iterW = Math.min(dstX1 - dstX0, srcWidth - srcX0);
3920
4296
  const iterH = Math.min(dstY1 - dstY0, srcHeight - srcY0);
4297
+ const srcData = binaryMaskSrc.data;
4298
+ const dstData = alphaMaskDst.data;
3921
4299
  let dstIdx = dstY0 * dstWidth + dstX0;
3922
4300
  let srcIdx = srcY0 * srcWidth + srcX0;
3923
4301
  if (invertMask) {
@@ -3926,8 +4304,8 @@ function applyBinaryMaskToAlphaMask(alphaMaskDst, dstWidth, binaryMaskSrc, srcWi
3926
4304
  let d = dstIdx;
3927
4305
  let s = srcIdx;
3928
4306
  while (d < dstEnd) {
3929
- if (binaryMaskSrc[s] !== 0) {
3930
- alphaMaskDst[d] = 0;
4307
+ if (srcData[s] !== 0) {
4308
+ dstData[d] = 0;
3931
4309
  }
3932
4310
  d++;
3933
4311
  s++;
@@ -3941,8 +4319,8 @@ function applyBinaryMaskToAlphaMask(alphaMaskDst, dstWidth, binaryMaskSrc, srcWi
3941
4319
  let d = dstIdx;
3942
4320
  let s = srcIdx;
3943
4321
  while (d < dstEnd) {
3944
- if (binaryMaskSrc[s] === 0) {
3945
- alphaMaskDst[d] = 0;
4322
+ if (srcData[s] === 0) {
4323
+ dstData[d] = 0;
3946
4324
  }
3947
4325
  d++;
3948
4326
  s++;
@@ -3955,25 +4333,72 @@ function applyBinaryMaskToAlphaMask(alphaMaskDst, dstWidth, binaryMaskSrc, srcWi
3955
4333
 
3956
4334
  // src/Mask/copyMask.ts
3957
4335
  function copyMask(src) {
3958
- return src.slice();
4336
+ return {
4337
+ type: src.type,
4338
+ data: src.data.slice(),
4339
+ w: src.w,
4340
+ h: src.h
4341
+ };
4342
+ }
4343
+
4344
+ // src/Mask/extractMask.ts
4345
+ function extractMask(mask, xOrRect, y, w, h) {
4346
+ let finalX;
4347
+ let finalY;
4348
+ let finalW;
4349
+ let finalH;
4350
+ if (typeof xOrRect === "object") {
4351
+ finalX = xOrRect.x;
4352
+ finalY = xOrRect.y;
4353
+ finalW = xOrRect.w;
4354
+ finalH = xOrRect.h;
4355
+ } else {
4356
+ finalX = xOrRect;
4357
+ finalY = y;
4358
+ finalW = w;
4359
+ finalH = h;
4360
+ }
4361
+ const out = {
4362
+ type: mask.type,
4363
+ w: finalW,
4364
+ h: finalH,
4365
+ data: new Uint8Array(finalW * finalH)
4366
+ };
4367
+ const srcH = mask.h;
4368
+ const stride = mask.w;
4369
+ for (let row = 0; row < finalH; row++) {
4370
+ const currentSrcY = finalY + row;
4371
+ if (currentSrcY < 0 || currentSrcY >= srcH) continue;
4372
+ const start = Math.max(0, finalX);
4373
+ const end = Math.min(stride, finalX + finalW);
4374
+ if (start < end) {
4375
+ const srcOffset = currentSrcY * stride + start;
4376
+ const dstOffset = row * finalW + (start - finalX);
4377
+ const count = end - start;
4378
+ out.data.set(mask.data.subarray(srcOffset, srcOffset + count), dstOffset);
4379
+ }
4380
+ }
4381
+ return out;
3959
4382
  }
3960
4383
 
3961
4384
  // src/Mask/invertMask.ts
3962
4385
  function invertBinaryMask(dst) {
3963
- const len = dst.length;
4386
+ const data = dst.data;
4387
+ const len = data.length;
3964
4388
  for (let i = 0; i < len; i++) {
3965
- dst[i] = dst[i] === 0 ? 1 : 0;
4389
+ data[i] = data[i] === 0 ? 1 : 0;
3966
4390
  }
3967
4391
  }
3968
4392
  function invertAlphaMask(dst) {
3969
- const len = dst.length;
4393
+ const data = dst.data;
4394
+ const len = data.length;
3970
4395
  for (let i = 0; i < len; i++) {
3971
- dst[i] = 255 - dst[i];
4396
+ data[i] = 255 - data[i];
3972
4397
  }
3973
4398
  }
3974
4399
 
3975
4400
  // src/Mask/mergeAlphaMasks.ts
3976
- function mergeAlphaMasks(dst, dstWidth, src, srcWidth, opts) {
4401
+ function mergeAlphaMasks(dst, src, opts) {
3977
4402
  const {
3978
4403
  x: targetX = 0,
3979
4404
  y: targetY = 0,
@@ -3984,15 +4409,17 @@ function mergeAlphaMasks(dst, dstWidth, src, srcWidth, opts) {
3984
4409
  my = 0,
3985
4410
  invertMask = false
3986
4411
  } = opts;
3987
- const dstHeight = dst.length / dstWidth | 0;
3988
- const srcHeight = src.length / srcWidth | 0;
3989
4412
  if (width <= 0) return;
3990
4413
  if (height <= 0) return;
3991
4414
  if (globalAlpha === 0) return;
4415
+ const dstData = dst.data;
4416
+ const srcData = src.data;
4417
+ const srcWidth = src.w;
4418
+ const dstWidth = dst.w;
3992
4419
  const startX = Math.max(0, -targetX, -mx);
3993
4420
  const startY = Math.max(0, -targetY, -my);
3994
4421
  const endX = Math.min(width, dstWidth - targetX, srcWidth - mx);
3995
- const endY = Math.min(height, dstHeight - targetY, srcHeight - my);
4422
+ const endY = Math.min(height, dst.h - targetY, src.h - my);
3996
4423
  if (startX >= endX) return;
3997
4424
  if (startY >= endY) return;
3998
4425
  for (let iy = startY; iy < endY; iy++) {
@@ -4001,7 +4428,7 @@ function mergeAlphaMasks(dst, dstWidth, src, srcWidth, opts) {
4001
4428
  let dIdx = dy * dstWidth + targetX + startX;
4002
4429
  let sIdx = sy * srcWidth + mx + startX;
4003
4430
  for (let ix = startX; ix < endX; ix++) {
4004
- const rawM = src[sIdx];
4431
+ const rawM = srcData[sIdx];
4005
4432
  const effectiveM = invertMask ? 255 - rawM : rawM;
4006
4433
  let weight = 0;
4007
4434
  if (effectiveM === 0) {
@@ -4015,13 +4442,13 @@ function mergeAlphaMasks(dst, dstWidth, src, srcWidth, opts) {
4015
4442
  }
4016
4443
  if (weight !== 255) {
4017
4444
  if (weight === 0) {
4018
- dst[dIdx] = 0;
4445
+ dstData[dIdx] = 0;
4019
4446
  } else {
4020
- const da = dst[dIdx];
4447
+ const da = dstData[dIdx];
4021
4448
  if (da === 255) {
4022
- dst[dIdx] = weight;
4449
+ dstData[dIdx] = weight;
4023
4450
  } else if (da !== 0) {
4024
- dst[dIdx] = da * weight + 128 >> 8;
4451
+ dstData[dIdx] = da * weight + 128 >> 8;
4025
4452
  }
4026
4453
  }
4027
4454
  }
@@ -4032,7 +4459,7 @@ function mergeAlphaMasks(dst, dstWidth, src, srcWidth, opts) {
4032
4459
  }
4033
4460
 
4034
4461
  // src/Mask/mergeBinaryMasks.ts
4035
- function mergeBinaryMasks(dst, dstWidth, src, srcWidth, opts) {
4462
+ function mergeBinaryMasks(dst, src, opts) {
4036
4463
  const {
4037
4464
  x: targetX = 0,
4038
4465
  y: targetY = 0,
@@ -4042,10 +4469,12 @@ function mergeBinaryMasks(dst, dstWidth, src, srcWidth, opts) {
4042
4469
  my = 0,
4043
4470
  invertMask = false
4044
4471
  } = opts;
4472
+ const dstData = dst.data;
4473
+ const srcData = src.data;
4474
+ const srcWidth = src.w;
4475
+ const dstWidth = dst.w;
4045
4476
  if (dstWidth <= 0) return;
4046
4477
  if (srcWidth <= 0) return;
4047
- const dstHeight = dst.length / dstWidth | 0;
4048
- const srcHeight = src.length / srcWidth | 0;
4049
4478
  let x = targetX;
4050
4479
  let y = targetY;
4051
4480
  let w = width;
@@ -4059,7 +4488,7 @@ function mergeBinaryMasks(dst, dstWidth, src, srcWidth, opts) {
4059
4488
  y = 0;
4060
4489
  }
4061
4490
  w = Math.min(w, dstWidth - x);
4062
- h = Math.min(h, dstHeight - y);
4491
+ h = Math.min(h, dst.h - y);
4063
4492
  if (w <= 0) return;
4064
4493
  if (h <= 0) return;
4065
4494
  const startX = mx + (x - targetX);
@@ -4067,7 +4496,7 @@ function mergeBinaryMasks(dst, dstWidth, src, srcWidth, opts) {
4067
4496
  const sX0 = Math.max(0, startX);
4068
4497
  const sY0 = Math.max(0, startY);
4069
4498
  const sX1 = Math.min(srcWidth, startX + w);
4070
- const sY1 = Math.min(srcHeight, startY + h);
4499
+ const sY1 = Math.min(src.h, startY + h);
4071
4500
  const finalW = sX1 - sX0;
4072
4501
  const finalH = sY1 - sY0;
4073
4502
  if (finalW <= 0) return;
@@ -4080,10 +4509,10 @@ function mergeBinaryMasks(dst, dstWidth, src, srcWidth, opts) {
4080
4509
  let sIdx = sY0 * srcWidth + sX0;
4081
4510
  for (let iy = 0; iy < finalH; iy++) {
4082
4511
  for (let ix = 0; ix < finalW; ix++) {
4083
- const mVal = src[sIdx];
4512
+ const mVal = srcData[sIdx];
4084
4513
  const isMaskedOut = invertMask ? mVal !== 0 : mVal === 0;
4085
4514
  if (isMaskedOut) {
4086
- dst[dIdx] = 0;
4515
+ dstData[dIdx] = 0;
4087
4516
  }
4088
4517
  dIdx++;
4089
4518
  sIdx++;
@@ -4093,6 +4522,177 @@ function mergeBinaryMasks(dst, dstWidth, src, srcWidth, opts) {
4093
4522
  }
4094
4523
  }
4095
4524
 
4525
+ // src/Mask/setMaskData.ts
4526
+ function setMaskData(mask, width, height, data) {
4527
+ ;
4528
+ mask.w = width;
4529
+ mask.h = height;
4530
+ mask.data = data;
4531
+ }
4532
+
4533
+ // src/MaskRect/subtractBinaryMaskRects.ts
4534
+ function subtractBinaryMaskRects(current, subtracting) {
4535
+ let result = [...current];
4536
+ for (const sub of subtracting) {
4537
+ const next = [];
4538
+ for (const r of result) {
4539
+ const ix = Math.max(r.x, sub.x);
4540
+ const iy = Math.max(r.y, sub.y);
4541
+ const ix2 = Math.min(r.x + r.w, sub.x + sub.w);
4542
+ const iy2 = Math.min(r.y + r.h, sub.y + sub.h);
4543
+ if (ix >= ix2 || iy >= iy2) {
4544
+ next.push(r);
4545
+ continue;
4546
+ }
4547
+ if (r.y < iy) pushPiece(next, r, r.x, r.y, r.w, iy - r.y);
4548
+ if (iy2 < r.y + r.h) pushPiece(next, r, r.x, iy2, r.w, r.y + r.h - iy2);
4549
+ if (r.x < ix) pushPiece(next, r, r.x, iy, ix - r.x, iy2 - iy);
4550
+ if (ix2 < r.x + r.w) pushPiece(next, r, ix2, iy, r.x + r.w - ix2, iy2 - iy);
4551
+ }
4552
+ result = next;
4553
+ }
4554
+ return result;
4555
+ }
4556
+ function pushPiece(dest, r, x, y, w, h) {
4557
+ if (r.data === null || r.data === void 0) {
4558
+ dest.push({
4559
+ x,
4560
+ y,
4561
+ w,
4562
+ h,
4563
+ data: null,
4564
+ type: null
4565
+ });
4566
+ return;
4567
+ }
4568
+ const lx = x - r.x;
4569
+ const ly = y - r.y;
4570
+ const data = new Uint8Array(w * h);
4571
+ for (let row = 0; row < h; row++) {
4572
+ data.set(r.data.subarray((ly + row) * r.w + lx, (ly + row) * r.w + lx + w), row * w);
4573
+ }
4574
+ dest.push({
4575
+ x,
4576
+ y,
4577
+ w,
4578
+ h,
4579
+ data,
4580
+ type: 1 /* BINARY */
4581
+ });
4582
+ }
4583
+
4584
+ // src/Rect/getRectsBounds.ts
4585
+ function getRectsBounds(rects) {
4586
+ if (rects.length === 1) return {
4587
+ ...rects[0]
4588
+ };
4589
+ let minX = Infinity, minY = Infinity;
4590
+ let maxX = -Infinity, maxY = -Infinity;
4591
+ for (let i = 0; i < rects.length; i++) {
4592
+ const r = rects[i];
4593
+ const x1 = r.x;
4594
+ const y1 = r.y;
4595
+ const x2 = x1 + r.w;
4596
+ const y2 = y1 + r.h;
4597
+ if (x1 < minX) minX = x1;
4598
+ if (y1 < minY) minY = y1;
4599
+ if (x2 > maxX) maxX = x2;
4600
+ if (y2 > maxY) maxY = y2;
4601
+ }
4602
+ return {
4603
+ x: minX,
4604
+ y: minY,
4605
+ w: maxX - minX,
4606
+ h: maxY - minY
4607
+ };
4608
+ }
4609
+
4610
+ // src/MaskRect/merge2BinaryMaskRects.ts
4611
+ function merge2BinaryMaskRects(a, b) {
4612
+ const bounds = getRectsBounds([a, b]);
4613
+ if ((a.data === null || a.data === void 0) && (b.data === null || b.data === void 0)) {
4614
+ const ix = Math.max(a.x, b.x);
4615
+ const iy = Math.max(a.y, b.y);
4616
+ const ir = Math.min(a.x + a.w, b.x + b.w);
4617
+ const ib = Math.min(a.y + a.h, b.y + b.h);
4618
+ const iw = Math.max(0, ir - ix);
4619
+ const ih = Math.max(0, ib - iy);
4620
+ const intersectionArea = iw * ih;
4621
+ const areaA = a.w * a.h;
4622
+ const areaB = b.w * b.h;
4623
+ const boundsArea = bounds.w * bounds.h;
4624
+ if (boundsArea === areaA + areaB - intersectionArea) {
4625
+ return {
4626
+ ...bounds,
4627
+ data: null,
4628
+ type: null
4629
+ };
4630
+ }
4631
+ }
4632
+ const maskData = new Uint8Array(bounds.w * bounds.h);
4633
+ const aOffY = a.y - bounds.y;
4634
+ const aOffX = a.x - bounds.x;
4635
+ if (a.data === void 0 || a.data === null) {
4636
+ for (let ay = 0; ay < a.h; ay++) {
4637
+ const destRow = (aOffY + ay) * bounds.w + aOffX;
4638
+ maskData.fill(1, destRow, destRow + a.w);
4639
+ }
4640
+ } else {
4641
+ for (let ay = 0; ay < a.h; ay++) {
4642
+ const srcRow = ay * a.w;
4643
+ const destRow = (aOffY + ay) * bounds.w + aOffX;
4644
+ maskData.set(a.data.subarray(srcRow, srcRow + a.w), destRow);
4645
+ }
4646
+ }
4647
+ const bOffY = b.y - bounds.y;
4648
+ const bOffX = b.x - bounds.x;
4649
+ if (b.data === void 0 || b.data === null) {
4650
+ for (let by = 0; by < b.h; by++) {
4651
+ const destRow = (bOffY + by) * bounds.w + bOffX;
4652
+ maskData.fill(1, destRow, destRow + b.w);
4653
+ }
4654
+ } else {
4655
+ for (let by = 0; by < b.h; by++) {
4656
+ const srcRow = by * b.w;
4657
+ const destRow = (bOffY + by) * bounds.w + bOffX;
4658
+ for (let bx = 0; bx < b.w; bx++) {
4659
+ maskData[destRow + bx] |= b.data[srcRow + bx];
4660
+ }
4661
+ }
4662
+ }
4663
+ return {
4664
+ ...bounds,
4665
+ data: maskData,
4666
+ type: 1 /* BINARY */
4667
+ };
4668
+ }
4669
+
4670
+ // src/MaskRect/mergeBinaryMaskRects.ts
4671
+ function mergeBinaryMaskRects(current, adding) {
4672
+ const rects = [...current, ...adding];
4673
+ let changed = true;
4674
+ while (changed) {
4675
+ changed = false;
4676
+ const next = [];
4677
+ for (const r of rects) {
4678
+ let merged = false;
4679
+ for (let i = 0; i < next.length; i++) {
4680
+ const n = next[i];
4681
+ 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;
4682
+ if (overlap) {
4683
+ next[i] = merge2BinaryMaskRects(n, r);
4684
+ merged = true;
4685
+ changed = true;
4686
+ break;
4687
+ }
4688
+ }
4689
+ if (!merged) next.push(r);
4690
+ }
4691
+ rects.splice(0, rects.length, ...next);
4692
+ }
4693
+ return rects;
4694
+ }
4695
+
4096
4696
  // src/PixelData/PixelData.ts
4097
4697
  var PixelData = class _PixelData {
4098
4698
  data32;
@@ -4133,223 +4733,6 @@ var PixelData = class _PixelData {
4133
4733
  }
4134
4734
  };
4135
4735
 
4136
- // src/PixelData/blendPixelDataAlphaMask.ts
4137
- function blendPixelDataAlphaMask(dst, src, alphaMask, opts = {}) {
4138
- const {
4139
- x: targetX = 0,
4140
- y: targetY = 0,
4141
- sx: sourceX = 0,
4142
- sy: sourceY = 0,
4143
- w: width = src.width,
4144
- h: height = src.height,
4145
- alpha: globalAlpha = 255,
4146
- blendFn = sourceOverPerfect,
4147
- mw = src.width,
4148
- mx = 0,
4149
- my = 0,
4150
- invertMask = false
4151
- } = opts;
4152
- if (globalAlpha === 0) return;
4153
- let x = targetX;
4154
- let y = targetY;
4155
- let sx = sourceX;
4156
- let sy = sourceY;
4157
- let w = width;
4158
- let h = height;
4159
- if (sx < 0) {
4160
- x -= sx;
4161
- w += sx;
4162
- sx = 0;
4163
- }
4164
- if (sy < 0) {
4165
- y -= sy;
4166
- h += sy;
4167
- sy = 0;
4168
- }
4169
- w = Math.min(w, src.width - sx);
4170
- h = Math.min(h, src.height - sy);
4171
- if (x < 0) {
4172
- sx -= x;
4173
- w += x;
4174
- x = 0;
4175
- }
4176
- if (y < 0) {
4177
- sy -= y;
4178
- h += y;
4179
- y = 0;
4180
- }
4181
- const actualW = Math.min(w, dst.width - x);
4182
- const actualH = Math.min(h, dst.height - y);
4183
- if (actualW <= 0 || actualH <= 0) return;
4184
- const dw = dst.width;
4185
- const sw = src.width;
4186
- const mPitch = mw;
4187
- const dx = x - targetX | 0;
4188
- const dy = y - targetY | 0;
4189
- const dst32 = dst.data32;
4190
- const src32 = src.data32;
4191
- let dIdx = y * dw + x | 0;
4192
- let sIdx = sy * sw + sx | 0;
4193
- let mIdx = (my + dy) * mPitch + (mx + dx) | 0;
4194
- const dStride = dw - actualW | 0;
4195
- const sStride = sw - actualW | 0;
4196
- const mStride = mPitch - actualW | 0;
4197
- const isOpaque = globalAlpha === 255;
4198
- const isOverwrite = blendFn.isOverwrite || false;
4199
- for (let iy = 0; iy < actualH; iy++) {
4200
- for (let ix = 0; ix < actualW; ix++) {
4201
- const mVal = alphaMask[mIdx];
4202
- const effM = invertMask ? 255 - mVal : mVal;
4203
- if (effM === 0) {
4204
- dIdx++;
4205
- sIdx++;
4206
- mIdx++;
4207
- continue;
4208
- }
4209
- const srcCol = src32[sIdx];
4210
- const srcAlpha = srcCol >>> 24;
4211
- if (srcAlpha === 0 && !isOverwrite) {
4212
- dIdx++;
4213
- sIdx++;
4214
- mIdx++;
4215
- continue;
4216
- }
4217
- let weight = globalAlpha;
4218
- if (isOpaque) {
4219
- weight = effM;
4220
- } else if (effM !== 255) {
4221
- weight = effM * globalAlpha + 128 >> 8;
4222
- }
4223
- if (weight === 0) {
4224
- dIdx++;
4225
- sIdx++;
4226
- mIdx++;
4227
- continue;
4228
- }
4229
- let finalCol = srcCol;
4230
- if (weight < 255) {
4231
- const a = srcAlpha * weight + 128 >> 8;
4232
- if (a === 0 && !isOverwrite) {
4233
- dIdx++;
4234
- sIdx++;
4235
- mIdx++;
4236
- continue;
4237
- }
4238
- finalCol = (srcCol & 16777215 | a << 24) >>> 0;
4239
- }
4240
- dst32[dIdx] = blendFn(finalCol, dst32[dIdx]);
4241
- dIdx++;
4242
- sIdx++;
4243
- mIdx++;
4244
- }
4245
- dIdx += dStride;
4246
- sIdx += sStride;
4247
- mIdx += mStride;
4248
- }
4249
- }
4250
-
4251
- // src/PixelData/blendPixelDataBinaryMask.ts
4252
- function blendPixelDataBinaryMask(dst, src, binaryMask, opts) {
4253
- const {
4254
- x: targetX = 0,
4255
- y: targetY = 0,
4256
- sx: sourceX = 0,
4257
- sy: sourceY = 0,
4258
- w: width = src.width,
4259
- h: height = src.height,
4260
- alpha: globalAlpha = 255,
4261
- blendFn = sourceOverPerfect,
4262
- mw = src.width,
4263
- mx = 0,
4264
- my = 0,
4265
- invertMask = false
4266
- } = opts;
4267
- if (globalAlpha === 0) return;
4268
- let x = targetX;
4269
- let y = targetY;
4270
- let sx = sourceX;
4271
- let sy = sourceY;
4272
- let w = width;
4273
- let h = height;
4274
- if (sx < 0) {
4275
- x -= sx;
4276
- w += sx;
4277
- sx = 0;
4278
- }
4279
- if (sy < 0) {
4280
- y -= sy;
4281
- h += sy;
4282
- sy = 0;
4283
- }
4284
- w = Math.min(w, src.width - sx);
4285
- h = Math.min(h, src.height - sy);
4286
- if (x < 0) {
4287
- sx -= x;
4288
- w += x;
4289
- x = 0;
4290
- }
4291
- if (y < 0) {
4292
- sy -= y;
4293
- h += y;
4294
- y = 0;
4295
- }
4296
- const actualW = Math.min(w, dst.width - x);
4297
- const actualH = Math.min(h, dst.height - y);
4298
- if (actualW <= 0 || actualH <= 0) return;
4299
- const dx = x - targetX | 0;
4300
- const dy = y - targetY | 0;
4301
- const dst32 = dst.data32;
4302
- const src32 = src.data32;
4303
- const dw = dst.width;
4304
- const sw = src.width;
4305
- const mPitch = mw;
4306
- let dIdx = y * dw + x | 0;
4307
- let sIdx = sy * sw + sx | 0;
4308
- let mIdx = (my + dy) * mPitch + (mx + dx) | 0;
4309
- const dStride = dw - actualW | 0;
4310
- const sStride = sw - actualW | 0;
4311
- const mStride = mPitch - actualW | 0;
4312
- const skipVal = invertMask ? 1 : 0;
4313
- const isOpaque = globalAlpha === 255;
4314
- const isOverwrite = blendFn.isOverwrite || false;
4315
- for (let iy = 0; iy < actualH; iy++) {
4316
- for (let ix = 0; ix < actualW; ix++) {
4317
- if (binaryMask[mIdx] === skipVal) {
4318
- dIdx++;
4319
- sIdx++;
4320
- mIdx++;
4321
- continue;
4322
- }
4323
- const srcCol = src32[sIdx];
4324
- const srcAlpha = srcCol >>> 24;
4325
- if (srcAlpha === 0 && !isOverwrite) {
4326
- dIdx++;
4327
- sIdx++;
4328
- mIdx++;
4329
- continue;
4330
- }
4331
- let finalCol = srcCol;
4332
- if (!isOpaque) {
4333
- const a = srcAlpha * globalAlpha + 128 >> 8;
4334
- if (a === 0 && !isOverwrite) {
4335
- dIdx++;
4336
- sIdx++;
4337
- mIdx++;
4338
- continue;
4339
- }
4340
- finalCol = (srcCol & 16777215 | a << 24) >>> 0;
4341
- }
4342
- dst32[dIdx] = blendFn(finalCol, dst32[dIdx]);
4343
- dIdx++;
4344
- sIdx++;
4345
- mIdx++;
4346
- }
4347
- dIdx += dStride;
4348
- sIdx += sStride;
4349
- mIdx += mStride;
4350
- }
4351
- }
4352
-
4353
4736
  // src/PixelData/clearPixelData.ts
4354
4737
  function clearPixelData(dst, rect) {
4355
4738
  fillPixelData(dst, 0, rect);
@@ -4442,10 +4825,11 @@ function pixelDataToAlphaMask(pixelData) {
4442
4825
  height
4443
4826
  } = pixelData;
4444
4827
  const len = data32.length;
4445
- const mask = new Uint8Array(width * height);
4828
+ const mask = makeAlphaMask(width, height);
4829
+ const maskData = mask.data;
4446
4830
  for (let i = 0; i < len; i++) {
4447
4831
  const val = data32[i];
4448
- mask[i] = val >>> 24 & 255;
4832
+ maskData[i] = val >>> 24 & 255;
4449
4833
  }
4450
4834
  return mask;
4451
4835
  }
@@ -4626,11 +5010,13 @@ export {
4626
5010
  exclusionPerfect,
4627
5011
  extractImageDataBuffer,
4628
5012
  extractMask,
5013
+ extractMaskBuffer,
4629
5014
  extractPixelData,
4630
5015
  extractPixelDataBuffer,
4631
5016
  fileInputChangeToImageData,
4632
5017
  fileToImageData,
4633
5018
  fillPixelData,
5019
+ fillPixelDataBinaryMask,
4634
5020
  floodFillSelection,
4635
5021
  forEachLinePoint,
4636
5022
  getCircleBrushOrPencilBounds,
@@ -4639,12 +5025,13 @@ export {
4639
5025
  getIndexedImageColorCounts,
4640
5026
  getRectBrushOrPencilBounds,
4641
5027
  getRectBrushOrPencilStrokeBounds,
5028
+ getRectsBounds,
4642
5029
  getSupportedPixelFormats,
4643
5030
  hardLightFast,
4644
5031
  hardLightPerfect,
4645
5032
  hardMixFast,
4646
5033
  hardMixPerfect,
4647
- imageDataToAlphaMask,
5034
+ imageDataToAlphaMaskBuffer,
4648
5035
  imageDataToDataUrl,
4649
5036
  imageDataToImgBlob,
4650
5037
  imageDataToUInt32Array,
@@ -4667,7 +5054,11 @@ export {
4667
5054
  linearDodgePerfect,
4668
5055
  linearLightFast,
4669
5056
  linearLightPerfect,
5057
+ makeAlphaMask,
5058
+ makeBinaryMask,
4670
5059
  makeBlendModeRegistry,
5060
+ makeCircleBrushAlphaMask,
5061
+ makeCircleBrushBinaryMask,
4671
5062
  makeFastBlendModeRegistry,
4672
5063
  makeFullPixelMutator,
4673
5064
  makeImageDataLike,
@@ -4675,7 +5066,9 @@ export {
4675
5066
  makePixelCanvas,
4676
5067
  makeReusableCanvas,
4677
5068
  makeReusableImageData,
5069
+ merge2BinaryMaskRects,
4678
5070
  mergeAlphaMasks,
5071
+ mergeBinaryMaskRects,
4679
5072
  mergeBinaryMasks,
4680
5073
  multiplyFast,
4681
5074
  multiplyPerfect,
@@ -4692,8 +5085,11 @@ export {
4692
5085
  mutatorBlendColor,
4693
5086
  mutatorBlendPixel,
4694
5087
  mutatorBlendPixelData,
5088
+ mutatorBlendPixelDataAlphaMask,
5089
+ mutatorBlendPixelDataBinaryMask,
4695
5090
  mutatorClear,
4696
5091
  mutatorFill,
5092
+ mutatorFillBinaryMask,
4697
5093
  mutatorInvert,
4698
5094
  overlayFast,
4699
5095
  overlayPerfect,
@@ -4716,10 +5112,12 @@ export {
4716
5112
  screenPerfect,
4717
5113
  serializeImageData,
4718
5114
  serializeNullableImageData,
5115
+ setMaskData,
4719
5116
  softLightFast,
4720
5117
  softLightPerfect,
4721
5118
  sourceOverFast,
4722
5119
  sourceOverPerfect,
5120
+ subtractBinaryMaskRects,
4723
5121
  subtractFast,
4724
5122
  subtractPerfect,
4725
5123
  toBlendModeIndexAndName,