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
@@ -75,11 +75,13 @@ __export(src_exports, {
75
75
  exclusionPerfect: () => exclusionPerfect,
76
76
  extractImageDataBuffer: () => extractImageDataBuffer,
77
77
  extractMask: () => extractMask,
78
+ extractMaskBuffer: () => extractMaskBuffer,
78
79
  extractPixelData: () => extractPixelData,
79
80
  extractPixelDataBuffer: () => extractPixelDataBuffer,
80
81
  fileInputChangeToImageData: () => fileInputChangeToImageData,
81
82
  fileToImageData: () => fileToImageData,
82
83
  fillPixelData: () => fillPixelData,
84
+ fillPixelDataBinaryMask: () => fillPixelDataBinaryMask,
83
85
  floodFillSelection: () => floodFillSelection,
84
86
  forEachLinePoint: () => forEachLinePoint,
85
87
  getCircleBrushOrPencilBounds: () => getCircleBrushOrPencilBounds,
@@ -88,12 +90,13 @@ __export(src_exports, {
88
90
  getIndexedImageColorCounts: () => getIndexedImageColorCounts,
89
91
  getRectBrushOrPencilBounds: () => getRectBrushOrPencilBounds,
90
92
  getRectBrushOrPencilStrokeBounds: () => getRectBrushOrPencilStrokeBounds,
93
+ getRectsBounds: () => getRectsBounds,
91
94
  getSupportedPixelFormats: () => getSupportedPixelFormats,
92
95
  hardLightFast: () => hardLightFast,
93
96
  hardLightPerfect: () => hardLightPerfect,
94
97
  hardMixFast: () => hardMixFast,
95
98
  hardMixPerfect: () => hardMixPerfect,
96
- imageDataToAlphaMask: () => imageDataToAlphaMask,
99
+ imageDataToAlphaMaskBuffer: () => imageDataToAlphaMaskBuffer,
97
100
  imageDataToDataUrl: () => imageDataToDataUrl,
98
101
  imageDataToImgBlob: () => imageDataToImgBlob,
99
102
  imageDataToUInt32Array: () => imageDataToUInt32Array,
@@ -116,7 +119,11 @@ __export(src_exports, {
116
119
  linearDodgePerfect: () => linearDodgePerfect,
117
120
  linearLightFast: () => linearLightFast,
118
121
  linearLightPerfect: () => linearLightPerfect,
122
+ makeAlphaMask: () => makeAlphaMask,
123
+ makeBinaryMask: () => makeBinaryMask,
119
124
  makeBlendModeRegistry: () => makeBlendModeRegistry,
125
+ makeCircleBrushAlphaMask: () => makeCircleBrushAlphaMask,
126
+ makeCircleBrushBinaryMask: () => makeCircleBrushBinaryMask,
120
127
  makeFastBlendModeRegistry: () => makeFastBlendModeRegistry,
121
128
  makeFullPixelMutator: () => makeFullPixelMutator,
122
129
  makeImageDataLike: () => makeImageDataLike,
@@ -124,7 +131,9 @@ __export(src_exports, {
124
131
  makePixelCanvas: () => makePixelCanvas,
125
132
  makeReusableCanvas: () => makeReusableCanvas,
126
133
  makeReusableImageData: () => makeReusableImageData,
134
+ merge2BinaryMaskRects: () => merge2BinaryMaskRects,
127
135
  mergeAlphaMasks: () => mergeAlphaMasks,
136
+ mergeBinaryMaskRects: () => mergeBinaryMaskRects,
128
137
  mergeBinaryMasks: () => mergeBinaryMasks,
129
138
  multiplyFast: () => multiplyFast,
130
139
  multiplyPerfect: () => multiplyPerfect,
@@ -143,6 +152,7 @@ __export(src_exports, {
143
152
  mutatorBlendPixelData: () => mutatorBlendPixelData,
144
153
  mutatorClear: () => mutatorClear,
145
154
  mutatorFill: () => mutatorFill,
155
+ mutatorFillBinaryMask: () => mutatorFillBinaryMask,
146
156
  mutatorInvert: () => mutatorInvert,
147
157
  overlayFast: () => overlayFast,
148
158
  overlayPerfect: () => overlayPerfect,
@@ -165,10 +175,12 @@ __export(src_exports, {
165
175
  screenPerfect: () => screenPerfect,
166
176
  serializeImageData: () => serializeImageData,
167
177
  serializeNullableImageData: () => serializeNullableImageData,
178
+ setMaskData: () => setMaskData,
168
179
  softLightFast: () => softLightFast,
169
180
  softLightPerfect: () => softLightPerfect,
170
181
  sourceOverFast: () => sourceOverFast,
171
182
  sourceOverPerfect: () => sourceOverPerfect,
183
+ subtractBinaryMaskRects: () => subtractBinaryMaskRects,
172
184
  subtractFast: () => subtractFast,
173
185
  subtractPerfect: () => subtractPerfect,
174
186
  toBlendModeIndexAndName: () => toBlendModeIndexAndName,
@@ -389,8 +401,8 @@ function extractImageDataBuffer(imageData, _x, _y, _w, _h) {
389
401
  return out;
390
402
  }
391
403
 
392
- // src/Mask/extractMask.ts
393
- function extractMask(mask, maskWidth, xOrRect, y, w, h) {
404
+ // src/Mask/extractMaskBuffer.ts
405
+ function extractMaskBuffer(maskBuffer, maskWidth, xOrRect, y, w, h) {
394
406
  let finalX;
395
407
  let finalY;
396
408
  let finalW;
@@ -407,7 +419,7 @@ function extractMask(mask, maskWidth, xOrRect, y, w, h) {
407
419
  finalH = h;
408
420
  }
409
421
  const out = new Uint8Array(finalW * finalH);
410
- const srcH = mask.length / maskWidth;
422
+ const srcH = maskBuffer.length / maskWidth;
411
423
  for (let row = 0; row < finalH; row++) {
412
424
  const currentSrcY = finalY + row;
413
425
  if (currentSrcY < 0 || currentSrcY >= srcH) {
@@ -419,7 +431,7 @@ function extractMask(mask, maskWidth, xOrRect, y, w, h) {
419
431
  const srcOffset = currentSrcY * maskWidth + start;
420
432
  const dstOffset = row * finalW + (start - finalX);
421
433
  const count = end - start;
422
- out.set(mask.subarray(srcOffset, srcOffset + count), dstOffset);
434
+ out.set(maskBuffer.subarray(srcOffset, srcOffset + count), dstOffset);
423
435
  }
424
436
  }
425
437
  return out;
@@ -437,8 +449,8 @@ function trimRectBounds(target, bounds) {
437
449
  if (intersectedMaxX <= intersectedX || intersectedMaxY <= intersectedY) {
438
450
  target.w = 0;
439
451
  target.h = 0;
440
- if ("mask" in target && target.mask) {
441
- target.mask = new Uint8Array(0);
452
+ if ("data" in target && target.data) {
453
+ target.data = new Uint8Array(0);
442
454
  }
443
455
  return;
444
456
  }
@@ -450,15 +462,15 @@ function trimRectBounds(target, bounds) {
450
462
  target.y = intersectedY;
451
463
  target.w = intersectedW;
452
464
  target.h = intersectedH;
453
- if ("mask" in target && target.mask) {
454
- const currentMask = extractMask(target.mask, originalW, offsetX, offsetY, intersectedW, intersectedH);
465
+ if ("data" in target && target.data) {
466
+ const currentMaskBuffer = extractMaskBuffer(target.data, originalW, offsetX, offsetY, intersectedW, intersectedH);
455
467
  let minX = intersectedW;
456
468
  let maxX = -1;
457
469
  let minY = intersectedH;
458
470
  let maxY = -1;
459
471
  for (let y = 0; y < intersectedH; y++) {
460
472
  for (let x = 0; x < intersectedW; x++) {
461
- if (currentMask[y * intersectedW + x] !== 0) {
473
+ if (currentMaskBuffer[y * intersectedW + x] !== 0) {
462
474
  if (x < minX) minX = x;
463
475
  if (x > maxX) maxX = x;
464
476
  if (y < minY) minY = y;
@@ -469,19 +481,22 @@ function trimRectBounds(target, bounds) {
469
481
  if (maxX === -1) {
470
482
  target.w = 0;
471
483
  target.h = 0;
472
- target.mask = new Uint8Array(0);
484
+ target.data = new Uint8Array(0);
473
485
  return;
474
486
  }
475
487
  const finalW = maxX - minX + 1;
476
488
  const finalH = maxY - minY + 1;
477
489
  if (finalW !== intersectedW || finalH !== intersectedH) {
478
- target.mask = extractMask(currentMask, intersectedW, minX, minY, finalW, finalH);
490
+ const newMaskBuffer = extractMaskBuffer(currentMaskBuffer, intersectedW, minX, minY, finalW, finalH);
479
491
  target.x += minX;
480
492
  target.y += minY;
481
493
  target.w = finalW;
482
494
  target.h = finalH;
495
+ target.data = newMaskBuffer;
483
496
  } else {
484
- target.mask = currentMask;
497
+ target.w = finalW;
498
+ target.h = finalH;
499
+ target.data = currentMaskBuffer;
485
500
  }
486
501
  }
487
502
  }
@@ -591,17 +606,19 @@ function floodFillSelection(img, startX, startY, {
591
606
  if (matchCount === 0) {
592
607
  return null;
593
608
  }
609
+ const w = maxX - minX + 1;
610
+ const h = maxY - minY + 1;
594
611
  const selectionRect = {
595
612
  x: minX,
596
613
  y: minY,
597
- w: maxX - minX + 1,
598
- h: maxY - minY + 1,
599
- mask: new Uint8Array((maxX - minX + 1) * (maxY - minY + 1)),
600
- maskType: 1 /* BINARY */
614
+ w,
615
+ h,
616
+ data: new Uint8Array(w * h),
617
+ type: 1 /* BINARY */
601
618
  };
602
619
  const sw = selectionRect.w;
603
620
  const sh = selectionRect.h;
604
- const finalMask = selectionRect.mask;
621
+ const finalMask = selectionRect.data;
605
622
  for (let i = 0; i < matchCount; i++) {
606
623
  const mx = matchX[i] - selectionRect.x;
607
624
  const my = matchY[i] - selectionRect.y;
@@ -1676,16 +1693,7 @@ function toBlendModeIndexAndName(input) {
1676
1693
  const num = Number(trimmed);
1677
1694
  const isNumeric = trimmed !== "" && !Number.isNaN(num);
1678
1695
  if (isNumeric && Number.isInteger(num)) {
1679
- console.log({
1680
- trimmed,
1681
- num,
1682
- isNumeric,
1683
- isInt: Number.isInteger(num)
1684
- });
1685
1696
  const name = getKeyByValue(BaseBlendMode, num);
1686
- console.log({
1687
- name
1688
- });
1689
1697
  if (name === void 0) throw new Error(`Invalid index: ${num}`);
1690
1698
  return {
1691
1699
  blendIndex: num,
@@ -2059,7 +2067,6 @@ function applyAlphaMaskToPixelData(dst, mask, opts = {}) {
2059
2067
  w: width = dst.width,
2060
2068
  h: height = dst.height,
2061
2069
  alpha: globalAlpha = 255,
2062
- mw,
2063
2070
  mx = 0,
2064
2071
  my = 0,
2065
2072
  invertMask = false
@@ -2081,15 +2088,14 @@ function applyAlphaMaskToPixelData(dst, mask, opts = {}) {
2081
2088
  h = Math.min(h, dst.height - y);
2082
2089
  if (w <= 0) return;
2083
2090
  if (h <= 0) return;
2084
- const mPitch = mw ?? width;
2091
+ const mPitch = mask.w;
2085
2092
  if (mPitch <= 0) return;
2086
- const maskHeight = mask.length / mPitch | 0;
2087
2093
  const startX = mx + (x - targetX);
2088
2094
  const startY = my + (y - targetY);
2089
2095
  const sX0 = Math.max(0, startX);
2090
2096
  const sY0 = Math.max(0, startY);
2091
2097
  const sX1 = Math.min(mPitch, startX + w);
2092
- const sY1 = Math.min(maskHeight, startY + h);
2098
+ const sY1 = Math.min(mask.h, startY + h);
2093
2099
  const finalW = sX1 - sX0;
2094
2100
  const finalH = sY1 - sY0;
2095
2101
  if (finalW <= 0) return;
@@ -2100,11 +2106,12 @@ function applyAlphaMaskToPixelData(dst, mask, opts = {}) {
2100
2106
  const dw = dst.width;
2101
2107
  const dStride = dw - finalW;
2102
2108
  const mStride = mPitch - finalW;
2109
+ const maskData = mask.data;
2103
2110
  let dIdx = (y + yShift) * dw + (x + xShift);
2104
2111
  let mIdx = sY0 * mPitch + sX0;
2105
2112
  for (let iy = 0; iy < h; iy++) {
2106
2113
  for (let ix = 0; ix < w; ix++) {
2107
- const mVal = mask[mIdx];
2114
+ const mVal = maskData[mIdx];
2108
2115
  const effectiveM = invertMask ? 255 - mVal : mVal;
2109
2116
  let weight = 0;
2110
2117
  if (effectiveM === 0) {
@@ -2164,13 +2171,12 @@ function applyBinaryMaskToPixelData(dst, mask, opts = {}) {
2164
2171
  y: targetY = 0,
2165
2172
  w: width = dst.width,
2166
2173
  h: height = dst.height,
2167
- alpha = 255,
2168
- mw,
2174
+ alpha: globalAlpha = 255,
2169
2175
  mx = 0,
2170
2176
  my = 0,
2171
2177
  invertMask = false
2172
2178
  } = opts;
2173
- if (alpha === 0) return;
2179
+ if (globalAlpha === 0) return;
2174
2180
  let x = targetX;
2175
2181
  let y = targetY;
2176
2182
  let w = width;
@@ -2187,15 +2193,14 @@ function applyBinaryMaskToPixelData(dst, mask, opts = {}) {
2187
2193
  h = Math.min(h, dst.height - y);
2188
2194
  if (w <= 0) return;
2189
2195
  if (h <= 0) return;
2190
- const mPitch = mw ?? width;
2196
+ const mPitch = mask.w;
2191
2197
  if (mPitch <= 0) return;
2192
- const maskHeight = mask.length / mPitch | 0;
2193
2198
  const startX = mx + (x - targetX);
2194
2199
  const startY = my + (y - targetY);
2195
2200
  const sX0 = Math.max(0, startX);
2196
2201
  const sY0 = Math.max(0, startY);
2197
2202
  const sX1 = Math.min(mPitch, startX + w);
2198
- const sY1 = Math.min(maskHeight, startY + h);
2203
+ const sY1 = Math.min(mask.h, startY + h);
2199
2204
  const finalW = sX1 - sX0;
2200
2205
  const finalH = sY1 - sY0;
2201
2206
  if (finalW <= 0) return;
@@ -2206,19 +2211,20 @@ function applyBinaryMaskToPixelData(dst, mask, opts = {}) {
2206
2211
  const dw = dst.width;
2207
2212
  const dStride = dw - finalW;
2208
2213
  const mStride = mPitch - finalW;
2214
+ const maskData = mask.data;
2209
2215
  let dIdx = (y + yShift) * dw + (x + xShift);
2210
2216
  let mIdx = sY0 * mPitch + sX0;
2211
2217
  for (let iy = 0; iy < h; iy++) {
2212
2218
  for (let ix = 0; ix < w; ix++) {
2213
- const mVal = mask[mIdx];
2219
+ const mVal = maskData[mIdx];
2214
2220
  const isMaskedOut = invertMask ? mVal !== 0 : mVal === 0;
2215
2221
  if (isMaskedOut) {
2216
2222
  dst32[dIdx] = (dst32[dIdx] & 16777215) >>> 0;
2217
- } else if (alpha !== 255) {
2223
+ } else if (globalAlpha !== 255) {
2218
2224
  const d = dst32[dIdx];
2219
2225
  const da = d >>> 24;
2220
2226
  if (da !== 0) {
2221
- const finalAlpha = da === 255 ? alpha : da * alpha + 128 >> 8;
2227
+ const finalAlpha = da === 255 ? globalAlpha : da * globalAlpha + 128 >> 8;
2222
2228
  dst32[dIdx] = (d & 16777215 | finalAlpha << 24) >>> 0;
2223
2229
  }
2224
2230
  }
@@ -2281,144 +2287,42 @@ function getCircleBrushOrPencilBounds(centerX, centerY, brushSize, targetWidth,
2281
2287
  return res;
2282
2288
  }
2283
2289
 
2284
- // src/PixelData/applyCircleBrushToPixelData.ts
2285
- function applyCircleBrushToPixelData(target, color, centerX, centerY, brushSize, alpha = 255, fallOff, blendFn = sourceOverPerfect, bounds) {
2286
- const targetWidth = target.width;
2287
- const targetHeight = target.height;
2288
- const b = bounds ?? getCircleBrushOrPencilBounds(centerX, centerY, brushSize, targetWidth, targetHeight);
2289
- if (b.w <= 0 || b.h <= 0) return;
2290
- const data32 = target.data32;
2291
- const r = brushSize / 2;
2292
- const rSqr = r * r;
2293
- const invR = 1 / r;
2294
- const centerOffset = brushSize % 2 === 0 ? 0.5 : 0;
2295
- const endX = b.x + b.w;
2296
- const endY = b.y + b.h;
2297
- const fCenterX = Math.floor(centerX);
2298
- const fCenterY = Math.floor(centerY);
2299
- const baseSrcAlpha = color >>> 24;
2300
- const colorRGB = color & 16777215;
2301
- const isOpaque = alpha === 255;
2302
- const isOverwrite = blendFn.isOverwrite;
2303
- for (let cy = b.y; cy < endY; cy++) {
2304
- const relY = cy - fCenterY + centerOffset;
2305
- const dySqr = relY * relY;
2306
- const rowOffset = cy * targetWidth;
2307
- for (let cx = b.x; cx < endX; cx++) {
2308
- const relX = cx - fCenterX + centerOffset;
2309
- const dSqr = relX * relX + dySqr;
2310
- if (dSqr <= rSqr) {
2311
- const idx = rowOffset + cx;
2312
- let weight = alpha;
2313
- const strength = fallOff(1 - Math.sqrt(dSqr) * invR);
2314
- const maskVal = strength * 255 | 0;
2315
- if (maskVal === 0) continue;
2316
- if (isOpaque) {
2317
- weight = maskVal;
2318
- } else if (maskVal !== 255) {
2319
- weight = maskVal * alpha + 128 >> 8;
2320
- }
2321
- let finalCol = color;
2322
- if (weight < 255) {
2323
- const a = baseSrcAlpha * weight + 128 >> 8;
2324
- if (a === 0 && !isOverwrite) continue;
2325
- finalCol = (colorRGB | a << 24) >>> 0;
2326
- }
2327
- data32[idx] = blendFn(finalCol, data32[idx]);
2328
- }
2329
- }
2330
- }
2331
- }
2332
-
2333
- // src/History/PixelMutator/mutatorApplyCircleBrush.ts
2334
- var defaults3 = {
2335
- applyCircleBrushToPixelData,
2336
- getCircleBrushOrPencilBounds
2337
- };
2338
- var mutatorApplyCircleBrush = ((writer, deps = defaults3) => {
2339
- const {
2340
- applyCircleBrushToPixelData: applyCircleBrushToPixelData2 = defaults3.applyCircleBrushToPixelData,
2341
- getCircleBrushOrPencilBounds: getCircleBrushOrPencilBounds2 = defaults3.getCircleBrushOrPencilBounds
2342
- } = deps;
2343
- const boundsOut = {
2344
- x: 0,
2345
- y: 0,
2346
- w: 0,
2347
- h: 0
2348
- };
2349
- return {
2350
- applyCircleBrush(color, centerX, centerY, brushSize, alpha = 255, fallOff, blendFn) {
2351
- const bounds = getCircleBrushOrPencilBounds2(centerX, centerY, brushSize, writer.target.width, writer.target.height, boundsOut);
2352
- const {
2353
- x,
2354
- y,
2355
- w,
2356
- h
2357
- } = bounds;
2358
- writer.accumulator.storeRegionBeforeState(x, y, w, h);
2359
- applyCircleBrushToPixelData2(writer.target, color, centerX, centerY, brushSize, alpha, fallOff, blendFn, bounds);
2360
- }
2361
- };
2362
- });
2363
-
2364
- // src/Algorithm/forEachLinePoint.ts
2365
- function forEachLinePoint(x0, y0, x1, y1, callback) {
2366
- const dx = x1 - x0;
2367
- const dy = y1 - y0;
2368
- const steps = Math.max(Math.abs(dx), Math.abs(dy));
2369
- if (steps === 0) {
2370
- callback(x0, y0);
2371
- return;
2372
- }
2373
- const xInc = dx / steps;
2374
- const yInc = dy / steps;
2375
- let curX = x0;
2376
- let curY = y0;
2377
- for (let i = 0; i <= steps; i++) {
2378
- callback(curX, curY);
2379
- curX += xInc;
2380
- curY += yInc;
2381
- }
2382
- }
2383
-
2384
2290
  // src/PixelData/blendColorPixelDataAlphaMask.ts
2385
- function blendColorPixelDataAlphaMask(dst, color, mask, opts) {
2386
- const {
2387
- x: targetX = 0,
2388
- y: targetY = 0,
2389
- w: width = dst.width,
2390
- h: height = dst.height,
2391
- alpha: globalAlpha = 255,
2392
- blendFn = sourceOverPerfect,
2393
- mw = width,
2394
- mx = 0,
2395
- my = 0,
2396
- invertMask = false
2397
- } = opts;
2398
- if (globalAlpha === 0 || !mask) return;
2291
+ function blendColorPixelDataAlphaMask(dst, color, mask, opts = {}) {
2292
+ const targetX = opts.x ?? 0;
2293
+ const targetY = opts.y ?? 0;
2294
+ const w = opts.w ?? mask.w;
2295
+ const h = opts.h ?? mask.h;
2296
+ const globalAlpha = opts.alpha ?? 255;
2297
+ const blendFn = opts.blendFn ?? sourceOverPerfect;
2298
+ const mx = opts.mx ?? 0;
2299
+ const my = opts.my ?? 0;
2300
+ const invertMask = opts.invertMask ?? false;
2301
+ if (globalAlpha === 0) return;
2399
2302
  const baseSrcAlpha = color >>> 24;
2400
2303
  const isOverwrite = blendFn.isOverwrite || false;
2401
2304
  if (baseSrcAlpha === 0 && !isOverwrite) return;
2402
2305
  let x = targetX;
2403
2306
  let y = targetY;
2404
- let w = width;
2405
- let h = height;
2307
+ let actualW = w;
2308
+ let actualH = h;
2406
2309
  if (x < 0) {
2407
- w += x;
2310
+ actualW += x;
2408
2311
  x = 0;
2409
2312
  }
2410
2313
  if (y < 0) {
2411
- h += y;
2314
+ actualH += y;
2412
2315
  y = 0;
2413
2316
  }
2414
- const actualW = Math.min(w, dst.width - x);
2415
- const actualH = Math.min(h, dst.height - y);
2317
+ actualW = Math.min(actualW, dst.width - x);
2318
+ actualH = Math.min(actualH, dst.height - y);
2416
2319
  if (actualW <= 0 || actualH <= 0) return;
2417
2320
  const dx = x - targetX | 0;
2418
2321
  const dy = y - targetY | 0;
2419
2322
  const dst32 = dst.data32;
2420
2323
  const dw = dst.width;
2421
- const mPitch = mw;
2324
+ const mPitch = mask.w;
2325
+ const maskData = mask.data;
2422
2326
  let dIdx = y * dw + x | 0;
2423
2327
  let mIdx = (my + dy) * mPitch + (mx + dx) | 0;
2424
2328
  const dStride = dw - actualW | 0;
@@ -2427,7 +2331,7 @@ function blendColorPixelDataAlphaMask(dst, color, mask, opts) {
2427
2331
  const colorRGB = color & 16777215;
2428
2332
  for (let iy = 0; iy < actualH; iy++) {
2429
2333
  for (let ix = 0; ix < actualW; ix++) {
2430
- const mVal = mask[mIdx];
2334
+ const mVal = maskData[mIdx];
2431
2335
  const effM = invertMask ? 255 - mVal : mVal;
2432
2336
  if (effM === 0) {
2433
2337
  dIdx++;
@@ -2464,6 +2368,155 @@ function blendColorPixelDataAlphaMask(dst, color, mask, opts) {
2464
2368
  }
2465
2369
  }
2466
2370
 
2371
+ // src/PixelData/blendColorPixelDataBinaryMask.ts
2372
+ function blendColorPixelDataBinaryMask(dst, color, mask, opts = {}) {
2373
+ const targetX = opts.x ?? 0;
2374
+ const targetY = opts.y ?? 0;
2375
+ let w = opts.w ?? mask.w;
2376
+ let h = opts.h ?? mask.h;
2377
+ const globalAlpha = opts.alpha ?? 255;
2378
+ const blendFn = opts.blendFn ?? sourceOverPerfect;
2379
+ const mx = opts.mx ?? 0;
2380
+ const my = opts.my ?? 0;
2381
+ const invertMask = opts.invertMask ?? false;
2382
+ if (globalAlpha === 0) return;
2383
+ const baseSrcAlpha = color >>> 24;
2384
+ const isOverwrite = blendFn.isOverwrite || false;
2385
+ if (baseSrcAlpha === 0 && !isOverwrite) return;
2386
+ let x = targetX;
2387
+ let y = targetY;
2388
+ if (x < 0) {
2389
+ w += x;
2390
+ x = 0;
2391
+ }
2392
+ if (y < 0) {
2393
+ h += y;
2394
+ y = 0;
2395
+ }
2396
+ const actualW = Math.min(w, dst.width - x);
2397
+ const actualH = Math.min(h, dst.height - y);
2398
+ if (actualW <= 0 || actualH <= 0) return;
2399
+ let baseColorWithGlobalAlpha = color;
2400
+ if (globalAlpha < 255) {
2401
+ const a = baseSrcAlpha * globalAlpha + 128 >> 8;
2402
+ if (a === 0 && !isOverwrite) return;
2403
+ baseColorWithGlobalAlpha = (color & 16777215 | a << 24) >>> 0;
2404
+ }
2405
+ const dx = x - targetX | 0;
2406
+ const dy = y - targetY | 0;
2407
+ const dst32 = dst.data32;
2408
+ const dw = dst.width;
2409
+ const mPitch = mask.w;
2410
+ const maskData = mask.data;
2411
+ let dIdx = y * dw + x | 0;
2412
+ let mIdx = (my + dy) * mPitch + (mx + dx) | 0;
2413
+ const dStride = dw - actualW | 0;
2414
+ const mStride = mPitch - actualW | 0;
2415
+ const skipVal = invertMask ? 1 : 0;
2416
+ for (let iy = 0; iy < actualH; iy++) {
2417
+ for (let ix = 0; ix < actualW; ix++) {
2418
+ if (maskData[mIdx] === skipVal) {
2419
+ dIdx++;
2420
+ mIdx++;
2421
+ continue;
2422
+ }
2423
+ dst32[dIdx] = blendFn(baseColorWithGlobalAlpha, dst32[dIdx]);
2424
+ dIdx++;
2425
+ mIdx++;
2426
+ }
2427
+ dIdx += dStride;
2428
+ mIdx += mStride;
2429
+ }
2430
+ }
2431
+
2432
+ // src/PixelData/applyCircleBrushToPixelData.ts
2433
+ function applyCircleBrushToPixelData(target, color, centerX, centerY, brush, alpha = 255, blendFn = sourceOverPerfect, scratchOptions = {}, bounds) {
2434
+ const b = bounds ?? getCircleBrushOrPencilBounds(centerX, centerY, brush.size, target.width, target.height);
2435
+ if (b.w <= 0 || b.h <= 0) return;
2436
+ const unclippedStartX = Math.floor(centerX + brush.minOffset);
2437
+ const unclippedStartY = Math.floor(centerY + brush.minOffset);
2438
+ const ix = Math.max(unclippedStartX, b.x);
2439
+ const iy = Math.max(unclippedStartY, b.y);
2440
+ const ir = Math.min(unclippedStartX + brush.w, b.x + b.w);
2441
+ const ib = Math.min(unclippedStartY + brush.h, b.y + b.h);
2442
+ const iw = ir - ix;
2443
+ const ih = ib - iy;
2444
+ if (iw <= 0 || ih <= 0) return;
2445
+ scratchOptions.x = ix;
2446
+ scratchOptions.y = iy;
2447
+ scratchOptions.w = iw;
2448
+ scratchOptions.h = ih;
2449
+ scratchOptions.mx = ix - unclippedStartX;
2450
+ scratchOptions.my = iy - unclippedStartY;
2451
+ scratchOptions.alpha = alpha;
2452
+ scratchOptions.blendFn = blendFn;
2453
+ if (brush.type === 0 /* ALPHA */) {
2454
+ blendColorPixelDataAlphaMask(target, color, brush, scratchOptions);
2455
+ }
2456
+ if (brush.type === 1 /* BINARY */) {
2457
+ blendColorPixelDataBinaryMask(target, color, brush, scratchOptions);
2458
+ }
2459
+ }
2460
+
2461
+ // src/History/PixelMutator/mutatorApplyCircleBrush.ts
2462
+ var defaults3 = {
2463
+ applyCircleBrushToPixelData,
2464
+ getCircleBrushOrPencilBounds
2465
+ };
2466
+ var mutatorApplyCircleBrush = ((writer, deps = defaults3) => {
2467
+ const {
2468
+ applyCircleBrushToPixelData: applyCircleBrushToPixelData2 = defaults3.applyCircleBrushToPixelData,
2469
+ getCircleBrushOrPencilBounds: getCircleBrushOrPencilBounds2 = defaults3.getCircleBrushOrPencilBounds
2470
+ } = deps;
2471
+ const boundsOut = {
2472
+ x: 0,
2473
+ y: 0,
2474
+ w: 0,
2475
+ h: 0
2476
+ };
2477
+ const blendColorPixelOptions = {
2478
+ alpha: 255,
2479
+ blendFn: sourceOverPerfect,
2480
+ x: 0,
2481
+ y: 0,
2482
+ w: 0,
2483
+ h: 0
2484
+ };
2485
+ return {
2486
+ applyCircleBrush(color, centerX, centerY, brush, alpha = 255, blendFn) {
2487
+ const bounds = getCircleBrushOrPencilBounds2(centerX, centerY, brush.size, writer.target.width, writer.target.height, boundsOut);
2488
+ const {
2489
+ x,
2490
+ y,
2491
+ w,
2492
+ h
2493
+ } = bounds;
2494
+ writer.accumulator.storeRegionBeforeState(x, y, w, h);
2495
+ applyCircleBrushToPixelData2(writer.target, color, centerX, centerY, brush, alpha, blendFn, blendColorPixelOptions, bounds);
2496
+ }
2497
+ };
2498
+ });
2499
+
2500
+ // src/Algorithm/forEachLinePoint.ts
2501
+ function forEachLinePoint(x0, y0, x1, y1, callback) {
2502
+ const dx = x1 - x0;
2503
+ const dy = y1 - y0;
2504
+ const steps = Math.max(Math.abs(dx), Math.abs(dy));
2505
+ if (steps === 0) {
2506
+ callback(x0, y0);
2507
+ return;
2508
+ }
2509
+ const xInc = dx / steps;
2510
+ const yInc = dy / steps;
2511
+ let curX = x0;
2512
+ let curY = y0;
2513
+ for (let i = 0; i <= steps; i++) {
2514
+ callback(curX, curY);
2515
+ curX += xInc;
2516
+ curY += yInc;
2517
+ }
2518
+ }
2519
+
2467
2520
  // src/Rect/getCircleBrushOrPencilStrokeBounds.ts
2468
2521
  function getCircleBrushOrPencilStrokeBounds(x0, y0, x1, y1, brushSize, result) {
2469
2522
  const r = Math.ceil(brushSize / 2);
@@ -2512,8 +2565,15 @@ var mutatorApplyCircleBrushStroke = ((writer, deps = defaults4) => {
2512
2565
  w: 0,
2513
2566
  h: 0
2514
2567
  };
2568
+ const mask = {
2569
+ type: 0 /* ALPHA */,
2570
+ data: null,
2571
+ w: 0,
2572
+ h: 0
2573
+ };
2515
2574
  return {
2516
- applyCircleBrushStroke(color, x0, y0, x1, y1, brushSize, alpha = 255, fallOff, blendFn = sourceOverPerfect) {
2575
+ applyCircleBrushStroke(color, x0, y0, x1, y1, brush, alpha = 255, blendFn = sourceOverPerfect) {
2576
+ const brushSize = brush.size;
2517
2577
  const {
2518
2578
  x: bx,
2519
2579
  y: by,
@@ -2521,11 +2581,12 @@ var mutatorApplyCircleBrushStroke = ((writer, deps = defaults4) => {
2521
2581
  h: bh
2522
2582
  } = getCircleBrushOrPencilStrokeBounds2(x0, y0, x1, y1, brushSize, strokeBoundsOut);
2523
2583
  if (bw <= 0 || bh <= 0) return;
2524
- const mask = new Uint8Array(bw * bh);
2525
- const r = brushSize / 2;
2526
- const rSqr = r * r;
2527
- const invR = 1 / r;
2528
- const centerOffset = brushSize % 2 === 0 ? 0.5 : 0;
2584
+ mask.data = new Uint8Array(bw * bh);
2585
+ mask.w = bw;
2586
+ mask.h = bh;
2587
+ const maskData = mask.data;
2588
+ const brushData = brush.data;
2589
+ const minOffset = brush.minOffset;
2529
2590
  const targetWidth = writer.target.width;
2530
2591
  const targetHeight = writer.target.height;
2531
2592
  forEachLinePoint2(x0, y0, x1, y1, (px, py) => {
@@ -2540,21 +2601,20 @@ var mutatorApplyCircleBrushStroke = ((writer, deps = defaults4) => {
2540
2601
  const startY = Math.max(by, cby);
2541
2602
  const endX = Math.min(bx + bw, cbx + cbw);
2542
2603
  const endY = Math.min(by + bh, cby + cbh);
2543
- const fPx = Math.floor(px);
2544
- const fPy = Math.floor(py);
2604
+ const unclippedStartX = Math.floor(px + minOffset);
2605
+ const unclippedStartY = Math.floor(py + minOffset);
2545
2606
  for (let my = startY; my < endY; my++) {
2546
- const dy = my - fPy + centerOffset;
2547
- const dySqr = dy * dy;
2548
- const maskRowOffset = (my - by) * bw;
2607
+ const strokeMaskY = my - by;
2608
+ const strokeMaskRowOffset = strokeMaskY * bw;
2609
+ const brushY = my - unclippedStartY;
2610
+ const brushRowOffset = brushY * brushSize;
2549
2611
  for (let mx = startX; mx < endX; mx++) {
2550
- const dx = mx - fPx + centerOffset;
2551
- const dSqr = dx * dx + dySqr;
2552
- if (dSqr <= rSqr) {
2553
- const maskIdx = maskRowOffset + (mx - bx);
2554
- const dist = Math.sqrt(dSqr) * invR;
2555
- const intensity = fallOff(1 - dist) * 255 | 0;
2556
- if (intensity > mask[maskIdx]) {
2557
- mask[maskIdx] = intensity;
2612
+ const brushX = mx - unclippedStartX;
2613
+ const brushVal = brushData[brushRowOffset + brushX];
2614
+ if (brushVal > 0) {
2615
+ const strokeMaskIdx = strokeMaskRowOffset + (mx - bx);
2616
+ if (brushVal > maskData[strokeMaskIdx]) {
2617
+ maskData[strokeMaskIdx] = brushVal;
2558
2618
  }
2559
2619
  }
2560
2620
  }
@@ -2571,71 +2631,6 @@ var mutatorApplyCircleBrushStroke = ((writer, deps = defaults4) => {
2571
2631
  };
2572
2632
  });
2573
2633
 
2574
- // src/PixelData/blendColorPixelDataBinaryMask.ts
2575
- function blendColorPixelDataBinaryMask(dst, color, mask, opts = {}) {
2576
- const {
2577
- x: targetX = 0,
2578
- y: targetY = 0,
2579
- w: width = dst.width,
2580
- h: height = dst.height,
2581
- alpha: globalAlpha = 255,
2582
- blendFn = sourceOverPerfect,
2583
- mw = width,
2584
- mx = 0,
2585
- my = 0,
2586
- invertMask = false
2587
- } = opts;
2588
- if (globalAlpha === 0 || !mask) return;
2589
- const baseSrcAlpha = color >>> 24;
2590
- const isOverwrite = blendFn.isOverwrite || false;
2591
- if (baseSrcAlpha === 0 && !isOverwrite) return;
2592
- let x = targetX;
2593
- let y = targetY;
2594
- let w = width;
2595
- let h = height;
2596
- if (x < 0) {
2597
- w += x;
2598
- x = 0;
2599
- }
2600
- if (y < 0) {
2601
- h += y;
2602
- y = 0;
2603
- }
2604
- const actualW = Math.min(w, dst.width - x);
2605
- const actualH = Math.min(h, dst.height - y);
2606
- if (actualW <= 0 || actualH <= 0) return;
2607
- let baseColorWithGlobalAlpha = color;
2608
- if (globalAlpha < 255) {
2609
- const a = baseSrcAlpha * globalAlpha + 128 >> 8;
2610
- if (a === 0 && !isOverwrite) return;
2611
- baseColorWithGlobalAlpha = (color & 16777215 | a << 24) >>> 0;
2612
- }
2613
- const dx = x - targetX | 0;
2614
- const dy = y - targetY | 0;
2615
- const dst32 = dst.data32;
2616
- const dw = dst.width;
2617
- const mPitch = mw;
2618
- let dIdx = y * dw + x | 0;
2619
- let mIdx = (my + dy) * mPitch + (mx + dx) | 0;
2620
- const dStride = dw - actualW | 0;
2621
- const mStride = mPitch - actualW | 0;
2622
- const skipVal = invertMask ? 1 : 0;
2623
- for (let iy = 0; iy < actualH; iy++) {
2624
- for (let ix = 0; ix < actualW; ix++) {
2625
- if (mask[mIdx] === skipVal) {
2626
- dIdx++;
2627
- mIdx++;
2628
- continue;
2629
- }
2630
- dst32[dIdx] = blendFn(baseColorWithGlobalAlpha, dst32[dIdx]);
2631
- dIdx++;
2632
- mIdx++;
2633
- }
2634
- dIdx += dStride;
2635
- mIdx += mStride;
2636
- }
2637
- }
2638
-
2639
2634
  // src/History/PixelMutator/mutatorApplyCirclePencilStroke.ts
2640
2635
  var defaults5 = {
2641
2636
  forEachLinePoint,
@@ -2670,19 +2665,25 @@ var mutatorApplyCirclePencilStroke = ((writer, deps = defaults5) => {
2670
2665
  w: 0,
2671
2666
  h: 0
2672
2667
  };
2668
+ const mask = {
2669
+ type: 1 /* BINARY */,
2670
+ data: null,
2671
+ w: 0,
2672
+ h: 0
2673
+ };
2673
2674
  return {
2674
- applyCirclePencilStroke(color, x0, y0, x1, y1, brushSize, alpha = 255, blendFn = sourceOverPerfect) {
2675
+ applyCirclePencilStroke(color, x0, y0, x1, y1, brush, alpha = 255, blendFn = sourceOverPerfect) {
2675
2676
  const {
2676
2677
  x: bx,
2677
2678
  y: by,
2678
2679
  w: bw,
2679
2680
  h: bh
2680
- } = getCircleBrushOrPencilStrokeBounds2(x0, y0, x1, y1, brushSize, strokeBoundsOut);
2681
+ } = getCircleBrushOrPencilStrokeBounds2(x0, y0, x1, y1, brush.size, strokeBoundsOut);
2681
2682
  if (bw <= 0 || bh <= 0) return;
2682
- const mask = new Uint8Array(bw * bh);
2683
- const r = brushSize / 2;
2684
- const rSqr = r * r;
2685
- const centerOffset = brushSize % 2 === 0 ? 0.5 : 0;
2683
+ mask.data = new Uint8Array(bw * bh);
2684
+ mask.w = bw;
2685
+ mask.h = bh;
2686
+ const maskData = mask.data;
2686
2687
  const targetWidth = writer.target.width;
2687
2688
  const targetHeight = writer.target.height;
2688
2689
  forEachLinePoint2(x0, y0, x1, y1, (px, py) => {
@@ -2691,24 +2692,24 @@ var mutatorApplyCirclePencilStroke = ((writer, deps = defaults5) => {
2691
2692
  y: cby,
2692
2693
  w: cbw,
2693
2694
  h: cbh
2694
- } = getCircleBrushOrPencilBounds2(px, py, brushSize, targetWidth, targetHeight, circlePencilBounds);
2695
+ } = getCircleBrushOrPencilBounds2(px, py, brush.size, targetWidth, targetHeight, circlePencilBounds);
2695
2696
  writer.accumulator.storeRegionBeforeState(cbx, cby, cbw, cbh);
2696
- const startX = Math.max(bx, cbx);
2697
- const startY = Math.max(by, cby);
2698
- const endX = Math.min(bx + bw, cbx + cbw);
2699
- const endY = Math.min(by + bh, cby + cbh);
2700
- const fPx = Math.floor(px);
2701
- const fPy = Math.floor(py);
2697
+ const unclippedStartX = Math.floor(px + brush.minOffset);
2698
+ const unclippedStartY = Math.floor(py + brush.minOffset);
2699
+ const startX = Math.max(bx, unclippedStartX);
2700
+ const startY = Math.max(by, unclippedStartY);
2701
+ const endX = Math.min(bx + bw, unclippedStartX + brush.w);
2702
+ const endY = Math.min(by + bh, unclippedStartY + brush.h);
2702
2703
  for (let my = startY; my < endY; my++) {
2703
- const dy = my - fPy + centerOffset;
2704
- const dySqr = dy * dy;
2704
+ const brushY = my - unclippedStartY;
2705
2705
  const maskRowOffset = (my - by) * bw;
2706
+ const brushRowOffset = brushY * brush.w;
2706
2707
  for (let mx = startX; mx < endX; mx++) {
2707
- const dx = mx - fPx + centerOffset;
2708
- const dSqr = dx * dx + dySqr;
2709
- if (dSqr <= rSqr) {
2708
+ const brushX = mx - unclippedStartX;
2709
+ const brushAlpha = brush.data[brushRowOffset + brushX];
2710
+ if (brushAlpha > 0) {
2710
2711
  const maskIdx = maskRowOffset + (mx - bx);
2711
- mask[maskIdx] = 1;
2712
+ maskData[maskIdx] = brushAlpha;
2712
2713
  }
2713
2714
  }
2714
2715
  }
@@ -2875,6 +2876,12 @@ var mutatorApplyRectBrushStroke = ((writer, deps = defaults7) => {
2875
2876
  w: 0,
2876
2877
  h: 0
2877
2878
  };
2879
+ const mask = {
2880
+ type: 0 /* ALPHA */,
2881
+ data: null,
2882
+ w: 0,
2883
+ h: 0
2884
+ };
2878
2885
  return {
2879
2886
  applyRectBrushStroke(color, x0, y0, x1, y1, brushWidth, brushHeight, alpha = 255, fallOff, blendFn = sourceOverPerfect) {
2880
2887
  const {
@@ -2884,7 +2891,10 @@ var mutatorApplyRectBrushStroke = ((writer, deps = defaults7) => {
2884
2891
  h: bh
2885
2892
  } = getRectBrushOrPencilStrokeBounds2(x0, y0, x1, y1, brushWidth, brushHeight, strokeBoundsOut);
2886
2893
  if (bw <= 0 || bh <= 0) return;
2887
- const mask = new Uint8Array(bw * bh);
2894
+ mask.data = new Uint8Array(bw * bh);
2895
+ mask.w = bw;
2896
+ mask.h = bh;
2897
+ const maskData = mask.data;
2888
2898
  const halfW = brushWidth / 2;
2889
2899
  const halfH = brushHeight / 2;
2890
2900
  const invHalfW = 1 / halfW;
@@ -2917,8 +2927,8 @@ var mutatorApplyRectBrushStroke = ((writer, deps = defaults7) => {
2917
2927
  const strength = fallOff(dist);
2918
2928
  if (strength > 0) {
2919
2929
  const intensity = strength * 255 | 0;
2920
- if (intensity > mask[maskIdx]) {
2921
- mask[maskIdx] = intensity;
2930
+ if (intensity > maskData[maskIdx]) {
2931
+ maskData[maskIdx] = intensity;
2922
2932
  }
2923
2933
  }
2924
2934
  }
@@ -3002,6 +3012,12 @@ var mutatorApplyRectPencilStroke = ((writer, deps = defaults9) => {
3002
3012
  w: 0,
3003
3013
  h: 0
3004
3014
  };
3015
+ const mask = {
3016
+ type: 1 /* BINARY */,
3017
+ data: null,
3018
+ w: 0,
3019
+ h: 0
3020
+ };
3005
3021
  return {
3006
3022
  applyRectPencilStroke(color, x0, y0, x1, y1, brushWidth, brushHeight, alpha = 255, blendFn = sourceOverPerfect) {
3007
3023
  const {
@@ -3011,7 +3027,10 @@ var mutatorApplyRectPencilStroke = ((writer, deps = defaults9) => {
3011
3027
  h: bh
3012
3028
  } = getRectBrushOrPencilStrokeBounds2(x0, y0, x1, y1, brushWidth, brushHeight, strokeBoundsOut);
3013
3029
  if (bw <= 0 || bh <= 0) return;
3014
- const mask = new Uint8Array(bw * bh);
3030
+ mask.data = new Uint8Array(bw * bh);
3031
+ mask.w = bw;
3032
+ mask.h = bh;
3033
+ const maskData = mask.data;
3015
3034
  const halfW = brushWidth / 2;
3016
3035
  const halfH = brushHeight / 2;
3017
3036
  const centerOffset = brushWidth % 2 === 0 ? 0.5 : 0;
@@ -3038,7 +3057,7 @@ var mutatorApplyRectPencilStroke = ((writer, deps = defaults9) => {
3038
3057
  const dx = Math.abs(mx - fPx + centerOffset);
3039
3058
  const maskIdx = maskRowOffset + (mx - bx);
3040
3059
  if (dx <= halfW && dy <= halfH) {
3041
- mask[maskIdx] = 1;
3060
+ maskData[maskIdx] = 1;
3042
3061
  }
3043
3062
  }
3044
3063
  }
@@ -3249,24 +3268,21 @@ var mutatorBlendPixelData = ((writer, deps = defaults11) => {
3249
3268
 
3250
3269
  // src/PixelData/fillPixelData.ts
3251
3270
  var SCRATCH_RECT = makeClippedRect();
3252
- function fillPixelData(dst, color, _x, _y, _w, _h, _mask) {
3271
+ function fillPixelData(dst, color, _x, _y, _w, _h) {
3253
3272
  let x;
3254
3273
  let y;
3255
3274
  let w;
3256
3275
  let h;
3257
- let mask;
3258
3276
  if (typeof _x === "object") {
3259
3277
  x = _x.x ?? 0;
3260
3278
  y = _x.y ?? 0;
3261
3279
  w = _x.w ?? dst.width;
3262
3280
  h = _x.h ?? dst.height;
3263
- mask = _x.mask;
3264
3281
  } else if (typeof _x === "number") {
3265
3282
  x = _x;
3266
3283
  y = _y;
3267
3284
  w = _w;
3268
3285
  h = _h;
3269
- mask = _mask;
3270
3286
  } else {
3271
3287
  x = 0;
3272
3288
  y = 0;
@@ -3287,28 +3303,10 @@ function fillPixelData(dst, color, _x, _y, _w, _h, _mask) {
3287
3303
  dst32.fill(color);
3288
3304
  return;
3289
3305
  }
3290
- if (mask) {
3291
- for (let iy = 0; iy < actualH; iy++) {
3292
- const currentY = finalY + iy;
3293
- const maskY = currentY - y;
3294
- const maskOffset = maskY * w;
3295
- for (let ix = 0; ix < actualW; ix++) {
3296
- const currentX = finalX + ix;
3297
- const maskX = currentX - x;
3298
- const maskIndex = maskOffset + maskX;
3299
- const isMasked = mask[maskIndex];
3300
- if (isMasked) {
3301
- const dstIndex = currentY * dw + currentX;
3302
- dst32[dstIndex] = color;
3303
- }
3304
- }
3305
- }
3306
- } else {
3307
- for (let iy = 0; iy < actualH; iy++) {
3308
- const start = (finalY + iy) * dw + finalX;
3309
- const end = start + actualW;
3310
- dst32.fill(color, start, end);
3311
- }
3306
+ for (let iy = 0; iy < actualH; iy++) {
3307
+ const start = (finalY + iy) * dw + finalX;
3308
+ const end = start + actualW;
3309
+ dst32.fill(color, start, end);
3312
3310
  }
3313
3311
  }
3314
3312
 
@@ -3322,12 +3320,10 @@ var mutatorClear = ((writer, deps = defaults12) => {
3322
3320
  } = deps;
3323
3321
  return {
3324
3322
  clear(rect = {}) {
3325
- const {
3326
- x = 0,
3327
- y = 0,
3328
- w = writer.target.width,
3329
- h = writer.target.height
3330
- } = rect;
3323
+ const x = rect.x ?? 0;
3324
+ const y = rect.y ?? 0;
3325
+ const w = rect.w ?? writer.target.width;
3326
+ const h = rect.h ?? writer.target.height;
3331
3327
  writer.accumulator.storeRegionBeforeState(x, y, w, h);
3332
3328
  fillPixelData2(writer.target, 0, x, y, w, h);
3333
3329
  }
@@ -3366,7 +3362,6 @@ function invertPixelData(pixelData, opts = {}) {
3366
3362
  w: width = pixelData.width,
3367
3363
  h: height = pixelData.height,
3368
3364
  mask,
3369
- mw,
3370
3365
  mx = 0,
3371
3366
  my = 0,
3372
3367
  invertMask = false
@@ -3381,7 +3376,7 @@ function invertPixelData(pixelData, opts = {}) {
3381
3376
  } = clip;
3382
3377
  const dst32 = dst.data32;
3383
3378
  const dw = dst.width;
3384
- const mPitch = mw ?? width;
3379
+ const mPitch = mask?.w ?? width;
3385
3380
  const dx = x - targetX;
3386
3381
  const dy = y - targetY;
3387
3382
  let dIdx = y * dw + x;
@@ -3389,9 +3384,10 @@ function invertPixelData(pixelData, opts = {}) {
3389
3384
  const dStride = dw - actualW;
3390
3385
  const mStride = mPitch - actualW;
3391
3386
  if (mask) {
3387
+ const maskData = mask.data;
3392
3388
  for (let iy = 0; iy < actualH; iy++) {
3393
3389
  for (let ix = 0; ix < actualW; ix++) {
3394
- const mVal = mask[mIdx];
3390
+ const mVal = maskData[mIdx];
3395
3391
  const isHit = invertMask ? mVal === 0 : mVal === 1;
3396
3392
  if (isHit) {
3397
3393
  dst32[dIdx] = dst32[dIdx] ^ 16777215;
@@ -3502,14 +3498,12 @@ var PixelWriter = class {
3502
3498
  // src/History/PixelMutator/mutatorApplyCirclePencil.ts
3503
3499
  var defaults15 = {
3504
3500
  applyCircleBrushToPixelData,
3505
- getCircleBrushOrPencilBounds,
3506
- fallOff: () => 1
3501
+ getCircleBrushOrPencilBounds
3507
3502
  };
3508
3503
  var mutatorApplyCirclePencil = ((writer, deps = defaults15) => {
3509
3504
  const {
3510
3505
  applyCircleBrushToPixelData: applyCircleBrushToPixelData2 = defaults15.applyCircleBrushToPixelData,
3511
- getCircleBrushOrPencilBounds: getCircleBrushOrPencilBounds2 = defaults15.getCircleBrushOrPencilBounds,
3512
- fallOff = defaults15.fallOff
3506
+ getCircleBrushOrPencilBounds: getCircleBrushOrPencilBounds2 = defaults15.getCircleBrushOrPencilBounds
3513
3507
  } = deps;
3514
3508
  const boundsOut = {
3515
3509
  x: 0,
@@ -3518,8 +3512,8 @@ var mutatorApplyCirclePencil = ((writer, deps = defaults15) => {
3518
3512
  h: 0
3519
3513
  };
3520
3514
  return {
3521
- applyCirclePencil(color, centerX, centerY, brushSize, alpha = 255, blendFn) {
3522
- const bounds = getCircleBrushOrPencilBounds2(centerX, centerY, brushSize, writer.target.width, writer.target.height, boundsOut);
3515
+ applyCirclePencil(color, centerX, centerY, brush, alpha = 255, blendFn) {
3516
+ const bounds = getCircleBrushOrPencilBounds2(centerX, centerY, brush.size, writer.target.width, writer.target.height, boundsOut);
3523
3517
  const {
3524
3518
  x,
3525
3519
  y,
@@ -3527,7 +3521,63 @@ var mutatorApplyCirclePencil = ((writer, deps = defaults15) => {
3527
3521
  h
3528
3522
  } = bounds;
3529
3523
  writer.accumulator.storeRegionBeforeState(x, y, w, h);
3530
- applyCircleBrushToPixelData2(writer.target, color, centerX, centerY, brushSize, alpha, fallOff, blendFn, bounds);
3524
+ applyCircleBrushToPixelData2(writer.target, color, centerX, centerY, brush, alpha, blendFn, bounds);
3525
+ }
3526
+ };
3527
+ });
3528
+
3529
+ // src/PixelData/fillPixelDataBinaryMask.ts
3530
+ var SCRATCH_RECT3 = makeClippedRect();
3531
+ function fillPixelDataBinaryMask(dst, color, mask, alpha = 255, x = 0, y = 0) {
3532
+ if (alpha === 0) return;
3533
+ const maskW = mask.w;
3534
+ const maskH = mask.h;
3535
+ const clip = resolveRectClipping(x, y, maskW, maskH, dst.width, dst.height, SCRATCH_RECT3);
3536
+ if (!clip.inBounds) return;
3537
+ const {
3538
+ x: finalX,
3539
+ y: finalY,
3540
+ w: actualW,
3541
+ h: actualH
3542
+ } = clip;
3543
+ const maskData = mask.data;
3544
+ const dst32 = dst.data32;
3545
+ const dw = dst.width;
3546
+ let finalCol = color;
3547
+ if (alpha < 255) {
3548
+ const baseSrcAlpha = color >>> 24;
3549
+ const colorRGB = color & 16777215;
3550
+ const a = baseSrcAlpha * alpha + 128 >> 8;
3551
+ finalCol = (colorRGB | a << 24) >>> 0;
3552
+ }
3553
+ for (let iy = 0; iy < actualH; iy++) {
3554
+ const currentY = finalY + iy;
3555
+ const maskY = currentY - y;
3556
+ const maskOffset = maskY * maskW;
3557
+ const dstRowOffset = currentY * dw;
3558
+ for (let ix = 0; ix < actualW; ix++) {
3559
+ const currentX = finalX + ix;
3560
+ const maskX = currentX - x;
3561
+ const maskIndex = maskOffset + maskX;
3562
+ if (maskData[maskIndex]) {
3563
+ dst32[dstRowOffset + currentX] = finalCol;
3564
+ }
3565
+ }
3566
+ }
3567
+ }
3568
+
3569
+ // src/History/PixelMutator/mutatorFillBinaryMask.ts
3570
+ var defaults16 = {
3571
+ fillPixelDataBinaryMask
3572
+ };
3573
+ var mutatorFillBinaryMask = ((writer, deps = defaults16) => {
3574
+ const {
3575
+ fillPixelDataBinaryMask: fillPixelDataBinaryMask2 = defaults16.fillPixelDataBinaryMask
3576
+ } = deps;
3577
+ return {
3578
+ fillBinaryMask(color, mask, alpha = 255, x = 0, y = 0) {
3579
+ writer.accumulator.storeRegionBeforeState(x, y, mask.w, mask.h);
3580
+ fillPixelDataBinaryMask2(writer.target, color, mask, alpha, x, y);
3531
3581
  }
3532
3582
  };
3533
3583
  });
@@ -3563,8 +3613,8 @@ function makeImageDataLike(width, height, data) {
3563
3613
  };
3564
3614
  }
3565
3615
 
3566
- // src/ImageData/imageDataToAlphaMask.ts
3567
- function imageDataToAlphaMask(imageData) {
3616
+ // src/ImageData/imageDataToAlphaMaskBuffer.ts
3617
+ function imageDataToAlphaMaskBuffer(imageData) {
3568
3618
  const {
3569
3619
  width,
3570
3620
  height,
@@ -3654,13 +3704,13 @@ function resampleImageData(source, factor) {
3654
3704
  }
3655
3705
 
3656
3706
  // src/ImageData/resizeImageData.ts
3657
- function resizeImageData(current, newWidth, newHeight, offsetX = 0, offsetY = 0) {
3707
+ function resizeImageData(target, newWidth, newHeight, offsetX = 0, offsetY = 0) {
3658
3708
  const result = new ImageData(newWidth, newHeight);
3659
3709
  const {
3660
3710
  width: oldW,
3661
3711
  height: oldH,
3662
3712
  data: oldData
3663
- } = current;
3713
+ } = target;
3664
3714
  const newData = result.data;
3665
3715
  const x0 = Math.max(0, offsetX);
3666
3716
  const y0 = Math.max(0, offsetY);
@@ -3815,7 +3865,7 @@ function writeImageData(target, source, x, y, sx = 0, sy = 0, sw = source.width,
3815
3865
 
3816
3866
  // src/ImageData/writeImageDataBuffer.ts
3817
3867
  var SCRATCH_BLIT3 = makeClippedBlit();
3818
- function writeImageDataBuffer(imageData, data, _x, _y, _w, _h) {
3868
+ function writeImageDataBuffer(target, data, _x, _y, _w, _h) {
3819
3869
  const {
3820
3870
  x,
3821
3871
  y,
@@ -3831,7 +3881,7 @@ function writeImageDataBuffer(imageData, data, _x, _y, _w, _h) {
3831
3881
  width: dstW,
3832
3882
  height: dstH,
3833
3883
  data: dst
3834
- } = imageData;
3884
+ } = target;
3835
3885
  const clip = resolveBlitClipping(x, y, 0, 0, w, h, dstW, dstH, w, h, SCRATCH_BLIT3);
3836
3886
  if (!clip.inBounds) return;
3837
3887
  const {
@@ -4077,8 +4127,84 @@ async function getSupportedPixelFormats(rasterMimes = defaultRasterMimes) {
4077
4127
  return formatsPromise;
4078
4128
  }
4079
4129
 
4130
+ // src/Mask/AlphaMask.ts
4131
+ function makeAlphaMask(w, h, data) {
4132
+ return {
4133
+ type: 0 /* ALPHA */,
4134
+ data: data ?? new Uint8Array(w * h),
4135
+ w,
4136
+ h
4137
+ };
4138
+ }
4139
+
4140
+ // src/Mask/BinaryMask.ts
4141
+ function makeBinaryMask(w, h, data) {
4142
+ return {
4143
+ type: 1 /* BINARY */,
4144
+ data: data ?? new Uint8Array(w * h),
4145
+ w,
4146
+ h
4147
+ };
4148
+ }
4149
+
4150
+ // src/Mask/CircleBrushAlphaMask.ts
4151
+ function makeCircleBrushAlphaMask(size, fallOff = () => 1) {
4152
+ const area = size * size;
4153
+ const data = new Uint8Array(area);
4154
+ const radius = size / 2;
4155
+ const invR = 1 / radius;
4156
+ const minOffset = -Math.ceil(radius - 0.5);
4157
+ for (let y = 0; y < size; y++) {
4158
+ for (let x = 0; x < size; x++) {
4159
+ const dx = x - radius + 0.5;
4160
+ const dy = y - radius + 0.5;
4161
+ const distSqr = dx * dx + dy * dy;
4162
+ if (distSqr <= radius * radius) {
4163
+ const dist = Math.sqrt(distSqr);
4164
+ data[y * size + x] = fallOff(1 - dist * invR) * 255 | 0;
4165
+ }
4166
+ }
4167
+ }
4168
+ return {
4169
+ type: 0 /* ALPHA */,
4170
+ data,
4171
+ w: size,
4172
+ h: size,
4173
+ radius,
4174
+ size,
4175
+ minOffset
4176
+ };
4177
+ }
4178
+
4179
+ // src/Mask/CircleBrushBinaryMask.ts
4180
+ function makeCircleBrushBinaryMask(size) {
4181
+ const area = size * size;
4182
+ const data = new Uint8Array(area);
4183
+ const radius = size / 2;
4184
+ const minOffset = -Math.ceil(radius - 0.5);
4185
+ for (let y = 0; y < size; y++) {
4186
+ for (let x = 0; x < size; x++) {
4187
+ const dx = x - radius + 0.5;
4188
+ const dy = y - radius + 0.5;
4189
+ const distSqr = dx * dx + dy * dy;
4190
+ if (distSqr <= radius * radius) {
4191
+ data[y * size + x] = 1;
4192
+ }
4193
+ }
4194
+ }
4195
+ return {
4196
+ type: 1 /* BINARY */,
4197
+ data,
4198
+ w: size,
4199
+ h: size,
4200
+ radius,
4201
+ size,
4202
+ minOffset
4203
+ };
4204
+ }
4205
+
4080
4206
  // src/Mask/applyBinaryMaskToAlphaMask.ts
4081
- function applyBinaryMaskToAlphaMask(alphaMaskDst, dstWidth, binaryMaskSrc, srcWidth, opts = {}) {
4207
+ function applyBinaryMaskToAlphaMask(alphaMaskDst, binaryMaskSrc, opts = {}) {
4082
4208
  const {
4083
4209
  x: targetX = 0,
4084
4210
  y: targetY = 0,
@@ -4088,11 +4214,13 @@ function applyBinaryMaskToAlphaMask(alphaMaskDst, dstWidth, binaryMaskSrc, srcWi
4088
4214
  my = 0,
4089
4215
  invertMask = false
4090
4216
  } = opts;
4217
+ const dstWidth = alphaMaskDst.w;
4091
4218
  if (dstWidth <= 0) return;
4092
- if (binaryMaskSrc.length === 0) return;
4219
+ if (binaryMaskSrc.data.length === 0) return;
4220
+ const srcWidth = binaryMaskSrc.w;
4093
4221
  if (srcWidth <= 0) return;
4094
- const dstHeight = alphaMaskDst.length / dstWidth | 0;
4095
- const srcHeight = binaryMaskSrc.length / srcWidth | 0;
4222
+ const dstHeight = alphaMaskDst.data.length / dstWidth | 0;
4223
+ const srcHeight = binaryMaskSrc.data.length / srcWidth | 0;
4096
4224
  if (dstHeight <= 0) return;
4097
4225
  if (srcHeight <= 0) return;
4098
4226
  const dstX0 = Math.max(0, targetX);
@@ -4109,6 +4237,8 @@ function applyBinaryMaskToAlphaMask(alphaMaskDst, dstWidth, binaryMaskSrc, srcWi
4109
4237
  if (srcY0 + (dstY1 - dstY0) <= 0) return;
4110
4238
  const iterW = Math.min(dstX1 - dstX0, srcWidth - srcX0);
4111
4239
  const iterH = Math.min(dstY1 - dstY0, srcHeight - srcY0);
4240
+ const srcData = binaryMaskSrc.data;
4241
+ const dstData = alphaMaskDst.data;
4112
4242
  let dstIdx = dstY0 * dstWidth + dstX0;
4113
4243
  let srcIdx = srcY0 * srcWidth + srcX0;
4114
4244
  if (invertMask) {
@@ -4117,8 +4247,8 @@ function applyBinaryMaskToAlphaMask(alphaMaskDst, dstWidth, binaryMaskSrc, srcWi
4117
4247
  let d = dstIdx;
4118
4248
  let s = srcIdx;
4119
4249
  while (d < dstEnd) {
4120
- if (binaryMaskSrc[s] !== 0) {
4121
- alphaMaskDst[d] = 0;
4250
+ if (srcData[s] !== 0) {
4251
+ dstData[d] = 0;
4122
4252
  }
4123
4253
  d++;
4124
4254
  s++;
@@ -4132,8 +4262,8 @@ function applyBinaryMaskToAlphaMask(alphaMaskDst, dstWidth, binaryMaskSrc, srcWi
4132
4262
  let d = dstIdx;
4133
4263
  let s = srcIdx;
4134
4264
  while (d < dstEnd) {
4135
- if (binaryMaskSrc[s] === 0) {
4136
- alphaMaskDst[d] = 0;
4265
+ if (srcData[s] === 0) {
4266
+ dstData[d] = 0;
4137
4267
  }
4138
4268
  d++;
4139
4269
  s++;
@@ -4146,25 +4276,72 @@ function applyBinaryMaskToAlphaMask(alphaMaskDst, dstWidth, binaryMaskSrc, srcWi
4146
4276
 
4147
4277
  // src/Mask/copyMask.ts
4148
4278
  function copyMask(src) {
4149
- return src.slice();
4279
+ return {
4280
+ type: src.type,
4281
+ data: src.data.slice(),
4282
+ w: src.w,
4283
+ h: src.h
4284
+ };
4285
+ }
4286
+
4287
+ // src/Mask/extractMask.ts
4288
+ function extractMask(mask, xOrRect, y, w, h) {
4289
+ let finalX;
4290
+ let finalY;
4291
+ let finalW;
4292
+ let finalH;
4293
+ if (typeof xOrRect === "object") {
4294
+ finalX = xOrRect.x;
4295
+ finalY = xOrRect.y;
4296
+ finalW = xOrRect.w;
4297
+ finalH = xOrRect.h;
4298
+ } else {
4299
+ finalX = xOrRect;
4300
+ finalY = y;
4301
+ finalW = w;
4302
+ finalH = h;
4303
+ }
4304
+ const out = {
4305
+ type: mask.type,
4306
+ w: finalW,
4307
+ h: finalH,
4308
+ data: new Uint8Array(finalW * finalH)
4309
+ };
4310
+ const srcH = mask.h;
4311
+ const stride = mask.w;
4312
+ for (let row = 0; row < finalH; row++) {
4313
+ const currentSrcY = finalY + row;
4314
+ if (currentSrcY < 0 || currentSrcY >= srcH) continue;
4315
+ const start = Math.max(0, finalX);
4316
+ const end = Math.min(stride, finalX + finalW);
4317
+ if (start < end) {
4318
+ const srcOffset = currentSrcY * stride + start;
4319
+ const dstOffset = row * finalW + (start - finalX);
4320
+ const count = end - start;
4321
+ out.data.set(mask.data.subarray(srcOffset, srcOffset + count), dstOffset);
4322
+ }
4323
+ }
4324
+ return out;
4150
4325
  }
4151
4326
 
4152
4327
  // src/Mask/invertMask.ts
4153
4328
  function invertBinaryMask(dst) {
4154
- const len = dst.length;
4329
+ const data = dst.data;
4330
+ const len = data.length;
4155
4331
  for (let i = 0; i < len; i++) {
4156
- dst[i] = dst[i] === 0 ? 1 : 0;
4332
+ data[i] = data[i] === 0 ? 1 : 0;
4157
4333
  }
4158
4334
  }
4159
4335
  function invertAlphaMask(dst) {
4160
- const len = dst.length;
4336
+ const data = dst.data;
4337
+ const len = data.length;
4161
4338
  for (let i = 0; i < len; i++) {
4162
- dst[i] = 255 - dst[i];
4339
+ data[i] = 255 - data[i];
4163
4340
  }
4164
4341
  }
4165
4342
 
4166
4343
  // src/Mask/mergeAlphaMasks.ts
4167
- function mergeAlphaMasks(dst, dstWidth, src, srcWidth, opts) {
4344
+ function mergeAlphaMasks(dst, src, opts) {
4168
4345
  const {
4169
4346
  x: targetX = 0,
4170
4347
  y: targetY = 0,
@@ -4175,15 +4352,17 @@ function mergeAlphaMasks(dst, dstWidth, src, srcWidth, opts) {
4175
4352
  my = 0,
4176
4353
  invertMask = false
4177
4354
  } = opts;
4178
- const dstHeight = dst.length / dstWidth | 0;
4179
- const srcHeight = src.length / srcWidth | 0;
4180
4355
  if (width <= 0) return;
4181
4356
  if (height <= 0) return;
4182
4357
  if (globalAlpha === 0) return;
4358
+ const dstData = dst.data;
4359
+ const srcData = src.data;
4360
+ const srcWidth = src.w;
4361
+ const dstWidth = dst.w;
4183
4362
  const startX = Math.max(0, -targetX, -mx);
4184
4363
  const startY = Math.max(0, -targetY, -my);
4185
4364
  const endX = Math.min(width, dstWidth - targetX, srcWidth - mx);
4186
- const endY = Math.min(height, dstHeight - targetY, srcHeight - my);
4365
+ const endY = Math.min(height, dst.h - targetY, src.h - my);
4187
4366
  if (startX >= endX) return;
4188
4367
  if (startY >= endY) return;
4189
4368
  for (let iy = startY; iy < endY; iy++) {
@@ -4192,7 +4371,7 @@ function mergeAlphaMasks(dst, dstWidth, src, srcWidth, opts) {
4192
4371
  let dIdx = dy * dstWidth + targetX + startX;
4193
4372
  let sIdx = sy * srcWidth + mx + startX;
4194
4373
  for (let ix = startX; ix < endX; ix++) {
4195
- const rawM = src[sIdx];
4374
+ const rawM = srcData[sIdx];
4196
4375
  const effectiveM = invertMask ? 255 - rawM : rawM;
4197
4376
  let weight = 0;
4198
4377
  if (effectiveM === 0) {
@@ -4206,13 +4385,13 @@ function mergeAlphaMasks(dst, dstWidth, src, srcWidth, opts) {
4206
4385
  }
4207
4386
  if (weight !== 255) {
4208
4387
  if (weight === 0) {
4209
- dst[dIdx] = 0;
4388
+ dstData[dIdx] = 0;
4210
4389
  } else {
4211
- const da = dst[dIdx];
4390
+ const da = dstData[dIdx];
4212
4391
  if (da === 255) {
4213
- dst[dIdx] = weight;
4392
+ dstData[dIdx] = weight;
4214
4393
  } else if (da !== 0) {
4215
- dst[dIdx] = da * weight + 128 >> 8;
4394
+ dstData[dIdx] = da * weight + 128 >> 8;
4216
4395
  }
4217
4396
  }
4218
4397
  }
@@ -4223,7 +4402,7 @@ function mergeAlphaMasks(dst, dstWidth, src, srcWidth, opts) {
4223
4402
  }
4224
4403
 
4225
4404
  // src/Mask/mergeBinaryMasks.ts
4226
- function mergeBinaryMasks(dst, dstWidth, src, srcWidth, opts) {
4405
+ function mergeBinaryMasks(dst, src, opts) {
4227
4406
  const {
4228
4407
  x: targetX = 0,
4229
4408
  y: targetY = 0,
@@ -4233,10 +4412,12 @@ function mergeBinaryMasks(dst, dstWidth, src, srcWidth, opts) {
4233
4412
  my = 0,
4234
4413
  invertMask = false
4235
4414
  } = opts;
4415
+ const dstData = dst.data;
4416
+ const srcData = src.data;
4417
+ const srcWidth = src.w;
4418
+ const dstWidth = dst.w;
4236
4419
  if (dstWidth <= 0) return;
4237
4420
  if (srcWidth <= 0) return;
4238
- const dstHeight = dst.length / dstWidth | 0;
4239
- const srcHeight = src.length / srcWidth | 0;
4240
4421
  let x = targetX;
4241
4422
  let y = targetY;
4242
4423
  let w = width;
@@ -4250,7 +4431,7 @@ function mergeBinaryMasks(dst, dstWidth, src, srcWidth, opts) {
4250
4431
  y = 0;
4251
4432
  }
4252
4433
  w = Math.min(w, dstWidth - x);
4253
- h = Math.min(h, dstHeight - y);
4434
+ h = Math.min(h, dst.h - y);
4254
4435
  if (w <= 0) return;
4255
4436
  if (h <= 0) return;
4256
4437
  const startX = mx + (x - targetX);
@@ -4258,7 +4439,7 @@ function mergeBinaryMasks(dst, dstWidth, src, srcWidth, opts) {
4258
4439
  const sX0 = Math.max(0, startX);
4259
4440
  const sY0 = Math.max(0, startY);
4260
4441
  const sX1 = Math.min(srcWidth, startX + w);
4261
- const sY1 = Math.min(srcHeight, startY + h);
4442
+ const sY1 = Math.min(src.h, startY + h);
4262
4443
  const finalW = sX1 - sX0;
4263
4444
  const finalH = sY1 - sY0;
4264
4445
  if (finalW <= 0) return;
@@ -4271,10 +4452,10 @@ function mergeBinaryMasks(dst, dstWidth, src, srcWidth, opts) {
4271
4452
  let sIdx = sY0 * srcWidth + sX0;
4272
4453
  for (let iy = 0; iy < finalH; iy++) {
4273
4454
  for (let ix = 0; ix < finalW; ix++) {
4274
- const mVal = src[sIdx];
4455
+ const mVal = srcData[sIdx];
4275
4456
  const isMaskedOut = invertMask ? mVal !== 0 : mVal === 0;
4276
4457
  if (isMaskedOut) {
4277
- dst[dIdx] = 0;
4458
+ dstData[dIdx] = 0;
4278
4459
  }
4279
4460
  dIdx++;
4280
4461
  sIdx++;
@@ -4284,6 +4465,177 @@ function mergeBinaryMasks(dst, dstWidth, src, srcWidth, opts) {
4284
4465
  }
4285
4466
  }
4286
4467
 
4468
+ // src/Mask/setMaskData.ts
4469
+ function setMaskData(mask, width, height, data) {
4470
+ ;
4471
+ mask.w = width;
4472
+ mask.h = height;
4473
+ mask.data = data;
4474
+ }
4475
+
4476
+ // src/MaskRect/subtractBinaryMaskRects.ts
4477
+ function subtractBinaryMaskRects(current, subtracting) {
4478
+ let result = [...current];
4479
+ for (const sub of subtracting) {
4480
+ const next = [];
4481
+ for (const r of result) {
4482
+ const ix = Math.max(r.x, sub.x);
4483
+ const iy = Math.max(r.y, sub.y);
4484
+ const ix2 = Math.min(r.x + r.w, sub.x + sub.w);
4485
+ const iy2 = Math.min(r.y + r.h, sub.y + sub.h);
4486
+ if (ix >= ix2 || iy >= iy2) {
4487
+ next.push(r);
4488
+ continue;
4489
+ }
4490
+ if (r.y < iy) pushPiece(next, r, r.x, r.y, r.w, iy - r.y);
4491
+ if (iy2 < r.y + r.h) pushPiece(next, r, r.x, iy2, r.w, r.y + r.h - iy2);
4492
+ if (r.x < ix) pushPiece(next, r, r.x, iy, ix - r.x, iy2 - iy);
4493
+ if (ix2 < r.x + r.w) pushPiece(next, r, ix2, iy, r.x + r.w - ix2, iy2 - iy);
4494
+ }
4495
+ result = next;
4496
+ }
4497
+ return result;
4498
+ }
4499
+ function pushPiece(dest, r, x, y, w, h) {
4500
+ if (r.data === null || r.data === void 0) {
4501
+ dest.push({
4502
+ x,
4503
+ y,
4504
+ w,
4505
+ h,
4506
+ data: null,
4507
+ type: null
4508
+ });
4509
+ return;
4510
+ }
4511
+ const lx = x - r.x;
4512
+ const ly = y - r.y;
4513
+ const data = new Uint8Array(w * h);
4514
+ for (let row = 0; row < h; row++) {
4515
+ data.set(r.data.subarray((ly + row) * r.w + lx, (ly + row) * r.w + lx + w), row * w);
4516
+ }
4517
+ dest.push({
4518
+ x,
4519
+ y,
4520
+ w,
4521
+ h,
4522
+ data,
4523
+ type: 1 /* BINARY */
4524
+ });
4525
+ }
4526
+
4527
+ // src/Rect/getRectsBounds.ts
4528
+ function getRectsBounds(rects) {
4529
+ if (rects.length === 1) return {
4530
+ ...rects[0]
4531
+ };
4532
+ let minX = Infinity, minY = Infinity;
4533
+ let maxX = -Infinity, maxY = -Infinity;
4534
+ for (let i = 0; i < rects.length; i++) {
4535
+ const r = rects[i];
4536
+ const x1 = r.x;
4537
+ const y1 = r.y;
4538
+ const x2 = x1 + r.w;
4539
+ const y2 = y1 + r.h;
4540
+ if (x1 < minX) minX = x1;
4541
+ if (y1 < minY) minY = y1;
4542
+ if (x2 > maxX) maxX = x2;
4543
+ if (y2 > maxY) maxY = y2;
4544
+ }
4545
+ return {
4546
+ x: minX,
4547
+ y: minY,
4548
+ w: maxX - minX,
4549
+ h: maxY - minY
4550
+ };
4551
+ }
4552
+
4553
+ // src/MaskRect/merge2BinaryMaskRects.ts
4554
+ function merge2BinaryMaskRects(a, b) {
4555
+ const bounds = getRectsBounds([a, b]);
4556
+ if ((a.data === null || a.data === void 0) && (b.data === null || b.data === void 0)) {
4557
+ const ix = Math.max(a.x, b.x);
4558
+ const iy = Math.max(a.y, b.y);
4559
+ const ir = Math.min(a.x + a.w, b.x + b.w);
4560
+ const ib = Math.min(a.y + a.h, b.y + b.h);
4561
+ const iw = Math.max(0, ir - ix);
4562
+ const ih = Math.max(0, ib - iy);
4563
+ const intersectionArea = iw * ih;
4564
+ const areaA = a.w * a.h;
4565
+ const areaB = b.w * b.h;
4566
+ const boundsArea = bounds.w * bounds.h;
4567
+ if (boundsArea === areaA + areaB - intersectionArea) {
4568
+ return {
4569
+ ...bounds,
4570
+ data: null,
4571
+ type: null
4572
+ };
4573
+ }
4574
+ }
4575
+ const maskData = new Uint8Array(bounds.w * bounds.h);
4576
+ const aOffY = a.y - bounds.y;
4577
+ const aOffX = a.x - bounds.x;
4578
+ if (a.data === void 0 || a.data === null) {
4579
+ for (let ay = 0; ay < a.h; ay++) {
4580
+ const destRow = (aOffY + ay) * bounds.w + aOffX;
4581
+ maskData.fill(1, destRow, destRow + a.w);
4582
+ }
4583
+ } else {
4584
+ for (let ay = 0; ay < a.h; ay++) {
4585
+ const srcRow = ay * a.w;
4586
+ const destRow = (aOffY + ay) * bounds.w + aOffX;
4587
+ maskData.set(a.data.subarray(srcRow, srcRow + a.w), destRow);
4588
+ }
4589
+ }
4590
+ const bOffY = b.y - bounds.y;
4591
+ const bOffX = b.x - bounds.x;
4592
+ if (b.data === void 0 || b.data === null) {
4593
+ for (let by = 0; by < b.h; by++) {
4594
+ const destRow = (bOffY + by) * bounds.w + bOffX;
4595
+ maskData.fill(1, destRow, destRow + b.w);
4596
+ }
4597
+ } else {
4598
+ for (let by = 0; by < b.h; by++) {
4599
+ const srcRow = by * b.w;
4600
+ const destRow = (bOffY + by) * bounds.w + bOffX;
4601
+ for (let bx = 0; bx < b.w; bx++) {
4602
+ maskData[destRow + bx] |= b.data[srcRow + bx];
4603
+ }
4604
+ }
4605
+ }
4606
+ return {
4607
+ ...bounds,
4608
+ data: maskData,
4609
+ type: 1 /* BINARY */
4610
+ };
4611
+ }
4612
+
4613
+ // src/MaskRect/mergeBinaryMaskRects.ts
4614
+ function mergeBinaryMaskRects(current, adding) {
4615
+ const rects = [...current, ...adding];
4616
+ let changed = true;
4617
+ while (changed) {
4618
+ changed = false;
4619
+ const next = [];
4620
+ for (const r of rects) {
4621
+ let merged = false;
4622
+ for (let i = 0; i < next.length; i++) {
4623
+ const n = next[i];
4624
+ 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;
4625
+ if (overlap) {
4626
+ next[i] = merge2BinaryMaskRects(n, r);
4627
+ merged = true;
4628
+ changed = true;
4629
+ break;
4630
+ }
4631
+ }
4632
+ if (!merged) next.push(r);
4633
+ }
4634
+ rects.splice(0, rects.length, ...next);
4635
+ }
4636
+ return rects;
4637
+ }
4638
+
4287
4639
  // src/PixelData/PixelData.ts
4288
4640
  var PixelData = class _PixelData {
4289
4641
  data32;
@@ -4335,7 +4687,6 @@ function blendPixelDataAlphaMask(dst, src, alphaMask, opts = {}) {
4335
4687
  h: height = src.height,
4336
4688
  alpha: globalAlpha = 255,
4337
4689
  blendFn = sourceOverPerfect,
4338
- mw = src.width,
4339
4690
  mx = 0,
4340
4691
  my = 0,
4341
4692
  invertMask = false
@@ -4374,7 +4725,8 @@ function blendPixelDataAlphaMask(dst, src, alphaMask, opts = {}) {
4374
4725
  if (actualW <= 0 || actualH <= 0) return;
4375
4726
  const dw = dst.width;
4376
4727
  const sw = src.width;
4377
- const mPitch = mw;
4728
+ const mPitch = alphaMask.w;
4729
+ const maskData = alphaMask.data;
4378
4730
  const dx = x - targetX | 0;
4379
4731
  const dy = y - targetY | 0;
4380
4732
  const dst32 = dst.data32;
@@ -4389,7 +4741,7 @@ function blendPixelDataAlphaMask(dst, src, alphaMask, opts = {}) {
4389
4741
  const isOverwrite = blendFn.isOverwrite || false;
4390
4742
  for (let iy = 0; iy < actualH; iy++) {
4391
4743
  for (let ix = 0; ix < actualW; ix++) {
4392
- const mVal = alphaMask[mIdx];
4744
+ const mVal = maskData[mIdx];
4393
4745
  const effM = invertMask ? 255 - mVal : mVal;
4394
4746
  if (effM === 0) {
4395
4747
  dIdx++;
@@ -4450,7 +4802,6 @@ function blendPixelDataBinaryMask(dst, src, binaryMask, opts) {
4450
4802
  h: height = src.height,
4451
4803
  alpha: globalAlpha = 255,
4452
4804
  blendFn = sourceOverPerfect,
4453
- mw = src.width,
4454
4805
  mx = 0,
4455
4806
  my = 0,
4456
4807
  invertMask = false
@@ -4493,7 +4844,8 @@ function blendPixelDataBinaryMask(dst, src, binaryMask, opts) {
4493
4844
  const src32 = src.data32;
4494
4845
  const dw = dst.width;
4495
4846
  const sw = src.width;
4496
- const mPitch = mw;
4847
+ const mPitch = binaryMask.w;
4848
+ const maskData = binaryMask.data;
4497
4849
  let dIdx = y * dw + x | 0;
4498
4850
  let sIdx = sy * sw + sx | 0;
4499
4851
  let mIdx = (my + dy) * mPitch + (mx + dx) | 0;
@@ -4505,7 +4857,7 @@ function blendPixelDataBinaryMask(dst, src, binaryMask, opts) {
4505
4857
  const isOverwrite = blendFn.isOverwrite || false;
4506
4858
  for (let iy = 0; iy < actualH; iy++) {
4507
4859
  for (let ix = 0; ix < actualW; ix++) {
4508
- if (binaryMask[mIdx] === skipVal) {
4860
+ if (maskData[mIdx] === skipVal) {
4509
4861
  dIdx++;
4510
4862
  sIdx++;
4511
4863
  mIdx++;
@@ -4633,10 +4985,11 @@ function pixelDataToAlphaMask(pixelData) {
4633
4985
  height
4634
4986
  } = pixelData;
4635
4987
  const len = data32.length;
4636
- const mask = new Uint8Array(width * height);
4988
+ const mask = makeAlphaMask(width, height);
4989
+ const maskData = mask.data;
4637
4990
  for (let i = 0; i < len; i++) {
4638
4991
  const val = data32[i];
4639
- mask[i] = val >>> 24 & 255;
4992
+ maskData[i] = val >>> 24 & 255;
4640
4993
  }
4641
4994
  return mask;
4642
4995
  }
@@ -4818,11 +5171,13 @@ function writePixelDataBuffer(target, data, _x, _y, _w, _h) {
4818
5171
  exclusionPerfect,
4819
5172
  extractImageDataBuffer,
4820
5173
  extractMask,
5174
+ extractMaskBuffer,
4821
5175
  extractPixelData,
4822
5176
  extractPixelDataBuffer,
4823
5177
  fileInputChangeToImageData,
4824
5178
  fileToImageData,
4825
5179
  fillPixelData,
5180
+ fillPixelDataBinaryMask,
4826
5181
  floodFillSelection,
4827
5182
  forEachLinePoint,
4828
5183
  getCircleBrushOrPencilBounds,
@@ -4831,12 +5186,13 @@ function writePixelDataBuffer(target, data, _x, _y, _w, _h) {
4831
5186
  getIndexedImageColorCounts,
4832
5187
  getRectBrushOrPencilBounds,
4833
5188
  getRectBrushOrPencilStrokeBounds,
5189
+ getRectsBounds,
4834
5190
  getSupportedPixelFormats,
4835
5191
  hardLightFast,
4836
5192
  hardLightPerfect,
4837
5193
  hardMixFast,
4838
5194
  hardMixPerfect,
4839
- imageDataToAlphaMask,
5195
+ imageDataToAlphaMaskBuffer,
4840
5196
  imageDataToDataUrl,
4841
5197
  imageDataToImgBlob,
4842
5198
  imageDataToUInt32Array,
@@ -4859,7 +5215,11 @@ function writePixelDataBuffer(target, data, _x, _y, _w, _h) {
4859
5215
  linearDodgePerfect,
4860
5216
  linearLightFast,
4861
5217
  linearLightPerfect,
5218
+ makeAlphaMask,
5219
+ makeBinaryMask,
4862
5220
  makeBlendModeRegistry,
5221
+ makeCircleBrushAlphaMask,
5222
+ makeCircleBrushBinaryMask,
4863
5223
  makeFastBlendModeRegistry,
4864
5224
  makeFullPixelMutator,
4865
5225
  makeImageDataLike,
@@ -4867,7 +5227,9 @@ function writePixelDataBuffer(target, data, _x, _y, _w, _h) {
4867
5227
  makePixelCanvas,
4868
5228
  makeReusableCanvas,
4869
5229
  makeReusableImageData,
5230
+ merge2BinaryMaskRects,
4870
5231
  mergeAlphaMasks,
5232
+ mergeBinaryMaskRects,
4871
5233
  mergeBinaryMasks,
4872
5234
  multiplyFast,
4873
5235
  multiplyPerfect,
@@ -4886,6 +5248,7 @@ function writePixelDataBuffer(target, data, _x, _y, _w, _h) {
4886
5248
  mutatorBlendPixelData,
4887
5249
  mutatorClear,
4888
5250
  mutatorFill,
5251
+ mutatorFillBinaryMask,
4889
5252
  mutatorInvert,
4890
5253
  overlayFast,
4891
5254
  overlayPerfect,
@@ -4908,10 +5271,12 @@ function writePixelDataBuffer(target, data, _x, _y, _w, _h) {
4908
5271
  screenPerfect,
4909
5272
  serializeImageData,
4910
5273
  serializeNullableImageData,
5274
+ setMaskData,
4911
5275
  softLightFast,
4912
5276
  softLightPerfect,
4913
5277
  sourceOverFast,
4914
5278
  sourceOverPerfect,
5279
+ subtractBinaryMaskRects,
4915
5280
  subtractFast,
4916
5281
  subtractPerfect,
4917
5282
  toBlendModeIndexAndName,