pixel-data-js 0.21.0 → 0.22.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/dist/index.dev.cjs +725 -362
  2. package/dist/index.dev.cjs.map +1 -1
  3. package/dist/index.dev.js +712 -361
  4. package/dist/index.dev.js.map +1 -1
  5. package/dist/index.prod.cjs +725 -362
  6. package/dist/index.prod.cjs.map +1 -1
  7. package/dist/index.prod.d.ts +282 -127
  8. package/dist/index.prod.js +712 -361
  9. package/dist/index.prod.js.map +1 -1
  10. package/package.json +1 -1
  11. package/src/Algorithm/floodFillSelection.ts +12 -14
  12. package/src/BlendModes/toBlendModeIndexAndName.ts +0 -7
  13. package/src/Clipboard/writeImgBlobToClipboard.ts +1 -1
  14. package/src/History/PixelMutator/mutatorApplyAlphaMask.ts +3 -0
  15. package/src/History/PixelMutator/mutatorApplyBinaryMask.ts +3 -0
  16. package/src/History/PixelMutator/mutatorApplyCircleBrush.ts +25 -6
  17. package/src/History/PixelMutator/mutatorApplyCircleBrushStroke.ts +89 -46
  18. package/src/History/PixelMutator/mutatorApplyCirclePencil.ts +7 -7
  19. package/src/History/PixelMutator/mutatorApplyCirclePencilStroke.ts +81 -41
  20. package/src/History/PixelMutator/mutatorApplyRectBrush.ts +3 -0
  21. package/src/History/PixelMutator/mutatorApplyRectBrushStroke.ts +18 -5
  22. package/src/History/PixelMutator/mutatorApplyRectPencil.ts +3 -0
  23. package/src/History/PixelMutator/mutatorApplyRectPencilStroke.ts +19 -4
  24. package/src/History/PixelMutator/mutatorBlendColor.ts +4 -0
  25. package/src/History/PixelMutator/mutatorBlendPixelData.ts +4 -0
  26. package/src/History/PixelMutator/mutatorClear.ts +10 -8
  27. package/src/History/PixelMutator/mutatorFill.ts +7 -4
  28. package/src/History/PixelMutator/mutatorFillBinaryMask.ts +28 -0
  29. package/src/History/PixelMutator/mutatorInvert.ts +3 -0
  30. package/src/ImageData/extractImageDataBuffer.ts +3 -3
  31. package/src/ImageData/{imageDataToAlphaMask.ts → imageDataToAlphaMaskBuffer.ts} +3 -4
  32. package/src/ImageData/resizeImageData.ts +3 -5
  33. package/src/ImageData/writeImageDataBuffer.ts +7 -7
  34. package/src/Mask/AlphaMask.ts +16 -0
  35. package/src/Mask/BinaryMask.ts +16 -0
  36. package/src/Mask/CircleBrushAlphaMask.ts +32 -0
  37. package/src/Mask/CircleBrushBinaryMask.ts +30 -0
  38. package/src/Mask/applyBinaryMaskToAlphaMask.ts +12 -9
  39. package/src/Mask/copyMask.ts +9 -3
  40. package/src/Mask/extractMask.ts +33 -31
  41. package/src/Mask/extractMaskBuffer.ts +87 -0
  42. package/src/Mask/invertMask.ts +6 -4
  43. package/src/Mask/mergeAlphaMasks.ts +11 -10
  44. package/src/Mask/mergeBinaryMasks.ts +10 -9
  45. package/src/Mask/setMaskData.ts +7 -0
  46. package/src/MaskRect/merge2BinaryMaskRects.ts +81 -0
  47. package/src/MaskRect/mergeBinaryMaskRects.ts +39 -0
  48. package/src/MaskRect/subtractBinaryMaskRects.ts +80 -0
  49. package/src/PixelData/applyAlphaMaskToPixelData.ts +8 -5
  50. package/src/PixelData/applyBinaryMaskToPixelData.ts +8 -9
  51. package/src/PixelData/applyCircleBrushToPixelData.ts +54 -62
  52. package/src/PixelData/blendColorPixelDataAlphaMask.ts +35 -25
  53. package/src/PixelData/blendColorPixelDataBinaryMask.ts +26 -19
  54. package/src/PixelData/blendPixelDataAlphaMask.ts +3 -3
  55. package/src/PixelData/blendPixelDataBinaryMask.ts +3 -3
  56. package/src/PixelData/fillPixelData.ts +15 -42
  57. package/src/PixelData/fillPixelDataBinaryMask.ts +79 -0
  58. package/src/PixelData/invertPixelData.ts +3 -3
  59. package/src/PixelData/pixelDataToAlphaMask.ts +4 -2
  60. package/src/PixelData/writePixelDataBuffer.ts +2 -3
  61. package/src/Rect/getRectsBounds.ts +22 -0
  62. package/src/Rect/trimRectBounds.ts +20 -17
  63. package/src/_types.ts +55 -29
  64. package/src/index.ts +16 -1
@@ -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,15 +3320,12 @@ 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
- mask = void 0
3331
- } = 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;
3332
3327
  writer.accumulator.storeRegionBeforeState(x, y, w, h);
3333
- fillPixelData2(writer.target, 0, x, y, w, h, mask);
3328
+ fillPixelData2(writer.target, 0, x, y, w, h);
3334
3329
  }
3335
3330
  };
3336
3331
  });
@@ -3349,11 +3344,10 @@ var mutatorFill = ((writer, deps = defaults13) => {
3349
3344
  x = 0,
3350
3345
  y = 0,
3351
3346
  w = writer.target.width,
3352
- h = writer.target.height,
3353
- mask = void 0
3347
+ h = writer.target.height
3354
3348
  } = rect;
3355
3349
  writer.accumulator.storeRegionBeforeState(x, y, w, h);
3356
- fillPixelData2(writer.target, color, x, y, w, h, mask);
3350
+ fillPixelData2(writer.target, color, x, y, w, h);
3357
3351
  }
3358
3352
  };
3359
3353
  });
@@ -3368,7 +3362,6 @@ function invertPixelData(pixelData, opts = {}) {
3368
3362
  w: width = pixelData.width,
3369
3363
  h: height = pixelData.height,
3370
3364
  mask,
3371
- mw,
3372
3365
  mx = 0,
3373
3366
  my = 0,
3374
3367
  invertMask = false
@@ -3383,7 +3376,7 @@ function invertPixelData(pixelData, opts = {}) {
3383
3376
  } = clip;
3384
3377
  const dst32 = dst.data32;
3385
3378
  const dw = dst.width;
3386
- const mPitch = mw ?? width;
3379
+ const mPitch = mask?.w ?? width;
3387
3380
  const dx = x - targetX;
3388
3381
  const dy = y - targetY;
3389
3382
  let dIdx = y * dw + x;
@@ -3391,9 +3384,10 @@ function invertPixelData(pixelData, opts = {}) {
3391
3384
  const dStride = dw - actualW;
3392
3385
  const mStride = mPitch - actualW;
3393
3386
  if (mask) {
3387
+ const maskData = mask.data;
3394
3388
  for (let iy = 0; iy < actualH; iy++) {
3395
3389
  for (let ix = 0; ix < actualW; ix++) {
3396
- const mVal = mask[mIdx];
3390
+ const mVal = maskData[mIdx];
3397
3391
  const isHit = invertMask ? mVal === 0 : mVal === 1;
3398
3392
  if (isHit) {
3399
3393
  dst32[dIdx] = dst32[dIdx] ^ 16777215;
@@ -3504,14 +3498,12 @@ var PixelWriter = class {
3504
3498
  // src/History/PixelMutator/mutatorApplyCirclePencil.ts
3505
3499
  var defaults15 = {
3506
3500
  applyCircleBrushToPixelData,
3507
- getCircleBrushOrPencilBounds,
3508
- fallOff: () => 1
3501
+ getCircleBrushOrPencilBounds
3509
3502
  };
3510
3503
  var mutatorApplyCirclePencil = ((writer, deps = defaults15) => {
3511
3504
  const {
3512
3505
  applyCircleBrushToPixelData: applyCircleBrushToPixelData2 = defaults15.applyCircleBrushToPixelData,
3513
- getCircleBrushOrPencilBounds: getCircleBrushOrPencilBounds2 = defaults15.getCircleBrushOrPencilBounds,
3514
- fallOff = defaults15.fallOff
3506
+ getCircleBrushOrPencilBounds: getCircleBrushOrPencilBounds2 = defaults15.getCircleBrushOrPencilBounds
3515
3507
  } = deps;
3516
3508
  const boundsOut = {
3517
3509
  x: 0,
@@ -3520,8 +3512,8 @@ var mutatorApplyCirclePencil = ((writer, deps = defaults15) => {
3520
3512
  h: 0
3521
3513
  };
3522
3514
  return {
3523
- applyCirclePencil(color, centerX, centerY, brushSize, alpha = 255, blendFn) {
3524
- 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);
3525
3517
  const {
3526
3518
  x,
3527
3519
  y,
@@ -3529,7 +3521,63 @@ var mutatorApplyCirclePencil = ((writer, deps = defaults15) => {
3529
3521
  h
3530
3522
  } = bounds;
3531
3523
  writer.accumulator.storeRegionBeforeState(x, y, w, h);
3532
- 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);
3533
3581
  }
3534
3582
  };
3535
3583
  });
@@ -3565,8 +3613,8 @@ function makeImageDataLike(width, height, data) {
3565
3613
  };
3566
3614
  }
3567
3615
 
3568
- // src/ImageData/imageDataToAlphaMask.ts
3569
- function imageDataToAlphaMask(imageData) {
3616
+ // src/ImageData/imageDataToAlphaMaskBuffer.ts
3617
+ function imageDataToAlphaMaskBuffer(imageData) {
3570
3618
  const {
3571
3619
  width,
3572
3620
  height,
@@ -3656,13 +3704,13 @@ function resampleImageData(source, factor) {
3656
3704
  }
3657
3705
 
3658
3706
  // src/ImageData/resizeImageData.ts
3659
- function resizeImageData(current, newWidth, newHeight, offsetX = 0, offsetY = 0) {
3707
+ function resizeImageData(target, newWidth, newHeight, offsetX = 0, offsetY = 0) {
3660
3708
  const result = new ImageData(newWidth, newHeight);
3661
3709
  const {
3662
3710
  width: oldW,
3663
3711
  height: oldH,
3664
3712
  data: oldData
3665
- } = current;
3713
+ } = target;
3666
3714
  const newData = result.data;
3667
3715
  const x0 = Math.max(0, offsetX);
3668
3716
  const y0 = Math.max(0, offsetY);
@@ -3817,7 +3865,7 @@ function writeImageData(target, source, x, y, sx = 0, sy = 0, sw = source.width,
3817
3865
 
3818
3866
  // src/ImageData/writeImageDataBuffer.ts
3819
3867
  var SCRATCH_BLIT3 = makeClippedBlit();
3820
- function writeImageDataBuffer(imageData, data, _x, _y, _w, _h) {
3868
+ function writeImageDataBuffer(target, data, _x, _y, _w, _h) {
3821
3869
  const {
3822
3870
  x,
3823
3871
  y,
@@ -3833,7 +3881,7 @@ function writeImageDataBuffer(imageData, data, _x, _y, _w, _h) {
3833
3881
  width: dstW,
3834
3882
  height: dstH,
3835
3883
  data: dst
3836
- } = imageData;
3884
+ } = target;
3837
3885
  const clip = resolveBlitClipping(x, y, 0, 0, w, h, dstW, dstH, w, h, SCRATCH_BLIT3);
3838
3886
  if (!clip.inBounds) return;
3839
3887
  const {
@@ -4079,8 +4127,84 @@ async function getSupportedPixelFormats(rasterMimes = defaultRasterMimes) {
4079
4127
  return formatsPromise;
4080
4128
  }
4081
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
+
4082
4206
  // src/Mask/applyBinaryMaskToAlphaMask.ts
4083
- function applyBinaryMaskToAlphaMask(alphaMaskDst, dstWidth, binaryMaskSrc, srcWidth, opts = {}) {
4207
+ function applyBinaryMaskToAlphaMask(alphaMaskDst, binaryMaskSrc, opts = {}) {
4084
4208
  const {
4085
4209
  x: targetX = 0,
4086
4210
  y: targetY = 0,
@@ -4090,11 +4214,13 @@ function applyBinaryMaskToAlphaMask(alphaMaskDst, dstWidth, binaryMaskSrc, srcWi
4090
4214
  my = 0,
4091
4215
  invertMask = false
4092
4216
  } = opts;
4217
+ const dstWidth = alphaMaskDst.w;
4093
4218
  if (dstWidth <= 0) return;
4094
- if (binaryMaskSrc.length === 0) return;
4219
+ if (binaryMaskSrc.data.length === 0) return;
4220
+ const srcWidth = binaryMaskSrc.w;
4095
4221
  if (srcWidth <= 0) return;
4096
- const dstHeight = alphaMaskDst.length / dstWidth | 0;
4097
- const srcHeight = binaryMaskSrc.length / srcWidth | 0;
4222
+ const dstHeight = alphaMaskDst.data.length / dstWidth | 0;
4223
+ const srcHeight = binaryMaskSrc.data.length / srcWidth | 0;
4098
4224
  if (dstHeight <= 0) return;
4099
4225
  if (srcHeight <= 0) return;
4100
4226
  const dstX0 = Math.max(0, targetX);
@@ -4111,6 +4237,8 @@ function applyBinaryMaskToAlphaMask(alphaMaskDst, dstWidth, binaryMaskSrc, srcWi
4111
4237
  if (srcY0 + (dstY1 - dstY0) <= 0) return;
4112
4238
  const iterW = Math.min(dstX1 - dstX0, srcWidth - srcX0);
4113
4239
  const iterH = Math.min(dstY1 - dstY0, srcHeight - srcY0);
4240
+ const srcData = binaryMaskSrc.data;
4241
+ const dstData = alphaMaskDst.data;
4114
4242
  let dstIdx = dstY0 * dstWidth + dstX0;
4115
4243
  let srcIdx = srcY0 * srcWidth + srcX0;
4116
4244
  if (invertMask) {
@@ -4119,8 +4247,8 @@ function applyBinaryMaskToAlphaMask(alphaMaskDst, dstWidth, binaryMaskSrc, srcWi
4119
4247
  let d = dstIdx;
4120
4248
  let s = srcIdx;
4121
4249
  while (d < dstEnd) {
4122
- if (binaryMaskSrc[s] !== 0) {
4123
- alphaMaskDst[d] = 0;
4250
+ if (srcData[s] !== 0) {
4251
+ dstData[d] = 0;
4124
4252
  }
4125
4253
  d++;
4126
4254
  s++;
@@ -4134,8 +4262,8 @@ function applyBinaryMaskToAlphaMask(alphaMaskDst, dstWidth, binaryMaskSrc, srcWi
4134
4262
  let d = dstIdx;
4135
4263
  let s = srcIdx;
4136
4264
  while (d < dstEnd) {
4137
- if (binaryMaskSrc[s] === 0) {
4138
- alphaMaskDst[d] = 0;
4265
+ if (srcData[s] === 0) {
4266
+ dstData[d] = 0;
4139
4267
  }
4140
4268
  d++;
4141
4269
  s++;
@@ -4148,25 +4276,72 @@ function applyBinaryMaskToAlphaMask(alphaMaskDst, dstWidth, binaryMaskSrc, srcWi
4148
4276
 
4149
4277
  // src/Mask/copyMask.ts
4150
4278
  function copyMask(src) {
4151
- 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;
4152
4325
  }
4153
4326
 
4154
4327
  // src/Mask/invertMask.ts
4155
4328
  function invertBinaryMask(dst) {
4156
- const len = dst.length;
4329
+ const data = dst.data;
4330
+ const len = data.length;
4157
4331
  for (let i = 0; i < len; i++) {
4158
- dst[i] = dst[i] === 0 ? 1 : 0;
4332
+ data[i] = data[i] === 0 ? 1 : 0;
4159
4333
  }
4160
4334
  }
4161
4335
  function invertAlphaMask(dst) {
4162
- const len = dst.length;
4336
+ const data = dst.data;
4337
+ const len = data.length;
4163
4338
  for (let i = 0; i < len; i++) {
4164
- dst[i] = 255 - dst[i];
4339
+ data[i] = 255 - data[i];
4165
4340
  }
4166
4341
  }
4167
4342
 
4168
4343
  // src/Mask/mergeAlphaMasks.ts
4169
- function mergeAlphaMasks(dst, dstWidth, src, srcWidth, opts) {
4344
+ function mergeAlphaMasks(dst, src, opts) {
4170
4345
  const {
4171
4346
  x: targetX = 0,
4172
4347
  y: targetY = 0,
@@ -4177,15 +4352,17 @@ function mergeAlphaMasks(dst, dstWidth, src, srcWidth, opts) {
4177
4352
  my = 0,
4178
4353
  invertMask = false
4179
4354
  } = opts;
4180
- const dstHeight = dst.length / dstWidth | 0;
4181
- const srcHeight = src.length / srcWidth | 0;
4182
4355
  if (width <= 0) return;
4183
4356
  if (height <= 0) return;
4184
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;
4185
4362
  const startX = Math.max(0, -targetX, -mx);
4186
4363
  const startY = Math.max(0, -targetY, -my);
4187
4364
  const endX = Math.min(width, dstWidth - targetX, srcWidth - mx);
4188
- const endY = Math.min(height, dstHeight - targetY, srcHeight - my);
4365
+ const endY = Math.min(height, dst.h - targetY, src.h - my);
4189
4366
  if (startX >= endX) return;
4190
4367
  if (startY >= endY) return;
4191
4368
  for (let iy = startY; iy < endY; iy++) {
@@ -4194,7 +4371,7 @@ function mergeAlphaMasks(dst, dstWidth, src, srcWidth, opts) {
4194
4371
  let dIdx = dy * dstWidth + targetX + startX;
4195
4372
  let sIdx = sy * srcWidth + mx + startX;
4196
4373
  for (let ix = startX; ix < endX; ix++) {
4197
- const rawM = src[sIdx];
4374
+ const rawM = srcData[sIdx];
4198
4375
  const effectiveM = invertMask ? 255 - rawM : rawM;
4199
4376
  let weight = 0;
4200
4377
  if (effectiveM === 0) {
@@ -4208,13 +4385,13 @@ function mergeAlphaMasks(dst, dstWidth, src, srcWidth, opts) {
4208
4385
  }
4209
4386
  if (weight !== 255) {
4210
4387
  if (weight === 0) {
4211
- dst[dIdx] = 0;
4388
+ dstData[dIdx] = 0;
4212
4389
  } else {
4213
- const da = dst[dIdx];
4390
+ const da = dstData[dIdx];
4214
4391
  if (da === 255) {
4215
- dst[dIdx] = weight;
4392
+ dstData[dIdx] = weight;
4216
4393
  } else if (da !== 0) {
4217
- dst[dIdx] = da * weight + 128 >> 8;
4394
+ dstData[dIdx] = da * weight + 128 >> 8;
4218
4395
  }
4219
4396
  }
4220
4397
  }
@@ -4225,7 +4402,7 @@ function mergeAlphaMasks(dst, dstWidth, src, srcWidth, opts) {
4225
4402
  }
4226
4403
 
4227
4404
  // src/Mask/mergeBinaryMasks.ts
4228
- function mergeBinaryMasks(dst, dstWidth, src, srcWidth, opts) {
4405
+ function mergeBinaryMasks(dst, src, opts) {
4229
4406
  const {
4230
4407
  x: targetX = 0,
4231
4408
  y: targetY = 0,
@@ -4235,10 +4412,12 @@ function mergeBinaryMasks(dst, dstWidth, src, srcWidth, opts) {
4235
4412
  my = 0,
4236
4413
  invertMask = false
4237
4414
  } = opts;
4415
+ const dstData = dst.data;
4416
+ const srcData = src.data;
4417
+ const srcWidth = src.w;
4418
+ const dstWidth = dst.w;
4238
4419
  if (dstWidth <= 0) return;
4239
4420
  if (srcWidth <= 0) return;
4240
- const dstHeight = dst.length / dstWidth | 0;
4241
- const srcHeight = src.length / srcWidth | 0;
4242
4421
  let x = targetX;
4243
4422
  let y = targetY;
4244
4423
  let w = width;
@@ -4252,7 +4431,7 @@ function mergeBinaryMasks(dst, dstWidth, src, srcWidth, opts) {
4252
4431
  y = 0;
4253
4432
  }
4254
4433
  w = Math.min(w, dstWidth - x);
4255
- h = Math.min(h, dstHeight - y);
4434
+ h = Math.min(h, dst.h - y);
4256
4435
  if (w <= 0) return;
4257
4436
  if (h <= 0) return;
4258
4437
  const startX = mx + (x - targetX);
@@ -4260,7 +4439,7 @@ function mergeBinaryMasks(dst, dstWidth, src, srcWidth, opts) {
4260
4439
  const sX0 = Math.max(0, startX);
4261
4440
  const sY0 = Math.max(0, startY);
4262
4441
  const sX1 = Math.min(srcWidth, startX + w);
4263
- const sY1 = Math.min(srcHeight, startY + h);
4442
+ const sY1 = Math.min(src.h, startY + h);
4264
4443
  const finalW = sX1 - sX0;
4265
4444
  const finalH = sY1 - sY0;
4266
4445
  if (finalW <= 0) return;
@@ -4273,10 +4452,10 @@ function mergeBinaryMasks(dst, dstWidth, src, srcWidth, opts) {
4273
4452
  let sIdx = sY0 * srcWidth + sX0;
4274
4453
  for (let iy = 0; iy < finalH; iy++) {
4275
4454
  for (let ix = 0; ix < finalW; ix++) {
4276
- const mVal = src[sIdx];
4455
+ const mVal = srcData[sIdx];
4277
4456
  const isMaskedOut = invertMask ? mVal !== 0 : mVal === 0;
4278
4457
  if (isMaskedOut) {
4279
- dst[dIdx] = 0;
4458
+ dstData[dIdx] = 0;
4280
4459
  }
4281
4460
  dIdx++;
4282
4461
  sIdx++;
@@ -4286,6 +4465,177 @@ function mergeBinaryMasks(dst, dstWidth, src, srcWidth, opts) {
4286
4465
  }
4287
4466
  }
4288
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
+
4289
4639
  // src/PixelData/PixelData.ts
4290
4640
  var PixelData = class _PixelData {
4291
4641
  data32;
@@ -4337,7 +4687,6 @@ function blendPixelDataAlphaMask(dst, src, alphaMask, opts = {}) {
4337
4687
  h: height = src.height,
4338
4688
  alpha: globalAlpha = 255,
4339
4689
  blendFn = sourceOverPerfect,
4340
- mw = src.width,
4341
4690
  mx = 0,
4342
4691
  my = 0,
4343
4692
  invertMask = false
@@ -4376,7 +4725,8 @@ function blendPixelDataAlphaMask(dst, src, alphaMask, opts = {}) {
4376
4725
  if (actualW <= 0 || actualH <= 0) return;
4377
4726
  const dw = dst.width;
4378
4727
  const sw = src.width;
4379
- const mPitch = mw;
4728
+ const mPitch = alphaMask.w;
4729
+ const maskData = alphaMask.data;
4380
4730
  const dx = x - targetX | 0;
4381
4731
  const dy = y - targetY | 0;
4382
4732
  const dst32 = dst.data32;
@@ -4391,7 +4741,7 @@ function blendPixelDataAlphaMask(dst, src, alphaMask, opts = {}) {
4391
4741
  const isOverwrite = blendFn.isOverwrite || false;
4392
4742
  for (let iy = 0; iy < actualH; iy++) {
4393
4743
  for (let ix = 0; ix < actualW; ix++) {
4394
- const mVal = alphaMask[mIdx];
4744
+ const mVal = maskData[mIdx];
4395
4745
  const effM = invertMask ? 255 - mVal : mVal;
4396
4746
  if (effM === 0) {
4397
4747
  dIdx++;
@@ -4452,7 +4802,6 @@ function blendPixelDataBinaryMask(dst, src, binaryMask, opts) {
4452
4802
  h: height = src.height,
4453
4803
  alpha: globalAlpha = 255,
4454
4804
  blendFn = sourceOverPerfect,
4455
- mw = src.width,
4456
4805
  mx = 0,
4457
4806
  my = 0,
4458
4807
  invertMask = false
@@ -4495,7 +4844,8 @@ function blendPixelDataBinaryMask(dst, src, binaryMask, opts) {
4495
4844
  const src32 = src.data32;
4496
4845
  const dw = dst.width;
4497
4846
  const sw = src.width;
4498
- const mPitch = mw;
4847
+ const mPitch = binaryMask.w;
4848
+ const maskData = binaryMask.data;
4499
4849
  let dIdx = y * dw + x | 0;
4500
4850
  let sIdx = sy * sw + sx | 0;
4501
4851
  let mIdx = (my + dy) * mPitch + (mx + dx) | 0;
@@ -4507,7 +4857,7 @@ function blendPixelDataBinaryMask(dst, src, binaryMask, opts) {
4507
4857
  const isOverwrite = blendFn.isOverwrite || false;
4508
4858
  for (let iy = 0; iy < actualH; iy++) {
4509
4859
  for (let ix = 0; ix < actualW; ix++) {
4510
- if (binaryMask[mIdx] === skipVal) {
4860
+ if (maskData[mIdx] === skipVal) {
4511
4861
  dIdx++;
4512
4862
  sIdx++;
4513
4863
  mIdx++;
@@ -4635,10 +4985,11 @@ function pixelDataToAlphaMask(pixelData) {
4635
4985
  height
4636
4986
  } = pixelData;
4637
4987
  const len = data32.length;
4638
- const mask = new Uint8Array(width * height);
4988
+ const mask = makeAlphaMask(width, height);
4989
+ const maskData = mask.data;
4639
4990
  for (let i = 0; i < len; i++) {
4640
4991
  const val = data32[i];
4641
- mask[i] = val >>> 24 & 255;
4992
+ maskData[i] = val >>> 24 & 255;
4642
4993
  }
4643
4994
  return mask;
4644
4995
  }
@@ -4820,11 +5171,13 @@ function writePixelDataBuffer(target, data, _x, _y, _w, _h) {
4820
5171
  exclusionPerfect,
4821
5172
  extractImageDataBuffer,
4822
5173
  extractMask,
5174
+ extractMaskBuffer,
4823
5175
  extractPixelData,
4824
5176
  extractPixelDataBuffer,
4825
5177
  fileInputChangeToImageData,
4826
5178
  fileToImageData,
4827
5179
  fillPixelData,
5180
+ fillPixelDataBinaryMask,
4828
5181
  floodFillSelection,
4829
5182
  forEachLinePoint,
4830
5183
  getCircleBrushOrPencilBounds,
@@ -4833,12 +5186,13 @@ function writePixelDataBuffer(target, data, _x, _y, _w, _h) {
4833
5186
  getIndexedImageColorCounts,
4834
5187
  getRectBrushOrPencilBounds,
4835
5188
  getRectBrushOrPencilStrokeBounds,
5189
+ getRectsBounds,
4836
5190
  getSupportedPixelFormats,
4837
5191
  hardLightFast,
4838
5192
  hardLightPerfect,
4839
5193
  hardMixFast,
4840
5194
  hardMixPerfect,
4841
- imageDataToAlphaMask,
5195
+ imageDataToAlphaMaskBuffer,
4842
5196
  imageDataToDataUrl,
4843
5197
  imageDataToImgBlob,
4844
5198
  imageDataToUInt32Array,
@@ -4861,7 +5215,11 @@ function writePixelDataBuffer(target, data, _x, _y, _w, _h) {
4861
5215
  linearDodgePerfect,
4862
5216
  linearLightFast,
4863
5217
  linearLightPerfect,
5218
+ makeAlphaMask,
5219
+ makeBinaryMask,
4864
5220
  makeBlendModeRegistry,
5221
+ makeCircleBrushAlphaMask,
5222
+ makeCircleBrushBinaryMask,
4865
5223
  makeFastBlendModeRegistry,
4866
5224
  makeFullPixelMutator,
4867
5225
  makeImageDataLike,
@@ -4869,7 +5227,9 @@ function writePixelDataBuffer(target, data, _x, _y, _w, _h) {
4869
5227
  makePixelCanvas,
4870
5228
  makeReusableCanvas,
4871
5229
  makeReusableImageData,
5230
+ merge2BinaryMaskRects,
4872
5231
  mergeAlphaMasks,
5232
+ mergeBinaryMaskRects,
4873
5233
  mergeBinaryMasks,
4874
5234
  multiplyFast,
4875
5235
  multiplyPerfect,
@@ -4888,6 +5248,7 @@ function writePixelDataBuffer(target, data, _x, _y, _w, _h) {
4888
5248
  mutatorBlendPixelData,
4889
5249
  mutatorClear,
4890
5250
  mutatorFill,
5251
+ mutatorFillBinaryMask,
4891
5252
  mutatorInvert,
4892
5253
  overlayFast,
4893
5254
  overlayPerfect,
@@ -4910,10 +5271,12 @@ function writePixelDataBuffer(target, data, _x, _y, _w, _h) {
4910
5271
  screenPerfect,
4911
5272
  serializeImageData,
4912
5273
  serializeNullableImageData,
5274
+ setMaskData,
4913
5275
  softLightFast,
4914
5276
  softLightPerfect,
4915
5277
  sourceOverFast,
4916
5278
  sourceOverPerfect,
5279
+ subtractBinaryMaskRects,
4917
5280
  subtractFast,
4918
5281
  subtractPerfect,
4919
5282
  toBlendModeIndexAndName,