pixel-data-js 0.24.0 → 0.25.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/dist/index.dev.cjs +1431 -1845
  2. package/dist/index.dev.cjs.map +1 -1
  3. package/dist/index.dev.js +1297 -1702
  4. package/dist/index.dev.js.map +1 -1
  5. package/dist/index.prod.cjs +1305 -1719
  6. package/dist/index.prod.cjs.map +1 -1
  7. package/dist/index.prod.d.ts +220 -328
  8. package/dist/index.prod.js +1423 -1828
  9. package/dist/index.prod.js.map +1 -1
  10. package/package.json +1 -1
  11. package/src/Algorithm/floodFillSelection.ts +2 -2
  12. package/src/Canvas/canvas-blend-modes.ts +28 -0
  13. package/src/History/PixelAccumulator.ts +52 -29
  14. package/src/History/PixelEngineConfig.ts +7 -9
  15. package/src/History/PixelMutator/mutatorBlendPixelData.ts +2 -2
  16. package/src/History/PixelMutator/mutatorBlendPixelDataAlphaMask.ts +2 -2
  17. package/src/History/PixelMutator/mutatorBlendPixelDataBinaryMask.ts +2 -2
  18. package/src/History/PixelMutator.ts +0 -20
  19. package/src/History/PixelPatchTiles.ts +2 -2
  20. package/src/History/PixelWriter.ts +132 -9
  21. package/src/Internal/helpers.ts +2 -0
  22. package/src/Paint/PaintBuffer.ts +269 -0
  23. package/src/{PixelTile/PaintBufferRenderer.ts → Paint/PaintBufferCanvasRenderer.ts} +13 -5
  24. package/src/Paint/makeCirclePaintAlphaMask.ts +41 -0
  25. package/src/{Mask/CircleBinaryMask.ts → Paint/makeCirclePaintBinaryMask.ts} +5 -6
  26. package/src/Paint/makePaintMask.ts +28 -0
  27. package/src/Paint/makeRectFalloffPaintAlphaMask.ts +47 -0
  28. package/src/PixelData/PixelBuffer32.ts +2 -2
  29. package/src/PixelData/PixelData.ts +1 -1
  30. package/src/PixelData/applyAlphaMaskToPixelData.ts +2 -2
  31. package/src/PixelData/applyBinaryMaskToPixelData.ts +2 -2
  32. package/src/PixelData/blendColorPixelData.ts +2 -2
  33. package/src/PixelData/blendColorPixelDataAlphaMask.ts +3 -3
  34. package/src/PixelData/blendColorPixelDataBinaryMask.ts +3 -3
  35. package/src/PixelData/blendPixel.ts +2 -2
  36. package/src/PixelData/blendPixelData.ts +3 -3
  37. package/src/PixelData/blendPixelDataAlphaMask.ts +3 -3
  38. package/src/PixelData/blendPixelDataBinaryMask.ts +3 -3
  39. package/src/PixelData/blendPixelDataPaintBuffer.ts +3 -3
  40. package/src/PixelData/clearPixelData.ts +2 -2
  41. package/src/PixelData/extractPixelData.ts +4 -4
  42. package/src/PixelData/extractPixelDataBuffer.ts +4 -4
  43. package/src/PixelData/fillPixelData.ts +5 -5
  44. package/src/PixelData/fillPixelDataBinaryMask.ts +3 -3
  45. package/src/PixelData/fillPixelDataFast.ts +5 -5
  46. package/src/PixelData/invertPixelData.ts +2 -2
  47. package/src/PixelData/pixelDataToAlphaMask.ts +2 -2
  48. package/src/PixelData/reflectPixelData.ts +3 -3
  49. package/src/PixelData/resamplePixelData.ts +2 -2
  50. package/src/PixelData/writePaintBufferToPixelData.ts +26 -0
  51. package/src/PixelData/writePixelDataBuffer.ts +5 -5
  52. package/src/Rect/trimMaskRectBounds.ts +121 -0
  53. package/src/Rect/trimRectBounds.ts +25 -116
  54. package/src/_types.ts +16 -15
  55. package/src/index.ts +9 -24
  56. package/src/History/PixelMutator/mutatorApplyCircleBrushStroke.ts +0 -182
  57. package/src/History/PixelMutator/mutatorApplyCirclePencil.ts +0 -59
  58. package/src/History/PixelMutator/mutatorApplyCirclePencilStroke.ts +0 -172
  59. package/src/History/PixelMutator/mutatorApplyRectBrush.ts +0 -64
  60. package/src/History/PixelMutator/mutatorApplyRectBrushStroke.ts +0 -184
  61. package/src/History/PixelMutator/mutatorApplyRectPencil.ts +0 -65
  62. package/src/History/PixelMutator/mutatorApplyRectPencilStroke.ts +0 -166
  63. package/src/History/PixelMutator/mutatorBlendColorCircleMask.ts +0 -71
  64. package/src/Mask/CircleAlphaMask.ts +0 -32
  65. package/src/PixelData/applyRectBrushToPixelData.ts +0 -98
  66. package/src/PixelData/blendColorPixelDataCircleMask.ts +0 -92
  67. package/src/PixelTile/PaintBuffer.ts +0 -122
  68. package/src/Rect/getCircleBrushOrPencilBounds.ts +0 -43
  69. package/src/Rect/getCircleBrushOrPencilStrokeBounds.ts +0 -24
  70. package/src/Rect/getRectBrushOrPencilBounds.ts +0 -38
  71. package/src/Rect/getRectBrushOrPencilStrokeBounds.ts +0 -26
@@ -41,13 +41,11 @@ __export(src_exports, {
41
41
  applyBinaryMaskToAlphaMask: () => applyBinaryMaskToAlphaMask,
42
42
  applyBinaryMaskToPixelData: () => applyBinaryMaskToPixelData,
43
43
  applyPatchTiles: () => applyPatchTiles,
44
- applyRectBrushToPixelData: () => applyRectBrushToPixelData,
45
44
  base64DecodeArrayBuffer: () => base64DecodeArrayBuffer,
46
45
  base64EncodeArrayBuffer: () => base64EncodeArrayBuffer,
47
46
  blendColorPixelData: () => blendColorPixelData,
48
47
  blendColorPixelDataAlphaMask: () => blendColorPixelDataAlphaMask,
49
48
  blendColorPixelDataBinaryMask: () => blendColorPixelDataBinaryMask,
50
- blendColorPixelDataCircleMask: () => blendColorPixelDataCircleMask,
51
49
  blendPixel: () => blendPixel,
52
50
  blendPixelData: () => blendPixelData,
53
51
  blendPixelDataAlphaMask: () => blendPixelDataAlphaMask,
@@ -89,12 +87,8 @@ __export(src_exports, {
89
87
  fillPixelDataFast: () => fillPixelDataFast,
90
88
  floodFillSelection: () => floodFillSelection,
91
89
  forEachLinePoint: () => forEachLinePoint,
92
- getCircleBrushOrPencilBounds: () => getCircleBrushOrPencilBounds,
93
- getCircleBrushOrPencilStrokeBounds: () => getCircleBrushOrPencilStrokeBounds,
94
90
  getImageDataFromClipboard: () => getImageDataFromClipboard,
95
91
  getIndexedImageColorCounts: () => getIndexedImageColorCounts,
96
- getRectBrushOrPencilBounds: () => getRectBrushOrPencilBounds,
97
- getRectBrushOrPencilStrokeBounds: () => getRectBrushOrPencilStrokeBounds,
98
92
  getRectsBounds: () => getRectsBounds,
99
93
  getSupportedPixelFormats: () => getSupportedPixelFormats,
100
94
  hardLightFast: () => hardLightFast,
@@ -128,15 +122,18 @@ __export(src_exports, {
128
122
  makeBinaryMask: () => makeBinaryMask,
129
123
  makeBlendModeRegistry: () => makeBlendModeRegistry,
130
124
  makeCanvasFrameRenderer: () => makeCanvasFrameRenderer,
131
- makeCircleAlphaMask: () => makeCircleAlphaMask,
132
- makeCircleBinaryMask: () => makeCircleBinaryMask,
125
+ makeCirclePaintAlphaMask: () => makeCirclePaintAlphaMask,
126
+ makeCirclePaintBinaryMask: () => makeCirclePaintBinaryMask,
133
127
  makeFastBlendModeRegistry: () => makeFastBlendModeRegistry,
134
128
  makeFullPixelMutator: () => makeFullPixelMutator,
135
129
  makeHistoryAction: () => makeHistoryAction,
136
130
  makeImageDataLike: () => makeImageDataLike,
137
- makePaintBufferRenderer: () => makePaintBufferRenderer,
131
+ makePaintAlphaMask: () => makePaintAlphaMask,
132
+ makePaintBinaryMask: () => makePaintBinaryMask,
133
+ makePaintBufferCanvasRenderer: () => makePaintBufferCanvasRenderer,
138
134
  makePerfectBlendModeRegistry: () => makePerfectBlendModeRegistry,
139
135
  makePixelCanvas: () => makePixelCanvas,
136
+ makeRectFalloffPaintAlphaMask: () => makeRectFalloffPaintAlphaMask,
140
137
  makeReusableCanvas: () => makeReusableCanvas,
141
138
  makeReusableImageData: () => makeReusableImageData,
142
139
  makeReusableOffscreenCanvas: () => makeReusableOffscreenCanvas,
@@ -148,15 +145,7 @@ __export(src_exports, {
148
145
  multiplyPerfect: () => multiplyPerfect,
149
146
  mutatorApplyAlphaMask: () => mutatorApplyAlphaMask,
150
147
  mutatorApplyBinaryMask: () => mutatorApplyBinaryMask,
151
- mutatorApplyCircleBrushStroke: () => mutatorApplyCircleBrushStroke,
152
- mutatorApplyCirclePencil: () => mutatorApplyCirclePencil,
153
- mutatorApplyCirclePencilStroke: () => mutatorApplyCirclePencilStroke,
154
- mutatorApplyRectBrush: () => mutatorApplyRectBrush,
155
- mutatorApplyRectBrushStroke: () => mutatorApplyRectBrushStroke,
156
- mutatorApplyRectPencil: () => mutatorApplyRectPencil,
157
- mutatorApplyRectPencilStroke: () => mutatorApplyRectPencilStroke,
158
148
  mutatorBlendColor: () => mutatorBlendColor,
159
- mutatorBlendColorCircleMask: () => mutatorBlendColorCircleMask,
160
149
  mutatorBlendPixel: () => mutatorBlendPixel,
161
150
  mutatorBlendPixelData: () => mutatorBlendPixelData,
162
151
  mutatorBlendPixelDataAlphaMask: () => mutatorBlendPixelDataAlphaMask,
@@ -196,6 +185,7 @@ __export(src_exports, {
196
185
  subtractFast: () => subtractFast,
197
186
  subtractPerfect: () => subtractPerfect,
198
187
  toBlendModeIndexAndName: () => toBlendModeIndexAndName,
188
+ trimMaskRectBounds: () => trimMaskRectBounds,
199
189
  trimRectBounds: () => trimRectBounds,
200
190
  uInt32ArrayToImageData: () => uInt32ArrayToImageData,
201
191
  uInt32ArrayToImageDataLike: () => uInt32ArrayToImageDataLike,
@@ -211,6 +201,7 @@ __export(src_exports, {
211
201
  writeImageDataBuffer: () => writeImageDataBuffer,
212
202
  writeImageDataToClipboard: () => writeImageDataToClipboard,
213
203
  writeImgBlobToClipboard: () => writeImgBlobToClipboard,
204
+ writePaintBufferToPixelData: () => writePaintBufferToPixelData,
214
205
  writePixelDataBuffer: () => writePixelDataBuffer
215
206
  });
216
207
  module.exports = __toCommonJS(src_exports);
@@ -449,8 +440,8 @@ function extractMaskBuffer(maskBuffer, maskWidth, xOrRect, y, w, h) {
449
440
  return out;
450
441
  }
451
442
 
452
- // src/Rect/trimRectBounds.ts
453
- function trimRectBounds(target, bounds) {
443
+ // src/Rect/trimMaskRectBounds.ts
444
+ function trimMaskRectBounds(target, bounds) {
454
445
  const originalX = target.x;
455
446
  const originalY = target.y;
456
447
  const originalW = target.w;
@@ -638,7 +629,7 @@ function floodFillSelection(img, startX, startY, {
638
629
  finalMask[my * sw + mx] = 1;
639
630
  }
640
631
  }
641
- trimRectBounds(selectionRect, {
632
+ trimMaskRectBounds(selectionRect, {
642
633
  x: 0,
643
634
  y: 0,
644
635
  w: width,
@@ -2028,11 +2019,11 @@ var PixelAccumulator = class {
2028
2019
  * @param y pixel y coordinate
2029
2020
  */
2030
2021
  storePixelBeforeState(x, y) {
2031
- let shift = this.config.tileShift;
2032
- let columns = this.config.targetColumns;
2033
- let tx = x >> shift;
2034
- let ty = y >> shift;
2035
- let id = ty * columns + tx;
2022
+ const shift = this.config.tileShift;
2023
+ const columns = this.config.targetColumns;
2024
+ const tx = x >> shift;
2025
+ const ty = y >> shift;
2026
+ const id = ty * columns + tx;
2036
2027
  let tile = this.lookup[id];
2037
2028
  let added = false;
2038
2029
  if (!tile) {
@@ -2058,16 +2049,16 @@ var PixelAccumulator = class {
2058
2049
  * @param h pixel height
2059
2050
  */
2060
2051
  storeRegionBeforeState(x, y, w, h) {
2061
- let shift = this.config.tileShift;
2062
- let columns = this.config.targetColumns;
2063
- let startX = x >> shift;
2064
- let startY = y >> shift;
2065
- let endX = x + w - 1 >> shift;
2066
- let endY = y + h - 1 >> shift;
2067
- let startIndex = this.beforeTiles.length;
2052
+ const shift = this.config.tileShift;
2053
+ const columns = this.config.targetColumns;
2054
+ const startX = x >> shift;
2055
+ const startY = y >> shift;
2056
+ const endX = x + w - 1 >> shift;
2057
+ const endY = y + h - 1 >> shift;
2058
+ const startIndex = this.beforeTiles.length;
2068
2059
  for (let ty = startY; ty <= endY; ty++) {
2069
2060
  for (let tx = startX; tx <= endX; tx++) {
2070
- let id = ty * columns + tx;
2061
+ const id = ty * columns + tx;
2071
2062
  let tile = this.lookup[id];
2072
2063
  if (!tile) {
2073
2064
  tile = this.tilePool.getTile(id, tx, ty);
@@ -2079,7 +2070,7 @@ var PixelAccumulator = class {
2079
2070
  }
2080
2071
  return (didChange) => {
2081
2072
  if (!didChange) {
2082
- let length = this.beforeTiles.length;
2073
+ const length = this.beforeTiles.length;
2083
2074
  for (let i = startIndex; i < length; i++) {
2084
2075
  let t = this.beforeTiles[i];
2085
2076
  if (t) {
@@ -2092,15 +2083,34 @@ var PixelAccumulator = class {
2092
2083
  return didChange;
2093
2084
  };
2094
2085
  }
2086
+ storeTileBeforeState(id, tx, ty) {
2087
+ let tile = this.lookup[id];
2088
+ let added = false;
2089
+ if (!tile) {
2090
+ tile = this.tilePool.getTile(id, tx, ty);
2091
+ this.extractState(tile);
2092
+ this.lookup[id] = tile;
2093
+ this.beforeTiles.push(tile);
2094
+ added = true;
2095
+ }
2096
+ return (didChange) => {
2097
+ if (!didChange && added) {
2098
+ this.beforeTiles.pop();
2099
+ this.lookup[id] = void 0;
2100
+ this.tilePool.releaseTile(tile);
2101
+ }
2102
+ return didChange;
2103
+ };
2104
+ }
2095
2105
  extractState(tile) {
2096
- let target = this.config.target;
2097
- let TILE_SIZE = this.config.tileSize;
2098
- let dst = tile.data32;
2099
- let src = target.data32;
2100
- let startX = tile.tx * TILE_SIZE;
2101
- let startY = tile.ty * TILE_SIZE;
2102
- let targetWidth = target.width;
2103
- let targetHeight = target.height;
2106
+ const target = this.config.target;
2107
+ const TILE_SIZE = this.config.tileSize;
2108
+ const dst = tile.data32;
2109
+ const src = target.data32;
2110
+ const startX = tile.tx * TILE_SIZE;
2111
+ const startY = tile.ty * TILE_SIZE;
2112
+ const targetWidth = target.width;
2113
+ const targetHeight = target.height;
2104
2114
  if (startX >= targetWidth || startX + TILE_SIZE <= 0 || startY >= targetHeight || startY + TILE_SIZE <= 0) {
2105
2115
  dst.fill(0);
2106
2116
  return;
@@ -2126,8 +2136,8 @@ var PixelAccumulator = class {
2126
2136
  }
2127
2137
  }
2128
2138
  extractPatch() {
2129
- let afterTiles = [];
2130
- let length = this.beforeTiles.length;
2139
+ const afterTiles = [];
2140
+ const length = this.beforeTiles.length;
2131
2141
  for (let i = 0; i < length; i++) {
2132
2142
  let beforeTile = this.beforeTiles[i];
2133
2143
  if (beforeTile) {
@@ -2136,7 +2146,7 @@ var PixelAccumulator = class {
2136
2146
  afterTiles.push(afterTile);
2137
2147
  }
2138
2148
  }
2139
- let beforeTiles = this.beforeTiles;
2149
+ const beforeTiles = this.beforeTiles;
2140
2150
  this.beforeTiles = [];
2141
2151
  this.lookup.length = 0;
2142
2152
  return {
@@ -2144,10 +2154,10 @@ var PixelAccumulator = class {
2144
2154
  afterTiles
2145
2155
  };
2146
2156
  }
2147
- rollback() {
2148
- let target = this.config.target;
2149
- let tileSize = this.config.tileSize;
2150
- let length = this.beforeTiles.length;
2157
+ rollbackAfterError() {
2158
+ const target = this.config.target;
2159
+ const tileSize = this.config.tileSize;
2160
+ const length = this.beforeTiles.length;
2151
2161
  applyPatchTiles(target, this.beforeTiles, tileSize);
2152
2162
  for (let i = 0; i < length; i++) {
2153
2163
  let tile = this.beforeTiles[i];
@@ -2171,6 +2181,7 @@ var PixelEngineConfig = class {
2171
2181
  tileArea;
2172
2182
  target;
2173
2183
  targetColumns = 0;
2184
+ targetRows = 0;
2174
2185
  constructor(tileSize, target) {
2175
2186
  if ((tileSize & tileSize - 1) !== 0) {
2176
2187
  throw new Error("tileSize must be a power of 2");
@@ -2179,28 +2190,26 @@ var PixelEngineConfig = class {
2179
2190
  this.tileShift = 31 - Math.clz32(tileSize);
2180
2191
  this.tileMask = tileSize - 1;
2181
2192
  this.tileArea = tileSize * tileSize;
2182
- this.setTarget(target);
2183
- }
2184
- setTarget(target) {
2185
- ;
2186
2193
  this.target = target;
2187
2194
  this.targetColumns = target.width + this.tileMask >> this.tileShift;
2195
+ this.targetRows = target.height + this.tileMask >> this.tileShift;
2188
2196
  }
2189
2197
  };
2190
2198
 
2191
- // src/PixelData/applyAlphaMaskToPixelData.ts
2192
- function applyAlphaMaskToPixelData(dst, mask, opts = {}) {
2199
+ // src/PixelData/blendColorPixelData.ts
2200
+ function blendColorPixelData(dst, color, opts = {}) {
2193
2201
  const {
2194
2202
  x: targetX = 0,
2195
2203
  y: targetY = 0,
2196
2204
  w: width = dst.width,
2197
2205
  h: height = dst.height,
2198
2206
  alpha: globalAlpha = 255,
2199
- mx = 0,
2200
- my = 0,
2201
- invertMask = false
2207
+ blendFn = sourceOverPerfect
2202
2208
  } = opts;
2203
2209
  if (globalAlpha === 0) return false;
2210
+ const baseSrcAlpha = color >>> 24;
2211
+ const isOverwrite = blendFn.isOverwrite || false;
2212
+ if (baseSrcAlpha === 0 && !isOverwrite) return false;
2204
2213
  let x = targetX;
2205
2214
  let y = targetY;
2206
2215
  let w = width;
@@ -2213,82 +2222,46 @@ function applyAlphaMaskToPixelData(dst, mask, opts = {}) {
2213
2222
  h += y;
2214
2223
  y = 0;
2215
2224
  }
2216
- w = Math.min(w, dst.width - x);
2217
- h = Math.min(h, dst.height - y);
2218
- if (w <= 0) return false;
2219
- if (h <= 0) return false;
2220
- const mPitch = mask.w;
2221
- if (mPitch <= 0) return false;
2222
- const startX = mx + (x - targetX);
2223
- const startY = my + (y - targetY);
2224
- const sX0 = Math.max(0, startX);
2225
- const sY0 = Math.max(0, startY);
2226
- const sX1 = Math.min(mPitch, startX + w);
2227
- const sY1 = Math.min(mask.h, startY + h);
2228
- const finalW = sX1 - sX0;
2229
- const finalH = sY1 - sY0;
2230
- if (finalW <= 0) return false;
2231
- if (finalH <= 0) return false;
2232
- const xShift = sX0 - startX;
2233
- const yShift = sY0 - startY;
2225
+ const actualW = Math.min(w, dst.width - x);
2226
+ const actualH = Math.min(h, dst.height - y);
2227
+ if (actualW <= 0 || actualH <= 0) return false;
2228
+ let finalSrcColor = color;
2229
+ if (globalAlpha < 255) {
2230
+ const a = baseSrcAlpha * globalAlpha + 128 >> 8;
2231
+ if (a === 0 && !isOverwrite) return false;
2232
+ finalSrcColor = (color & 16777215 | a << 24) >>> 0;
2233
+ }
2234
2234
  const dst32 = dst.data32;
2235
2235
  const dw = dst.width;
2236
- const dStride = dw - finalW;
2237
- const mStride = mPitch - finalW;
2238
- const maskData = mask.data;
2239
- let dIdx = (y + yShift) * dw + (x + xShift);
2240
- let mIdx = sY0 * mPitch + sX0;
2236
+ let dIdx = y * dw + x | 0;
2237
+ const dStride = dw - actualW | 0;
2241
2238
  let didChange = false;
2242
- for (let iy = 0; iy < h; iy++) {
2243
- for (let ix = 0; ix < w; ix++) {
2244
- const mVal = maskData[mIdx];
2245
- const effectiveM = invertMask ? 255 - mVal : mVal;
2246
- let weight = 0;
2247
- if (effectiveM === 0) {
2248
- weight = 0;
2249
- } else if (effectiveM === 255) {
2250
- weight = globalAlpha;
2251
- } else if (globalAlpha === 255) {
2252
- weight = effectiveM;
2253
- } else {
2254
- weight = effectiveM * globalAlpha + 128 >> 8;
2255
- }
2256
- if (weight === 0) {
2257
- dst32[dIdx] = (dst32[dIdx] & 16777215) >>> 0;
2239
+ for (let iy = 0; iy < actualH; iy++) {
2240
+ for (let ix = 0; ix < actualW; ix++) {
2241
+ const current = dst32[dIdx];
2242
+ const next = blendFn(finalSrcColor, current);
2243
+ if (current !== next) {
2244
+ dst32[dIdx] = next;
2258
2245
  didChange = true;
2259
- } else if (weight !== 255) {
2260
- const d = dst32[dIdx];
2261
- const da = d >>> 24;
2262
- if (da !== 0) {
2263
- const finalAlpha = da === 255 ? weight : da * weight + 128 >> 8;
2264
- const current = dst32[dIdx];
2265
- const next = (d & 16777215 | finalAlpha << 24) >>> 0;
2266
- if (current !== next) {
2267
- dst32[dIdx] = next;
2268
- didChange = true;
2269
- }
2270
- }
2271
2246
  }
2272
2247
  dIdx++;
2273
- mIdx++;
2274
2248
  }
2275
2249
  dIdx += dStride;
2276
- mIdx += mStride;
2277
2250
  }
2278
2251
  return didChange;
2279
2252
  }
2280
2253
 
2281
- // src/History/PixelMutator/mutatorApplyAlphaMask.ts
2254
+ // src/History/PixelMutator/mutatorBlendColor.ts
2282
2255
  var defaults2 = {
2283
- applyAlphaMaskToPixelData
2256
+ blendColorPixelData
2284
2257
  };
2285
- var mutatorApplyAlphaMask = ((writer, deps = defaults2) => {
2258
+ var mutatorBlendColor = ((writer, deps = defaults2) => {
2286
2259
  const {
2287
- applyAlphaMaskToPixelData: applyAlphaMaskToPixelData2 = defaults2.applyAlphaMaskToPixelData
2260
+ blendColorPixelData: blendColorPixelData2 = defaults2.blendColorPixelData
2288
2261
  } = deps;
2289
2262
  return {
2290
- applyAlphaMask(mask, opts = {}) {
2291
- let target = writer.config.target;
2263
+ blendColor(color, opts = {}) {
2264
+ const target = writer.config.target;
2292
2265
  const {
2293
2266
  x = 0,
2294
2267
  y = 0,
@@ -2296,159 +2269,227 @@ var mutatorApplyAlphaMask = ((writer, deps = defaults2) => {
2296
2269
  h = target.height
2297
2270
  } = opts;
2298
2271
  const didChange = writer.accumulator.storeRegionBeforeState(x, y, w, h);
2299
- return didChange(applyAlphaMaskToPixelData2(target, mask, opts));
2272
+ return didChange(blendColorPixelData2(target, color, opts));
2300
2273
  }
2301
2274
  };
2302
2275
  });
2303
2276
 
2304
- // src/PixelData/applyBinaryMaskToPixelData.ts
2305
- function applyBinaryMaskToPixelData(dst, mask, opts = {}) {
2277
+ // src/PixelData/blendPixel.ts
2278
+ function blendPixel(target, x, y, color, alpha = 255, blendFn = sourceOverPerfect) {
2279
+ if (alpha === 0) return false;
2280
+ let width = target.width;
2281
+ let height = target.height;
2282
+ if (x < 0 || x >= width || y < 0 || y >= height) return false;
2283
+ let srcAlpha = color >>> 24;
2284
+ let isOverwrite = blendFn.isOverwrite;
2285
+ if (srcAlpha === 0 && !isOverwrite) return false;
2286
+ let dst32 = target.data32;
2287
+ let index = y * width + x;
2288
+ let finalColor = color;
2289
+ if (alpha !== 255) {
2290
+ let finalAlpha = srcAlpha * alpha + 128 >> 8;
2291
+ if (finalAlpha === 0 && !isOverwrite) return false;
2292
+ finalColor = (color & 16777215 | finalAlpha << 24) >>> 0;
2293
+ }
2294
+ let current = dst32[index];
2295
+ let next = blendFn(finalColor, current);
2296
+ if (current !== next) {
2297
+ dst32[index] = next;
2298
+ return true;
2299
+ }
2300
+ return false;
2301
+ }
2302
+
2303
+ // src/History/PixelMutator/mutatorBlendPixel.ts
2304
+ var defaults3 = {
2305
+ blendPixel
2306
+ };
2307
+ var mutatorBlendPixel = ((writer, deps = defaults3) => {
2308
+ const {
2309
+ blendPixel: blendPixel2 = defaults3.blendPixel
2310
+ } = deps;
2311
+ return {
2312
+ blendPixel(x, y, color, alpha, blendFn) {
2313
+ const didChange = writer.accumulator.storePixelBeforeState(x, y);
2314
+ return didChange(blendPixel2(writer.config.target, x, y, color, alpha, blendFn));
2315
+ }
2316
+ };
2317
+ });
2318
+
2319
+ // src/PixelData/blendPixelData.ts
2320
+ function blendPixelData(dst, src, opts = {}) {
2306
2321
  const {
2307
2322
  x: targetX = 0,
2308
2323
  y: targetY = 0,
2309
- w: width = dst.width,
2310
- h: height = dst.height,
2324
+ sx: sourceX = 0,
2325
+ sy: sourceY = 0,
2326
+ w: width = src.width,
2327
+ h: height = src.height,
2311
2328
  alpha: globalAlpha = 255,
2312
- mx = 0,
2313
- my = 0,
2314
- invertMask = false
2329
+ blendFn = sourceOverPerfect
2315
2330
  } = opts;
2316
2331
  if (globalAlpha === 0) return false;
2317
2332
  let x = targetX;
2318
2333
  let y = targetY;
2334
+ let sx = sourceX;
2335
+ let sy = sourceY;
2319
2336
  let w = width;
2320
2337
  let h = height;
2338
+ if (sx < 0) {
2339
+ x -= sx;
2340
+ w += sx;
2341
+ sx = 0;
2342
+ }
2343
+ if (sy < 0) {
2344
+ y -= sy;
2345
+ h += sy;
2346
+ sy = 0;
2347
+ }
2348
+ w = Math.min(w, src.width - sx);
2349
+ h = Math.min(h, src.height - sy);
2321
2350
  if (x < 0) {
2351
+ sx -= x;
2322
2352
  w += x;
2323
2353
  x = 0;
2324
2354
  }
2325
2355
  if (y < 0) {
2356
+ sy -= y;
2326
2357
  h += y;
2327
2358
  y = 0;
2328
2359
  }
2329
- w = Math.min(w, dst.width - x);
2330
- h = Math.min(h, dst.height - y);
2331
- if (w <= 0 || h <= 0) return false;
2332
- const mPitch = mask.w;
2333
- if (mPitch <= 0) return false;
2334
- const startX = mx + (x - targetX);
2335
- const startY = my + (y - targetY);
2336
- const sX0 = Math.max(0, startX);
2337
- const sY0 = Math.max(0, startY);
2338
- const sX1 = Math.min(mPitch, startX + w);
2339
- const sY1 = Math.min(mask.h, startY + h);
2340
- const finalW = sX1 - sX0;
2341
- const finalH = sY1 - sY0;
2342
- if (finalW <= 0 || finalH <= 0) {
2343
- return false;
2344
- }
2345
- const xShift = sX0 - startX;
2346
- const yShift = sY0 - startY;
2360
+ const actualW = Math.min(w, dst.width - x);
2361
+ const actualH = Math.min(h, dst.height - y);
2362
+ if (actualW <= 0 || actualH <= 0) return false;
2347
2363
  const dst32 = dst.data32;
2364
+ const src32 = src.data32;
2348
2365
  const dw = dst.width;
2349
- const dStride = dw - finalW;
2350
- const mStride = mPitch - finalW;
2351
- const maskData = mask.data;
2352
- let dIdx = (y + yShift) * dw + (x + xShift);
2353
- let mIdx = sY0 * mPitch + sX0;
2366
+ const sw = src.width;
2367
+ let dIdx = y * dw + x | 0;
2368
+ let sIdx = sy * sw + sx | 0;
2369
+ const dStride = dw - actualW | 0;
2370
+ const sStride = sw - actualW | 0;
2371
+ const isOpaque = globalAlpha === 255;
2372
+ const isOverwrite = blendFn.isOverwrite;
2354
2373
  let didChange = false;
2355
- for (let iy = 0; iy < finalH; iy++) {
2356
- for (let ix = 0; ix < finalW; ix++) {
2357
- const mVal = maskData[mIdx];
2358
- const isMaskedOut = invertMask ? mVal !== 0 : mVal === 0;
2359
- if (isMaskedOut) {
2360
- const current = dst32[dIdx];
2361
- const next = (current & 16777215) >>> 0;
2362
- if (current !== next) {
2363
- dst32[dIdx] = next;
2364
- didChange = true;
2365
- }
2366
- } else if (globalAlpha !== 255) {
2367
- const d = dst32[dIdx];
2368
- const da = d >>> 24;
2369
- if (da !== 0) {
2370
- const finalAlpha = da === 255 ? globalAlpha : da * globalAlpha + 128 >> 8;
2371
- const next = (d & 16777215 | finalAlpha << 24) >>> 0;
2372
- if (d !== next) {
2373
- dst32[dIdx] = next;
2374
- didChange = true;
2375
- }
2374
+ for (let iy = 0; iy < actualH; iy++) {
2375
+ for (let ix = 0; ix < actualW; ix++) {
2376
+ const srcCol = src32[sIdx];
2377
+ const srcAlpha = srcCol >>> 24;
2378
+ if (srcAlpha === 0 && !isOverwrite) {
2379
+ dIdx++;
2380
+ sIdx++;
2381
+ continue;
2382
+ }
2383
+ let finalCol = srcCol;
2384
+ if (!isOpaque) {
2385
+ const a = srcAlpha * globalAlpha + 128 >> 8;
2386
+ if (a === 0 && !isOverwrite) {
2387
+ dIdx++;
2388
+ sIdx++;
2389
+ continue;
2376
2390
  }
2391
+ finalCol = (srcCol & 16777215 | a << 24) >>> 0;
2377
2392
  }
2378
- dIdx++;
2379
- mIdx++;
2393
+ const current = dst32[dIdx];
2394
+ const next = blendFn(finalCol, dst32[dIdx]);
2395
+ if (current !== next) {
2396
+ dst32[dIdx] = next;
2397
+ didChange = true;
2398
+ }
2399
+ dIdx++;
2400
+ sIdx++;
2380
2401
  }
2381
2402
  dIdx += dStride;
2382
- mIdx += mStride;
2403
+ sIdx += sStride;
2383
2404
  }
2384
2405
  return didChange;
2385
2406
  }
2386
2407
 
2387
- // src/History/PixelMutator/mutatorApplyBinaryMask.ts
2388
- var defaults3 = {
2389
- applyBinaryMaskToPixelData
2408
+ // src/History/PixelMutator/mutatorBlendPixelData.ts
2409
+ var defaults4 = {
2410
+ blendPixelData
2390
2411
  };
2391
- var mutatorApplyBinaryMask = ((writer, deps = defaults3) => {
2412
+ var mutatorBlendPixelData = ((writer, deps = defaults4) => {
2392
2413
  const {
2393
- applyBinaryMaskToPixelData: applyBinaryMaskToPixelData2 = defaults3.applyBinaryMaskToPixelData
2414
+ blendPixelData: blendPixelData2 = defaults4.blendPixelData
2394
2415
  } = deps;
2395
2416
  return {
2396
- applyBinaryMask(mask, opts = {}) {
2397
- let target = writer.config.target;
2417
+ blendPixelData(src, opts = {}) {
2398
2418
  const {
2399
2419
  x = 0,
2400
2420
  y = 0,
2401
- w = target.width,
2402
- h = target.height
2421
+ w = src.width,
2422
+ h = src.height
2403
2423
  } = opts;
2404
2424
  const didChange = writer.accumulator.storeRegionBeforeState(x, y, w, h);
2405
- return didChange(applyBinaryMaskToPixelData2(target, mask, opts));
2425
+ return didChange(blendPixelData2(writer.config.target, src, opts));
2406
2426
  }
2407
2427
  };
2408
2428
  });
2409
2429
 
2410
- // src/PixelData/blendColorPixelDataAlphaMask.ts
2411
- function blendColorPixelDataAlphaMask(dst, color, mask, opts = {}) {
2412
- const targetX = opts.x ?? 0;
2413
- const targetY = opts.y ?? 0;
2414
- const w = opts.w ?? mask.w;
2415
- const h = opts.h ?? mask.h;
2416
- const globalAlpha = opts.alpha ?? 255;
2417
- const blendFn = opts.blendFn ?? sourceOverPerfect;
2418
- const mx = opts.mx ?? 0;
2419
- const my = opts.my ?? 0;
2420
- const invertMask = opts.invertMask ?? false;
2430
+ // src/PixelData/blendPixelDataAlphaMask.ts
2431
+ function blendPixelDataAlphaMask(dst, src, alphaMask, opts = {}) {
2432
+ const {
2433
+ x: targetX = 0,
2434
+ y: targetY = 0,
2435
+ sx: sourceX = 0,
2436
+ sy: sourceY = 0,
2437
+ w: width = src.width,
2438
+ h: height = src.height,
2439
+ alpha: globalAlpha = 255,
2440
+ blendFn = sourceOverPerfect,
2441
+ mx = 0,
2442
+ my = 0,
2443
+ invertMask = false
2444
+ } = opts;
2421
2445
  if (globalAlpha === 0) return false;
2422
- const baseSrcAlpha = color >>> 24;
2423
- const isOverwrite = blendFn.isOverwrite || false;
2424
- if (baseSrcAlpha === 0 && !isOverwrite) return false;
2425
2446
  let x = targetX;
2426
2447
  let y = targetY;
2427
- let actualW = w;
2428
- let actualH = h;
2448
+ let sx = sourceX;
2449
+ let sy = sourceY;
2450
+ let w = width;
2451
+ let h = height;
2452
+ if (sx < 0) {
2453
+ x -= sx;
2454
+ w += sx;
2455
+ sx = 0;
2456
+ }
2457
+ if (sy < 0) {
2458
+ y -= sy;
2459
+ h += sy;
2460
+ sy = 0;
2461
+ }
2462
+ w = Math.min(w, src.width - sx);
2463
+ h = Math.min(h, src.height - sy);
2429
2464
  if (x < 0) {
2430
- actualW += x;
2465
+ sx -= x;
2466
+ w += x;
2431
2467
  x = 0;
2432
2468
  }
2433
2469
  if (y < 0) {
2434
- actualH += y;
2470
+ sy -= y;
2471
+ h += y;
2435
2472
  y = 0;
2436
2473
  }
2437
- actualW = Math.min(actualW, dst.width - x);
2438
- actualH = Math.min(actualH, dst.height - y);
2474
+ const actualW = Math.min(w, dst.width - x);
2475
+ const actualH = Math.min(h, dst.height - y);
2439
2476
  if (actualW <= 0 || actualH <= 0) return false;
2477
+ const dw = dst.width;
2478
+ const sw = src.width;
2479
+ const mPitch = alphaMask.w;
2480
+ const maskData = alphaMask.data;
2440
2481
  const dx = x - targetX | 0;
2441
2482
  const dy = y - targetY | 0;
2442
2483
  const dst32 = dst.data32;
2443
- const dw = dst.width;
2444
- const mPitch = mask.w;
2445
- const maskData = mask.data;
2484
+ const src32 = src.data32;
2446
2485
  let dIdx = y * dw + x | 0;
2486
+ let sIdx = sy * sw + sx | 0;
2447
2487
  let mIdx = (my + dy) * mPitch + (mx + dx) | 0;
2448
2488
  const dStride = dw - actualW | 0;
2489
+ const sStride = sw - actualW | 0;
2449
2490
  const mStride = mPitch - actualW | 0;
2450
2491
  const isOpaque = globalAlpha === 255;
2451
- const colorRGB = color & 16777215;
2492
+ const isOverwrite = blendFn.isOverwrite || false;
2452
2493
  let didChange = false;
2453
2494
  for (let iy = 0; iy < actualH; iy++) {
2454
2495
  for (let ix = 0; ix < actualW; ix++) {
@@ -2456,6 +2497,15 @@ function blendColorPixelDataAlphaMask(dst, color, mask, opts = {}) {
2456
2497
  const effM = invertMask ? 255 - mVal : mVal;
2457
2498
  if (effM === 0) {
2458
2499
  dIdx++;
2500
+ sIdx++;
2501
+ mIdx++;
2502
+ continue;
2503
+ }
2504
+ const srcCol = src32[sIdx];
2505
+ const srcAlpha = srcCol >>> 24;
2506
+ if (srcAlpha === 0 && !isOverwrite) {
2507
+ dIdx++;
2508
+ sIdx++;
2459
2509
  mIdx++;
2460
2510
  continue;
2461
2511
  }
@@ -2467,844 +2517,459 @@ function blendColorPixelDataAlphaMask(dst, color, mask, opts = {}) {
2467
2517
  }
2468
2518
  if (weight === 0) {
2469
2519
  dIdx++;
2520
+ sIdx++;
2470
2521
  mIdx++;
2471
2522
  continue;
2472
2523
  }
2473
- let finalCol = color;
2524
+ let finalCol = srcCol;
2474
2525
  if (weight < 255) {
2475
- const a = baseSrcAlpha * weight + 128 >> 8;
2526
+ const a = srcAlpha * weight + 128 >> 8;
2476
2527
  if (a === 0 && !isOverwrite) {
2477
2528
  dIdx++;
2529
+ sIdx++;
2478
2530
  mIdx++;
2479
2531
  continue;
2480
2532
  }
2481
- finalCol = (colorRGB | a << 24) >>> 0;
2533
+ finalCol = (srcCol & 16777215 | a << 24) >>> 0;
2482
2534
  }
2483
2535
  const current = dst32[dIdx];
2484
- const next = blendFn(finalCol, current);
2536
+ const next = blendFn(finalCol, dst32[dIdx]);
2485
2537
  if (current !== next) {
2486
2538
  dst32[dIdx] = next;
2487
2539
  didChange = true;
2488
2540
  }
2489
2541
  dIdx++;
2542
+ sIdx++;
2490
2543
  mIdx++;
2491
2544
  }
2492
2545
  dIdx += dStride;
2546
+ sIdx += sStride;
2493
2547
  mIdx += mStride;
2494
2548
  }
2495
2549
  return didChange;
2496
2550
  }
2497
2551
 
2498
- // src/Rect/getCircleBrushOrPencilBounds.ts
2499
- function getCircleBrushOrPencilBounds(centerX, centerY, brushSize, targetWidth, targetHeight, out) {
2500
- const r = brushSize / 2;
2501
- const minOffset = -Math.ceil(r - 0.5);
2502
- const maxOffset = Math.floor(r - 0.5);
2503
- const startX = Math.floor(centerX + minOffset);
2504
- const startY = Math.floor(centerY + minOffset);
2505
- const endX = Math.floor(centerX + maxOffset) + 1;
2506
- const endY = Math.floor(centerY + maxOffset) + 1;
2507
- const res = out ?? {
2508
- x: 0,
2509
- y: 0,
2510
- w: 0,
2511
- h: 0
2512
- };
2513
- const cStartX = Math.max(0, startX);
2514
- const cStartY = Math.max(0, startY);
2515
- const cEndX = Math.min(targetWidth, endX);
2516
- const cEndY = Math.min(targetHeight, endY);
2517
- const w = cEndX - cStartX;
2518
- const h = cEndY - cStartY;
2519
- res.x = cStartX;
2520
- res.y = cStartY;
2521
- res.w = w < 0 ? 0 : w;
2522
- res.h = h < 0 ? 0 : h;
2523
- return res;
2524
- }
2525
-
2526
- // src/Rect/getCircleBrushOrPencilStrokeBounds.ts
2527
- function getCircleBrushOrPencilStrokeBounds(x0, y0, x1, y1, brushSize, result) {
2528
- const r = Math.ceil(brushSize / 2);
2529
- const minX = Math.min(x0, x1) - r;
2530
- const minY = Math.min(y0, y1) - r;
2531
- const maxX = Math.max(x0, x1) + r;
2532
- const maxY = Math.max(x0, y1) + r;
2533
- result.x = Math.floor(minX);
2534
- result.y = Math.floor(minY);
2535
- result.w = Math.ceil(maxX - minX);
2536
- result.h = Math.ceil(maxY - minY);
2537
- return result;
2538
- }
2539
-
2540
- // src/History/PixelMutator/mutatorApplyCircleBrushStroke.ts
2541
- var defaults4 = {
2542
- forEachLinePoint,
2543
- blendColorPixelDataAlphaMask,
2544
- getCircleBrushOrPencilBounds,
2545
- getCircleBrushOrPencilStrokeBounds
2552
+ // src/History/PixelMutator/mutatorBlendPixelDataAlphaMask.ts
2553
+ var defaults5 = {
2554
+ blendPixelDataAlphaMask
2546
2555
  };
2547
- var mutatorApplyCircleBrushStroke = ((writer, deps = defaults4) => {
2556
+ var mutatorBlendPixelDataAlphaMask = ((writer, deps = defaults5) => {
2548
2557
  const {
2549
- forEachLinePoint: forEachLinePoint2 = defaults4.forEachLinePoint,
2550
- blendColorPixelDataAlphaMask: blendColorPixelDataAlphaMask2 = defaults4.blendColorPixelDataAlphaMask,
2551
- getCircleBrushOrPencilBounds: getCircleBrushOrPencilBounds2 = defaults4.getCircleBrushOrPencilBounds,
2552
- getCircleBrushOrPencilStrokeBounds: getCircleBrushOrPencilStrokeBounds2 = defaults4.getCircleBrushOrPencilStrokeBounds
2558
+ blendPixelDataAlphaMask: blendPixelDataAlphaMask2 = defaults5.blendPixelDataAlphaMask
2553
2559
  } = deps;
2554
- const strokeBoundsOut = {
2555
- x: 0,
2556
- y: 0,
2557
- w: 0,
2558
- h: 0
2559
- };
2560
- const circleBrushBounds = {
2561
- x: 0,
2562
- y: 0,
2563
- w: 0,
2564
- h: 0
2565
- };
2566
- const blendColorPixelOptions = {
2567
- alpha: 255,
2568
- blendFn: sourceOverPerfect,
2569
- x: 0,
2570
- y: 0,
2571
- w: 0,
2572
- h: 0
2573
- };
2574
- const mask = {
2575
- type: 0 /* ALPHA */,
2576
- data: null,
2577
- w: 0,
2578
- h: 0
2579
- };
2580
2560
  return {
2581
- applyCircleBrushStroke(color, x0, y0, x1, y1, brush, alpha = 255, blendFn = sourceOverPerfect) {
2582
- const brushSize = brush.size;
2583
- const {
2584
- x: bx,
2585
- y: by,
2586
- w: bw,
2587
- h: bh
2588
- } = getCircleBrushOrPencilStrokeBounds2(x0, y0, x1, y1, brushSize, strokeBoundsOut);
2589
- if (bw <= 0 || bh <= 0) return;
2590
- mask.data = new Uint8Array(bw * bh);
2591
- mask.w = bw;
2592
- mask.h = bh;
2593
- const maskData = mask.data;
2594
- const brushData = brush.data;
2595
- const minOffset = brush.minOffset;
2596
- const target = writer.config.target;
2597
- const targetWidth = target.width;
2598
- const targetHeight = target.height;
2599
- forEachLinePoint2(x0, y0, x1, y1, (px, py) => {
2600
- const {
2601
- x: cbx,
2602
- y: cby,
2603
- w: cbw,
2604
- h: cbh
2605
- } = getCircleBrushOrPencilBounds2(px, py, brushSize, targetWidth, targetHeight, circleBrushBounds);
2606
- writer.accumulator.storeRegionBeforeState(cbx, cby, cbw, cbh);
2607
- const startX = Math.max(bx, cbx);
2608
- const startY = Math.max(by, cby);
2609
- const endX = Math.min(bx + bw, cbx + cbw);
2610
- const endY = Math.min(by + bh, cby + cbh);
2611
- const unclippedStartX = Math.floor(px + minOffset);
2612
- const unclippedStartY = Math.floor(py + minOffset);
2613
- for (let my = startY; my < endY; my++) {
2614
- const strokeMaskY = my - by;
2615
- const strokeMaskRowOffset = strokeMaskY * bw;
2616
- const brushY = my - unclippedStartY;
2617
- const brushRowOffset = brushY * brushSize;
2618
- for (let mx = startX; mx < endX; mx++) {
2619
- const brushX = mx - unclippedStartX;
2620
- const brushVal = brushData[brushRowOffset + brushX];
2621
- if (brushVal > 0) {
2622
- const strokeMaskIdx = strokeMaskRowOffset + (mx - bx);
2623
- if (brushVal > maskData[strokeMaskIdx]) {
2624
- maskData[strokeMaskIdx] = brushVal;
2625
- }
2626
- }
2627
- }
2628
- }
2629
- });
2630
- blendColorPixelOptions.blendFn = blendFn;
2631
- blendColorPixelOptions.alpha = alpha;
2632
- blendColorPixelOptions.x = bx;
2633
- blendColorPixelOptions.y = by;
2634
- blendColorPixelOptions.w = bw;
2635
- blendColorPixelOptions.h = bh;
2636
- blendColorPixelDataAlphaMask2(target, color, mask, blendColorPixelOptions);
2561
+ blendPixelDataAlphaMask(src, mask, opts = {}) {
2562
+ const x = opts.x ?? 0;
2563
+ const y = opts.y ?? 0;
2564
+ const w = opts.w ?? src.width;
2565
+ const h = opts.h ?? src.height;
2566
+ const didChange = writer.accumulator.storeRegionBeforeState(x, y, w, h);
2567
+ return didChange(blendPixelDataAlphaMask2(writer.config.target, src, mask, opts));
2637
2568
  }
2638
2569
  };
2639
2570
  });
2640
2571
 
2641
- // src/PixelData/blendColorPixelDataBinaryMask.ts
2642
- function blendColorPixelDataBinaryMask(dst, color, mask, opts = {}) {
2643
- const targetX = opts.x ?? 0;
2644
- const targetY = opts.y ?? 0;
2645
- let w = opts.w ?? mask.w;
2646
- let h = opts.h ?? mask.h;
2647
- const globalAlpha = opts.alpha ?? 255;
2648
- const blendFn = opts.blendFn ?? sourceOverPerfect;
2649
- const mx = opts.mx ?? 0;
2650
- const my = opts.my ?? 0;
2651
- const invertMask = opts.invertMask ?? false;
2572
+ // src/PixelData/blendPixelDataBinaryMask.ts
2573
+ function blendPixelDataBinaryMask(dst, src, binaryMask, opts = {}) {
2574
+ const {
2575
+ x: targetX = 0,
2576
+ y: targetY = 0,
2577
+ sx: sourceX = 0,
2578
+ sy: sourceY = 0,
2579
+ w: width = src.width,
2580
+ h: height = src.height,
2581
+ alpha: globalAlpha = 255,
2582
+ blendFn = sourceOverPerfect,
2583
+ mx = 0,
2584
+ my = 0,
2585
+ invertMask = false
2586
+ } = opts;
2652
2587
  if (globalAlpha === 0) return false;
2653
- const baseSrcAlpha = color >>> 24;
2654
- const isOverwrite = blendFn.isOverwrite || false;
2655
- if (baseSrcAlpha === 0 && !isOverwrite) return false;
2656
2588
  let x = targetX;
2657
2589
  let y = targetY;
2590
+ let sx = sourceX;
2591
+ let sy = sourceY;
2592
+ let w = width;
2593
+ let h = height;
2594
+ if (sx < 0) {
2595
+ x -= sx;
2596
+ w += sx;
2597
+ sx = 0;
2598
+ }
2599
+ if (sy < 0) {
2600
+ y -= sy;
2601
+ h += sy;
2602
+ sy = 0;
2603
+ }
2604
+ w = Math.min(w, src.width - sx);
2605
+ h = Math.min(h, src.height - sy);
2658
2606
  if (x < 0) {
2607
+ sx -= x;
2659
2608
  w += x;
2660
2609
  x = 0;
2661
2610
  }
2662
2611
  if (y < 0) {
2612
+ sy -= y;
2663
2613
  h += y;
2664
2614
  y = 0;
2665
2615
  }
2666
2616
  const actualW = Math.min(w, dst.width - x);
2667
2617
  const actualH = Math.min(h, dst.height - y);
2668
2618
  if (actualW <= 0 || actualH <= 0) return false;
2669
- let baseColorWithGlobalAlpha = color;
2670
- if (globalAlpha < 255) {
2671
- const a = baseSrcAlpha * globalAlpha + 128 >> 8;
2672
- if (a === 0 && !isOverwrite) return false;
2673
- baseColorWithGlobalAlpha = (color & 16777215 | a << 24) >>> 0;
2674
- }
2675
2619
  const dx = x - targetX | 0;
2676
2620
  const dy = y - targetY | 0;
2677
2621
  const dst32 = dst.data32;
2622
+ const src32 = src.data32;
2678
2623
  const dw = dst.width;
2679
- const mPitch = mask.w;
2680
- const maskData = mask.data;
2624
+ const sw = src.width;
2625
+ const mPitch = binaryMask.w;
2626
+ const maskData = binaryMask.data;
2681
2627
  let dIdx = y * dw + x | 0;
2628
+ let sIdx = sy * sw + sx | 0;
2682
2629
  let mIdx = (my + dy) * mPitch + (mx + dx) | 0;
2683
2630
  const dStride = dw - actualW | 0;
2631
+ const sStride = sw - actualW | 0;
2684
2632
  const mStride = mPitch - actualW | 0;
2685
2633
  const skipVal = invertMask ? 1 : 0;
2634
+ const isOpaque = globalAlpha === 255;
2635
+ const isOverwrite = blendFn.isOverwrite || false;
2686
2636
  let didChange = false;
2687
2637
  for (let iy = 0; iy < actualH; iy++) {
2688
2638
  for (let ix = 0; ix < actualW; ix++) {
2689
2639
  if (maskData[mIdx] === skipVal) {
2690
2640
  dIdx++;
2641
+ sIdx++;
2642
+ mIdx++;
2643
+ continue;
2644
+ }
2645
+ const srcCol = src32[sIdx];
2646
+ const srcAlpha = srcCol >>> 24;
2647
+ if (srcAlpha === 0 && !isOverwrite) {
2648
+ dIdx++;
2649
+ sIdx++;
2691
2650
  mIdx++;
2692
2651
  continue;
2693
2652
  }
2653
+ let finalCol = srcCol;
2654
+ if (!isOpaque) {
2655
+ const a = srcAlpha * globalAlpha + 128 >> 8;
2656
+ if (a === 0 && !isOverwrite) {
2657
+ dIdx++;
2658
+ sIdx++;
2659
+ mIdx++;
2660
+ continue;
2661
+ }
2662
+ finalCol = (srcCol & 16777215 | a << 24) >>> 0;
2663
+ }
2694
2664
  const current = dst32[dIdx];
2695
- const next = blendFn(baseColorWithGlobalAlpha, current);
2665
+ const next = blendFn(finalCol, dst32[dIdx]);
2696
2666
  if (current !== next) {
2697
2667
  dst32[dIdx] = next;
2698
2668
  didChange = true;
2699
2669
  }
2700
2670
  dIdx++;
2671
+ sIdx++;
2701
2672
  mIdx++;
2702
2673
  }
2703
2674
  dIdx += dStride;
2675
+ sIdx += sStride;
2704
2676
  mIdx += mStride;
2705
2677
  }
2706
2678
  return didChange;
2707
2679
  }
2708
2680
 
2709
- // src/PixelData/blendColorPixelDataCircleMask.ts
2710
- function blendColorPixelDataCircleMask(target, color, centerX, centerY, brush, alpha = 255, blendFn = sourceOverPerfect, scratchOptions = {}, bounds) {
2711
- const b = bounds ?? getCircleBrushOrPencilBounds(centerX, centerY, brush.size, target.width, target.height);
2712
- if (b.w <= 0 || b.h <= 0) return false;
2713
- const unclippedStartX = Math.floor(centerX + brush.minOffset);
2714
- const unclippedStartY = Math.floor(centerY + brush.minOffset);
2715
- const ix = Math.max(unclippedStartX, b.x);
2716
- const iy = Math.max(unclippedStartY, b.y);
2717
- const ir = Math.min(unclippedStartX + brush.w, b.x + b.w);
2718
- const ib = Math.min(unclippedStartY + brush.h, b.y + b.h);
2719
- const iw = ir - ix;
2720
- const ih = ib - iy;
2721
- if (iw <= 0 || ih <= 0) return false;
2722
- scratchOptions.x = ix;
2723
- scratchOptions.y = iy;
2724
- scratchOptions.w = iw;
2725
- scratchOptions.h = ih;
2726
- scratchOptions.mx = ix - unclippedStartX;
2727
- scratchOptions.my = iy - unclippedStartY;
2728
- scratchOptions.alpha = alpha;
2729
- scratchOptions.blendFn = blendFn;
2730
- if (brush.type === 0 /* ALPHA */) {
2731
- return blendColorPixelDataAlphaMask(target, color, brush, scratchOptions);
2732
- }
2733
- if (brush.type === 1 /* BINARY */) {
2734
- return blendColorPixelDataBinaryMask(target, color, brush, scratchOptions);
2735
- }
2736
- return false;
2737
- }
2738
-
2739
- // src/History/PixelMutator/mutatorBlendColorCircleMask.ts
2740
- var defaults5 = {
2741
- blendColorPixelDataCircleMask,
2742
- getCircleBrushOrPencilBounds
2681
+ // src/History/PixelMutator/mutatorBlendPixelDataBinaryMask.ts
2682
+ var defaults6 = {
2683
+ blendPixelDataBinaryMask
2743
2684
  };
2744
- var mutatorBlendColorCircleMask = ((writer, deps = defaults5) => {
2685
+ var mutatorBlendPixelDataBinaryMask = ((writer, deps = defaults6) => {
2745
2686
  const {
2746
- blendColorPixelDataCircleMask: blendColorPixelDataCircleMask2 = defaults5.blendColorPixelDataCircleMask,
2747
- getCircleBrushOrPencilBounds: getCircleBrushOrPencilBounds2 = defaults5.getCircleBrushOrPencilBounds
2687
+ blendPixelDataBinaryMask: blendPixelDataBinaryMask2 = defaults6.blendPixelDataBinaryMask
2748
2688
  } = deps;
2749
- const boundsOut = {
2750
- x: 0,
2751
- y: 0,
2752
- w: 0,
2753
- h: 0
2754
- };
2755
- const blendColorPixelOptions = {
2756
- alpha: 255,
2757
- blendFn: sourceOverPerfect,
2758
- x: 0,
2759
- y: 0,
2760
- w: 0,
2761
- h: 0
2762
- };
2763
2689
  return {
2764
- applyCircleMask(color, centerX, centerY, brush, alpha = 255, blendFn) {
2765
- const target = writer.config.target;
2766
- const b = getCircleBrushOrPencilBounds2(centerX, centerY, brush.size, target.width, target.height, boundsOut);
2767
- const didChange = writer.accumulator.storeRegionBeforeState(b.x, b.y, b.w, b.h);
2768
- return didChange(blendColorPixelDataCircleMask2(target, color, centerX, centerY, brush, alpha, blendFn, blendColorPixelOptions, b));
2690
+ blendPixelDataBinaryMask(src, mask, opts = {}) {
2691
+ const x = opts.x ?? 0;
2692
+ const y = opts.y ?? 0;
2693
+ const w = opts.w ?? src.width;
2694
+ const h = opts.h ?? src.height;
2695
+ const didChange = writer.accumulator.storeRegionBeforeState(x, y, w, h);
2696
+ return didChange(blendPixelDataBinaryMask2(writer.config.target, src, mask, opts));
2769
2697
  }
2770
2698
  };
2771
2699
  });
2772
2700
 
2773
- // src/History/PixelMutator/mutatorApplyCirclePencil.ts
2774
- var defaults6 = {
2775
- applyCircleMaskToPixelData: blendColorPixelDataCircleMask,
2776
- getCircleBrushOrPencilBounds
2777
- };
2778
- var mutatorApplyCirclePencil = ((writer, deps = defaults6) => {
2701
+ // src/PixelData/fillPixelDataFast.ts
2702
+ var SCRATCH_RECT = makeClippedRect();
2703
+ function fillPixelDataFast(dst, color, _x, _y, _w, _h) {
2704
+ let x;
2705
+ let y;
2706
+ let w;
2707
+ let h;
2708
+ if (typeof _x === "object") {
2709
+ x = _x.x ?? 0;
2710
+ y = _x.y ?? 0;
2711
+ w = _x.w ?? dst.width;
2712
+ h = _x.h ?? dst.height;
2713
+ } else if (typeof _x === "number") {
2714
+ x = _x;
2715
+ y = _y;
2716
+ w = _w;
2717
+ h = _h;
2718
+ } else {
2719
+ x = 0;
2720
+ y = 0;
2721
+ w = dst.width;
2722
+ h = dst.height;
2723
+ }
2724
+ const clip = resolveRectClipping(x, y, w, h, dst.width, dst.height, SCRATCH_RECT);
2725
+ if (!clip.inBounds) return;
2779
2726
  const {
2780
- applyCircleMaskToPixelData = defaults6.applyCircleMaskToPixelData,
2781
- getCircleBrushOrPencilBounds: getCircleBrushOrPencilBounds2 = defaults6.getCircleBrushOrPencilBounds
2782
- } = deps;
2783
- const boundsOut = {
2784
- x: 0,
2785
- y: 0,
2786
- w: 0,
2787
- h: 0
2788
- };
2789
- return {
2790
- applyCirclePencil(color, centerX, centerY, brush, alpha = 255, blendFn) {
2791
- const target = writer.config.target;
2792
- const b = getCircleBrushOrPencilBounds2(centerX, centerY, brush.size, target.width, target.height, boundsOut);
2793
- const didChange = writer.accumulator.storeRegionBeforeState(b.x, b.y, b.w, b.h);
2794
- return didChange(applyCircleMaskToPixelData(target, color, centerX, centerY, brush, alpha, blendFn, b));
2795
- }
2796
- };
2797
- });
2727
+ x: finalX,
2728
+ y: finalY,
2729
+ w: actualW,
2730
+ h: actualH
2731
+ } = clip;
2732
+ const dst32 = dst.data32;
2733
+ const dw = dst.width;
2734
+ if (actualW === dw && actualH === dst.height && finalX === 0 && finalY === 0) {
2735
+ dst32.fill(color);
2736
+ return;
2737
+ }
2738
+ for (let iy = 0; iy < actualH; iy++) {
2739
+ const start = (finalY + iy) * dw + finalX;
2740
+ const end = start + actualW;
2741
+ dst32.fill(color, start, end);
2742
+ }
2743
+ }
2798
2744
 
2799
- // src/History/PixelMutator/mutatorApplyCirclePencilStroke.ts
2745
+ // src/History/PixelMutator/mutatorClear.ts
2800
2746
  var defaults7 = {
2801
- forEachLinePoint,
2802
- blendColorPixelDataBinaryMask,
2803
- getCircleBrushOrPencilBounds,
2804
- getCircleBrushOrPencilStrokeBounds
2747
+ fillPixelData: fillPixelDataFast
2805
2748
  };
2806
- var mutatorApplyCirclePencilStroke = ((writer, deps = defaults7) => {
2749
+ var mutatorClear = ((writer, deps = defaults7) => {
2807
2750
  const {
2808
- forEachLinePoint: forEachLinePoint2 = defaults7.forEachLinePoint,
2809
- blendColorPixelDataBinaryMask: blendColorPixelDataBinaryMask2 = defaults7.blendColorPixelDataBinaryMask,
2810
- getCircleBrushOrPencilStrokeBounds: getCircleBrushOrPencilStrokeBounds2 = defaults7.getCircleBrushOrPencilStrokeBounds,
2811
- getCircleBrushOrPencilBounds: getCircleBrushOrPencilBounds2 = defaults7.getCircleBrushOrPencilBounds
2751
+ fillPixelData: fillPixelData2 = defaults7.fillPixelData
2812
2752
  } = deps;
2813
- const strokeBoundsOut = {
2814
- x: 0,
2815
- y: 0,
2816
- w: 0,
2817
- h: 0
2818
- };
2819
- const circlePencilBounds = {
2820
- x: 0,
2821
- y: 0,
2822
- w: 0,
2823
- h: 0
2824
- };
2825
- const blendColorPixelOptions = {
2826
- alpha: 255,
2827
- blendFn: sourceOverPerfect,
2828
- x: 0,
2829
- y: 0,
2830
- w: 0,
2831
- h: 0
2832
- };
2833
- const mask = {
2834
- type: 1 /* BINARY */,
2835
- data: null,
2836
- w: 0,
2837
- h: 0
2838
- };
2839
2753
  return {
2840
- applyCirclePencilStroke(color, x0, y0, x1, y1, brush, alpha = 255, blendFn = sourceOverPerfect) {
2841
- const {
2842
- x: bx,
2843
- y: by,
2844
- w: bw,
2845
- h: bh
2846
- } = getCircleBrushOrPencilStrokeBounds2(x0, y0, x1, y1, brush.size, strokeBoundsOut);
2847
- if (bw <= 0 || bh <= 0) return;
2848
- mask.data = new Uint8Array(bw * bh);
2849
- mask.w = bw;
2850
- mask.h = bh;
2851
- const maskData = mask.data;
2754
+ clear(rect = {}) {
2852
2755
  const target = writer.config.target;
2853
- const targetWidth = target.width;
2854
- const targetHeight = target.height;
2855
- forEachLinePoint2(x0, y0, x1, y1, (px, py) => {
2856
- const {
2857
- x: cbx,
2858
- y: cby,
2859
- w: cbw,
2860
- h: cbh
2861
- } = getCircleBrushOrPencilBounds2(px, py, brush.size, targetWidth, targetHeight, circlePencilBounds);
2862
- writer.accumulator.storeRegionBeforeState(cbx, cby, cbw, cbh);
2863
- const unclippedStartX = Math.floor(px + brush.minOffset);
2864
- const unclippedStartY = Math.floor(py + brush.minOffset);
2865
- const startX = Math.max(bx, unclippedStartX);
2866
- const startY = Math.max(by, unclippedStartY);
2867
- const endX = Math.min(bx + bw, unclippedStartX + brush.w);
2868
- const endY = Math.min(by + bh, unclippedStartY + brush.h);
2869
- for (let my = startY; my < endY; my++) {
2870
- const brushY = my - unclippedStartY;
2871
- const maskRowOffset = (my - by) * bw;
2872
- const brushRowOffset = brushY * brush.w;
2873
- for (let mx = startX; mx < endX; mx++) {
2874
- const brushX = mx - unclippedStartX;
2875
- const brushAlpha = brush.data[brushRowOffset + brushX];
2876
- if (brushAlpha > 0) {
2877
- const maskIdx = maskRowOffset + (mx - bx);
2878
- maskData[maskIdx] = brushAlpha;
2879
- }
2880
- }
2881
- }
2882
- });
2883
- blendColorPixelOptions.blendFn = blendFn;
2884
- blendColorPixelOptions.alpha = alpha;
2885
- blendColorPixelOptions.x = bx;
2886
- blendColorPixelOptions.y = by;
2887
- blendColorPixelOptions.w = bw;
2888
- blendColorPixelOptions.h = bh;
2889
- blendColorPixelDataBinaryMask2(target, color, mask, blendColorPixelOptions);
2756
+ const x = rect.x ?? 0;
2757
+ const y = rect.y ?? 0;
2758
+ const w = rect.w ?? target.width;
2759
+ const h = rect.h ?? target.height;
2760
+ writer.accumulator.storeRegionBeforeState(x, y, w, h);
2761
+ fillPixelData2(target, 0, x, y, w, h);
2890
2762
  }
2891
2763
  };
2892
2764
  });
2893
2765
 
2894
- // src/Rect/getRectBrushOrPencilBounds.ts
2895
- function getRectBrushOrPencilBounds(centerX, centerY, brushWidth, brushHeight, targetWidth, targetHeight, out) {
2896
- const startX = Math.floor(centerX - brushWidth / 2);
2897
- const startY = Math.floor(centerY - brushHeight / 2);
2898
- const endX = startX + brushWidth;
2899
- const endY = startY + brushHeight;
2900
- const res = out ?? {
2901
- x: 0,
2902
- y: 0,
2903
- w: 0,
2904
- h: 0
2905
- };
2906
- const cStartX = Math.max(0, startX);
2907
- const cStartY = Math.max(0, startY);
2908
- const cEndX = Math.min(targetWidth, endX);
2909
- const cEndY = Math.min(targetHeight, endY);
2910
- const w = cEndX - cStartX;
2911
- const h = cEndY - cStartY;
2912
- res.x = cStartX;
2913
- res.y = cStartY;
2914
- res.w = w < 0 ? 0 : w;
2915
- res.h = h < 0 ? 0 : h;
2916
- return res;
2917
- }
2918
-
2919
- // src/PixelData/applyRectBrushToPixelData.ts
2920
- function applyRectBrushToPixelData(target, color, centerX, centerY, brushWidth, brushHeight, alpha = 255, fallOff, blendFn = sourceOverPerfect, bounds) {
2921
- const targetWidth = target.width;
2922
- const targetHeight = target.height;
2923
- const b = bounds ?? getRectBrushOrPencilBounds(centerX, centerY, brushWidth, brushHeight, targetWidth, targetHeight);
2924
- if (b.w <= 0 || b.h <= 0) {
2925
- return false;
2766
+ // src/PixelData/fillPixelData.ts
2767
+ var SCRATCH_RECT2 = makeClippedRect();
2768
+ function fillPixelData(dst, color, _x, _y, _w, _h) {
2769
+ let x;
2770
+ let y;
2771
+ let w;
2772
+ let h;
2773
+ if (typeof _x === "object") {
2774
+ x = _x.x ?? 0;
2775
+ y = _x.y ?? 0;
2776
+ w = _x.w ?? dst.width;
2777
+ h = _x.h ?? dst.height;
2778
+ } else if (typeof _x === "number") {
2779
+ x = _x;
2780
+ y = _y;
2781
+ w = _w;
2782
+ h = _h;
2783
+ } else {
2784
+ x = 0;
2785
+ y = 0;
2786
+ w = dst.width;
2787
+ h = dst.height;
2926
2788
  }
2927
- const data32 = target.data32;
2928
- const baseColor = color & 16777215;
2929
- const baseSrcAlpha = color >>> 24;
2930
- const isOpaque = alpha === 255;
2931
- const invHalfW = 1 / (brushWidth / 2);
2932
- const invHalfH = 1 / (brushHeight / 2);
2933
- const centerOffsetX = brushWidth % 2 === 0 ? 0.5 : 0;
2934
- const centerOffsetY = brushHeight % 2 === 0 ? 0.5 : 0;
2935
- const fCenterX = Math.floor(centerX);
2936
- const fCenterY = Math.floor(centerY);
2937
- const endX = b.x + b.w;
2938
- const endY = b.y + b.h;
2939
- const isOverwrite = blendFn.isOverwrite;
2940
- let didChange = false;
2941
- for (let py = b.y; py < endY; py++) {
2942
- const rowOffset = py * targetWidth;
2943
- const dy = Math.abs(py - fCenterY + centerOffsetY) * invHalfH;
2944
- for (let px = b.x; px < endX; px++) {
2945
- const idx = rowOffset + px;
2946
- const dx = Math.abs(px - fCenterX + centerOffsetX) * invHalfW;
2947
- const dist = dx > dy ? dx : dy;
2948
- const strength = fallOff(dist);
2949
- const maskVal = strength * 255 | 0;
2950
- if (maskVal <= 0) continue;
2951
- let weight = alpha;
2952
- if (isOpaque) {
2953
- weight = maskVal;
2954
- } else if (maskVal !== 255) {
2955
- weight = maskVal * alpha + 128 >> 8;
2956
- }
2957
- let finalCol = color;
2958
- if (weight < 255) {
2959
- const a = baseSrcAlpha * weight + 128 >> 8;
2960
- if (a === 0 && !isOverwrite) continue;
2961
- finalCol = (a << 24 | baseColor) >>> 0;
2962
- }
2963
- const current = data32[idx];
2964
- const next = blendFn(finalCol, current);
2965
- if (current !== next) {
2966
- data32[idx] = next;
2967
- didChange = true;
2789
+ const clip = resolveRectClipping(x, y, w, h, dst.width, dst.height, SCRATCH_RECT2);
2790
+ if (!clip.inBounds) return false;
2791
+ const {
2792
+ x: finalX,
2793
+ y: finalY,
2794
+ w: actualW,
2795
+ h: actualH
2796
+ } = clip;
2797
+ const dst32 = dst.data32;
2798
+ const dw = dst.width;
2799
+ let hasChanged = false;
2800
+ for (let iy = 0; iy < actualH; iy++) {
2801
+ const rowOffset = (finalY + iy) * dw;
2802
+ const start = rowOffset + finalX;
2803
+ const end = start + actualW;
2804
+ for (let i = start; i < end; i++) {
2805
+ if (dst32[i] !== color) {
2806
+ dst32[i] = color;
2807
+ hasChanged = true;
2968
2808
  }
2969
2809
  }
2970
2810
  }
2971
- return didChange;
2811
+ return hasChanged;
2972
2812
  }
2973
2813
 
2974
- // src/History/PixelMutator/mutatorApplyRectBrush.ts
2814
+ // src/History/PixelMutator/mutatorFill.ts
2975
2815
  var defaults8 = {
2976
- applyRectBrushToPixelData,
2977
- getRectBrushOrPencilBounds
2816
+ fillPixelData
2978
2817
  };
2979
- var mutatorApplyRectBrush = ((writer, deps = defaults8) => {
2818
+ var mutatorFill = ((writer, deps = defaults8) => {
2980
2819
  const {
2981
- applyRectBrushToPixelData: applyRectBrushToPixelData2 = defaults8.applyRectBrushToPixelData,
2982
- getRectBrushOrPencilBounds: getRectBrushOrPencilBounds2 = defaults8.getRectBrushOrPencilBounds
2820
+ fillPixelData: fillPixelData2 = defaults8.fillPixelData
2983
2821
  } = deps;
2984
- const boundsOut = {
2985
- x: 0,
2986
- y: 0,
2987
- w: 0,
2988
- h: 0
2989
- };
2990
2822
  return {
2991
- applyRectBrush(color, centerX, centerY, brushWidth, brushHeight, alpha = 255, fallOff, blendFn) {
2823
+ fill(color, x = 0, y = 0, w = writer.config.target.width, h = writer.config.target.height) {
2992
2824
  const target = writer.config.target;
2993
- const b = getRectBrushOrPencilBounds2(centerX, centerY, brushWidth, brushHeight, target.width, target.height, boundsOut);
2994
- const didChange = writer.accumulator.storeRegionBeforeState(b.x, b.y, b.w, b.h);
2995
- return didChange(applyRectBrushToPixelData2(target, color, centerX, centerY, brushWidth, brushHeight, alpha, fallOff, blendFn, b));
2825
+ const didChange = writer.accumulator.storeRegionBeforeState(x, y, w, h);
2826
+ return didChange(fillPixelData2(target, color, x, y, w, h));
2996
2827
  }
2997
2828
  };
2998
2829
  });
2999
-
3000
- // src/Rect/getRectBrushOrPencilStrokeBounds.ts
3001
- function getRectBrushOrPencilStrokeBounds(x0, y0, x1, y1, brushWidth, brushHeight, result) {
3002
- const halfW = brushWidth / 2;
3003
- const halfH = brushHeight / 2;
3004
- const minX = Math.min(x0, x1) - halfW;
3005
- const minY = Math.min(y0, y1) - halfH;
3006
- const maxX = Math.max(x0, x1) + halfW;
3007
- const maxY = Math.max(y0, y1) + halfH;
3008
- result.x = Math.floor(minX);
3009
- result.y = Math.floor(minY);
3010
- result.w = Math.ceil(maxX - minX);
3011
- result.h = Math.ceil(maxY - minY);
3012
- return result;
3013
- }
3014
-
3015
- // src/History/PixelMutator/mutatorApplyRectBrushStroke.ts
3016
- var defaults9 = {
3017
- forEachLinePoint,
3018
- blendColorPixelDataAlphaMask,
3019
- getRectBrushOrPencilBounds,
3020
- getRectBrushOrPencilStrokeBounds
3021
- };
3022
- var mutatorApplyRectBrushStroke = ((writer, deps = defaults9) => {
2830
+ var mutatorFillRect = ((writer, deps = defaults8) => {
3023
2831
  const {
3024
- forEachLinePoint: forEachLinePoint2 = defaults9.forEachLinePoint,
3025
- blendColorPixelDataAlphaMask: blendColorPixelDataAlphaMask2 = defaults9.blendColorPixelDataAlphaMask,
3026
- getRectBrushOrPencilBounds: getRectBrushOrPencilBounds2 = defaults9.getRectBrushOrPencilBounds,
3027
- getRectBrushOrPencilStrokeBounds: getRectBrushOrPencilStrokeBounds2 = defaults9.getRectBrushOrPencilStrokeBounds
2832
+ fillPixelData: fillPixelData2 = defaults8.fillPixelData
3028
2833
  } = deps;
3029
- const strokeBoundsOut = {
3030
- x: 0,
3031
- y: 0,
3032
- w: 0,
3033
- h: 0
3034
- };
3035
- const rectBrushBounds = {
3036
- x: 0,
3037
- y: 0,
3038
- w: 0,
3039
- h: 0
3040
- };
3041
- const blendColorPixelOptions = {
3042
- alpha: 255,
3043
- blendFn: sourceOverPerfect,
3044
- x: 0,
3045
- y: 0,
3046
- w: 0,
3047
- h: 0
3048
- };
3049
- const mask = {
3050
- type: 0 /* ALPHA */,
3051
- data: null,
3052
- w: 0,
3053
- h: 0
3054
- };
3055
2834
  return {
3056
- applyRectBrushStroke(color, x0, y0, x1, y1, brushWidth, brushHeight, alpha = 255, fallOff, blendFn = sourceOverPerfect) {
3057
- const {
3058
- x: bx,
3059
- y: by,
3060
- w: bw,
3061
- h: bh
3062
- } = getRectBrushOrPencilStrokeBounds2(x0, y0, x1, y1, brushWidth, brushHeight, strokeBoundsOut);
3063
- if (bw <= 0 || bh <= 0) return;
3064
- mask.data = new Uint8Array(bw * bh);
3065
- mask.w = bw;
3066
- mask.h = bh;
3067
- const maskData = mask.data;
3068
- const halfW = brushWidth / 2;
3069
- const halfH = brushHeight / 2;
3070
- const invHalfW = 1 / halfW;
3071
- const invHalfH = 1 / halfH;
3072
- const centerOffsetX = brushWidth % 2 === 0 ? 0.5 : 0;
3073
- const centerOffsetY = brushHeight % 2 === 0 ? 0.5 : 0;
2835
+ fillRect(color, rect) {
3074
2836
  const target = writer.config.target;
3075
- const targetWidth = target.width;
3076
- const targetHeight = target.height;
3077
- forEachLinePoint2(x0, y0, x1, y1, (px, py) => {
3078
- const {
3079
- x: rbx,
3080
- y: rby,
3081
- w: rbw,
3082
- h: rbh
3083
- } = getRectBrushOrPencilBounds2(px, py, brushWidth, brushHeight, targetWidth, targetHeight, rectBrushBounds);
3084
- writer.accumulator.storeRegionBeforeState(rbx, rby, rbw, rbh);
3085
- const startX = Math.max(bx, rbx);
3086
- const startY = Math.max(by, rby);
3087
- const endX = Math.min(bx + bw, rbx + rbw);
3088
- const endY = Math.min(by + bh, rby + rbh);
3089
- const fPx = Math.floor(px);
3090
- const fPy = Math.floor(py);
3091
- for (let my = startY; my < endY; my++) {
3092
- const dy = Math.abs(my - fPy + centerOffsetY) * invHalfH;
3093
- const maskRowOffset = (my - by) * bw;
3094
- for (let mx = startX; mx < endX; mx++) {
3095
- const dx = Math.abs(mx - fPx + centerOffsetX) * invHalfW;
3096
- const maskIdx = maskRowOffset + (mx - bx);
3097
- const dist = dx > dy ? dx : dy;
3098
- const strength = fallOff(dist);
3099
- if (strength > 0) {
3100
- const intensity = strength * 255 | 0;
3101
- if (intensity > maskData[maskIdx]) {
3102
- maskData[maskIdx] = intensity;
3103
- }
3104
- }
3105
- }
3106
- }
3107
- });
3108
- blendColorPixelOptions.blendFn = blendFn;
3109
- blendColorPixelOptions.alpha = alpha;
3110
- blendColorPixelOptions.x = bx;
3111
- blendColorPixelOptions.y = by;
3112
- blendColorPixelOptions.w = bw;
3113
- blendColorPixelOptions.h = bh;
3114
- blendColorPixelDataAlphaMask2(target, color, mask, blendColorPixelOptions);
2837
+ const didChange = writer.accumulator.storeRegionBeforeState(rect.x, rect.y, rect.w, rect.h);
2838
+ return didChange(fillPixelData2(target, color, rect.x, rect.y, rect.w, rect.h));
3115
2839
  }
3116
2840
  };
3117
2841
  });
3118
2842
 
3119
- // src/History/PixelMutator/mutatorApplyRectPencil.ts
3120
- var defaults10 = {
3121
- applyRectBrushToPixelData,
3122
- getRectBrushOrPencilBounds,
3123
- fallOff: () => 1
3124
- };
3125
- var mutatorApplyRectPencil = ((writer, deps = defaults10) => {
2843
+ // src/PixelData/fillPixelDataBinaryMask.ts
2844
+ var SCRATCH_RECT3 = makeClippedRect();
2845
+ function fillPixelDataBinaryMask(dst, color, mask, alpha = 255, x = 0, y = 0) {
2846
+ if (alpha === 0) return false;
2847
+ const maskW = mask.w;
2848
+ const maskH = mask.h;
2849
+ const clip = resolveRectClipping(x, y, maskW, maskH, dst.width, dst.height, SCRATCH_RECT3);
2850
+ if (!clip.inBounds) return false;
3126
2851
  const {
3127
- applyRectBrushToPixelData: applyRectBrushToPixelData2 = defaults10.applyRectBrushToPixelData,
3128
- getRectBrushOrPencilBounds: getRectBrushOrPencilBounds2 = defaults10.getRectBrushOrPencilBounds,
3129
- fallOff = defaults10.fallOff
3130
- } = deps;
3131
- const boundsOut = {
3132
- x: 0,
3133
- y: 0,
3134
- w: 0,
3135
- h: 0
3136
- };
3137
- return {
3138
- applyRectPencil(color, centerX, centerY, brushWidth, brushHeight, alpha = 255, blendFn) {
3139
- const target = writer.config.target;
3140
- const b = getRectBrushOrPencilBounds2(centerX, centerY, brushWidth, brushHeight, target.width, target.height, boundsOut);
3141
- const didChange = writer.accumulator.storeRegionBeforeState(b.x, b.y, b.w, b.h);
3142
- return didChange(applyRectBrushToPixelData2(target, color, centerX, centerY, brushWidth, brushHeight, alpha, fallOff, blendFn, b));
3143
- }
3144
- };
3145
- });
3146
-
3147
- // src/History/PixelMutator/mutatorApplyRectPencilStroke.ts
3148
- var defaults11 = {
3149
- forEachLinePoint,
3150
- getRectBrushOrPencilBounds,
3151
- getRectBrushOrPencilStrokeBounds,
3152
- blendColorPixelDataBinaryMask
2852
+ x: finalX,
2853
+ y: finalY,
2854
+ w: actualW,
2855
+ h: actualH
2856
+ } = clip;
2857
+ const maskData = mask.data;
2858
+ const dst32 = dst.data32;
2859
+ const dw = dst.width;
2860
+ let finalCol = color;
2861
+ if (alpha < 255) {
2862
+ const baseSrcAlpha = color >>> 24;
2863
+ const colorRGB = color & 16777215;
2864
+ const a = baseSrcAlpha * alpha + 128 >> 8;
2865
+ finalCol = (colorRGB | a << 24) >>> 0;
2866
+ }
2867
+ let hasChanged = false;
2868
+ for (let iy = 0; iy < actualH; iy++) {
2869
+ const currentY = finalY + iy;
2870
+ const maskY = currentY - y;
2871
+ const maskOffset = maskY * maskW;
2872
+ const dstRowOffset = currentY * dw;
2873
+ for (let ix = 0; ix < actualW; ix++) {
2874
+ const currentX = finalX + ix;
2875
+ const maskX = currentX - x;
2876
+ const maskIndex = maskOffset + maskX;
2877
+ if (maskData[maskIndex]) {
2878
+ const current = dst32[dstRowOffset + currentX];
2879
+ if (current !== finalCol) {
2880
+ dst32[dstRowOffset + currentX] = finalCol;
2881
+ hasChanged = true;
2882
+ }
2883
+ }
2884
+ }
2885
+ }
2886
+ return hasChanged;
2887
+ }
2888
+
2889
+ // src/History/PixelMutator/mutatorFillBinaryMask.ts
2890
+ var defaults9 = {
2891
+ fillPixelDataBinaryMask
3153
2892
  };
3154
- var mutatorApplyRectPencilStroke = ((writer, deps = defaults11) => {
2893
+ var mutatorFillBinaryMask = ((writer, deps = defaults9) => {
3155
2894
  const {
3156
- forEachLinePoint: forEachLinePoint2 = defaults11.forEachLinePoint,
3157
- blendColorPixelDataBinaryMask: blendColorPixelDataBinaryMask2 = defaults11.blendColorPixelDataBinaryMask,
3158
- getRectBrushOrPencilBounds: getRectBrushOrPencilBounds2 = defaults11.getRectBrushOrPencilBounds,
3159
- getRectBrushOrPencilStrokeBounds: getRectBrushOrPencilStrokeBounds2 = defaults11.getRectBrushOrPencilStrokeBounds
2895
+ fillPixelDataBinaryMask: fillPixelDataBinaryMask2 = defaults9.fillPixelDataBinaryMask
3160
2896
  } = deps;
3161
- const strokeBoundsOut = {
3162
- x: 0,
3163
- y: 0,
3164
- w: 0,
3165
- h: 0
3166
- };
3167
- const rectPencilBounds = {
3168
- x: 0,
3169
- y: 0,
3170
- w: 0,
3171
- h: 0
3172
- };
3173
- const blendColorPixelOptions = {
3174
- alpha: 255,
3175
- blendFn: sourceOverPerfect,
3176
- x: 0,
3177
- y: 0,
3178
- w: 0,
3179
- h: 0
3180
- };
3181
- const mask = {
3182
- type: 1 /* BINARY */,
3183
- data: null,
3184
- w: 0,
3185
- h: 0
3186
- };
3187
2897
  return {
3188
- applyRectPencilStroke(color, x0, y0, x1, y1, brushWidth, brushHeight, alpha = 255, blendFn = sourceOverPerfect) {
3189
- const {
3190
- x: bx,
3191
- y: by,
3192
- w: bw,
3193
- h: bh
3194
- } = getRectBrushOrPencilStrokeBounds2(x0, y0, x1, y1, brushWidth, brushHeight, strokeBoundsOut);
3195
- if (bw <= 0 || bh <= 0) return;
3196
- mask.data = new Uint8Array(bw * bh);
3197
- mask.w = bw;
3198
- mask.h = bh;
3199
- const maskData = mask.data;
3200
- const halfW = brushWidth / 2;
3201
- const halfH = brushHeight / 2;
3202
- const centerOffset = brushWidth % 2 === 0 ? 0.5 : 0;
3203
- const target = writer.config.target;
3204
- const targetWidth = target.width;
3205
- const targetHeight = target.height;
3206
- forEachLinePoint2(x0, y0, x1, y1, (px, py) => {
3207
- const {
3208
- x: rbx,
3209
- y: rby,
3210
- w: rbw,
3211
- h: rbh
3212
- } = getRectBrushOrPencilBounds2(px, py, brushWidth, brushHeight, targetWidth, targetHeight, rectPencilBounds);
3213
- writer.accumulator.storeRegionBeforeState(rbx, rby, rbw, rbh);
3214
- const startX = Math.max(bx, rbx);
3215
- const startY = Math.max(by, rby);
3216
- const endX = Math.min(bx + bw, rbx + rbw);
3217
- const endY = Math.min(by + bh, rby + rbh);
3218
- const fPx = Math.floor(px);
3219
- const fPy = Math.floor(py);
3220
- for (let my = startY; my < endY; my++) {
3221
- const dy = Math.abs(my - fPy + centerOffset);
3222
- const maskRowOffset = (my - by) * bw;
3223
- for (let mx = startX; mx < endX; mx++) {
3224
- const dx = Math.abs(mx - fPx + centerOffset);
3225
- const maskIdx = maskRowOffset + (mx - bx);
3226
- if (dx <= halfW && dy <= halfH) {
3227
- maskData[maskIdx] = 1;
3228
- }
3229
- }
3230
- }
3231
- });
3232
- blendColorPixelOptions.blendFn = blendFn;
3233
- blendColorPixelOptions.alpha = alpha;
3234
- blendColorPixelOptions.x = bx;
3235
- blendColorPixelOptions.y = by;
3236
- blendColorPixelOptions.w = bw;
3237
- blendColorPixelOptions.h = bh;
3238
- blendColorPixelDataBinaryMask2(target, color, mask, blendColorPixelOptions);
2898
+ fillBinaryMask(color, mask, alpha = 255, x = 0, y = 0) {
2899
+ const didChange = writer.accumulator.storeRegionBeforeState(x, y, mask.w, mask.h);
2900
+ return didChange(fillPixelDataBinaryMask2(writer.config.target, color, mask, alpha, x, y));
3239
2901
  }
3240
2902
  };
3241
2903
  });
3242
2904
 
3243
- // src/PixelData/blendColorPixelData.ts
3244
- function blendColorPixelData(dst, color, opts = {}) {
2905
+ // src/PixelData/invertPixelData.ts
2906
+ var SCRATCH_RECT4 = makeClippedRect();
2907
+ function invertPixelData(pixelData, opts = {}) {
2908
+ const dst = pixelData;
3245
2909
  const {
3246
2910
  x: targetX = 0,
3247
2911
  y: targetY = 0,
3248
- w: width = dst.width,
3249
- h: height = dst.height,
3250
- alpha: globalAlpha = 255,
3251
- blendFn = sourceOverPerfect
2912
+ w: width = pixelData.width,
2913
+ h: height = pixelData.height,
2914
+ mask,
2915
+ mx = 0,
2916
+ my = 0,
2917
+ invertMask = false
3252
2918
  } = opts;
3253
- if (globalAlpha === 0) return false;
3254
- const baseSrcAlpha = color >>> 24;
3255
- const isOverwrite = blendFn.isOverwrite || false;
3256
- if (baseSrcAlpha === 0 && !isOverwrite) return false;
3257
- let x = targetX;
3258
- let y = targetY;
3259
- let w = width;
3260
- let h = height;
3261
- if (x < 0) {
3262
- w += x;
3263
- x = 0;
3264
- }
3265
- if (y < 0) {
3266
- h += y;
3267
- y = 0;
3268
- }
3269
- const actualW = Math.min(w, dst.width - x);
3270
- const actualH = Math.min(h, dst.height - y);
3271
- if (actualW <= 0 || actualH <= 0) return false;
3272
- let finalSrcColor = color;
3273
- if (globalAlpha < 255) {
3274
- const a = baseSrcAlpha * globalAlpha + 128 >> 8;
3275
- if (a === 0 && !isOverwrite) return false;
3276
- finalSrcColor = (color & 16777215 | a << 24) >>> 0;
3277
- }
2919
+ const clip = resolveRectClipping(targetX, targetY, width, height, dst.width, dst.height, SCRATCH_RECT4);
2920
+ if (!clip.inBounds) return false;
2921
+ const {
2922
+ x,
2923
+ y,
2924
+ w: actualW,
2925
+ h: actualH
2926
+ } = clip;
3278
2927
  const dst32 = dst.data32;
3279
2928
  const dw = dst.width;
3280
- let dIdx = y * dw + x | 0;
3281
- const dStride = dw - actualW | 0;
3282
- let didChange = false;
3283
- for (let iy = 0; iy < actualH; iy++) {
3284
- for (let ix = 0; ix < actualW; ix++) {
3285
- const current = dst32[dIdx];
3286
- const next = blendFn(finalSrcColor, current);
3287
- if (current !== next) {
3288
- dst32[dIdx] = next;
3289
- didChange = true;
2929
+ const mPitch = mask?.w ?? width;
2930
+ const dx = x - targetX;
2931
+ const dy = y - targetY;
2932
+ let dIdx = y * dw + x;
2933
+ let mIdx = (my + dy) * mPitch + (mx + dx);
2934
+ const dStride = dw - actualW;
2935
+ const mStride = mPitch - actualW;
2936
+ if (mask) {
2937
+ const maskData = mask.data;
2938
+ for (let iy = 0; iy < actualH; iy++) {
2939
+ for (let ix = 0; ix < actualW; ix++) {
2940
+ const mVal = maskData[mIdx];
2941
+ const isHit = invertMask ? mVal === 0 : mVal === 1;
2942
+ if (isHit) {
2943
+ dst32[dIdx] = dst32[dIdx] ^ 16777215;
2944
+ }
2945
+ dIdx++;
2946
+ mIdx++;
3290
2947
  }
3291
- dIdx++;
2948
+ dIdx += dStride;
2949
+ mIdx += mStride;
2950
+ }
2951
+ } else {
2952
+ for (let iy = 0; iy < actualH; iy++) {
2953
+ for (let ix = 0; ix < actualW; ix++) {
2954
+ dst32[dIdx] = dst32[dIdx] ^ 16777215;
2955
+ dIdx++;
2956
+ }
2957
+ dIdx += dStride;
3292
2958
  }
3293
- dIdx += dStride;
3294
2959
  }
3295
- return didChange;
2960
+ return true;
3296
2961
  }
3297
2962
 
3298
- // src/History/PixelMutator/mutatorBlendColor.ts
3299
- var defaults12 = {
3300
- blendColorPixelData
2963
+ // src/History/PixelMutator/mutatorInvert.ts
2964
+ var defaults10 = {
2965
+ invertPixelData
3301
2966
  };
3302
- var mutatorBlendColor = ((writer, deps = defaults12) => {
2967
+ var mutatorInvert = ((writer, deps = defaults10) => {
3303
2968
  const {
3304
- blendColorPixelData: blendColorPixelData2 = defaults12.blendColorPixelData
2969
+ invertPixelData: invertPixelData2 = defaults10.invertPixelData
3305
2970
  } = deps;
3306
2971
  return {
3307
- blendColor(color, opts = {}) {
2972
+ invert(opts = {}) {
3308
2973
  const target = writer.config.target;
3309
2974
  const {
3310
2975
  x = 0,
@@ -3313,708 +2978,644 @@ var mutatorBlendColor = ((writer, deps = defaults12) => {
3313
2978
  h = target.height
3314
2979
  } = opts;
3315
2980
  const didChange = writer.accumulator.storeRegionBeforeState(x, y, w, h);
3316
- return didChange(blendColorPixelData2(target, color, opts));
2981
+ return didChange(invertPixelData2(target, opts));
3317
2982
  }
3318
2983
  };
3319
2984
  });
3320
2985
 
3321
- // src/PixelData/blendPixel.ts
3322
- function blendPixel(target, x, y, color, alpha = 255, blendFn = sourceOverPerfect) {
3323
- if (alpha === 0) return false;
3324
- let width = target.width;
3325
- let height = target.height;
3326
- if (x < 0 || x >= width || y < 0 || y >= height) return false;
3327
- let srcAlpha = color >>> 24;
3328
- let isOverwrite = blendFn.isOverwrite;
3329
- if (srcAlpha === 0 && !isOverwrite) return false;
3330
- let dst32 = target.data32;
3331
- let index = y * width + x;
3332
- let finalColor = color;
3333
- if (alpha !== 255) {
3334
- let finalAlpha = srcAlpha * alpha + 128 >> 8;
3335
- if (finalAlpha === 0 && !isOverwrite) return false;
3336
- finalColor = (color & 16777215 | finalAlpha << 24) >>> 0;
2986
+ // src/History/PixelMutator.ts
2987
+ function makeFullPixelMutator(writer) {
2988
+ return {
2989
+ // @sort
2990
+ ...mutatorBlendColor(writer),
2991
+ ...mutatorBlendPixel(writer),
2992
+ ...mutatorBlendPixelData(writer),
2993
+ ...mutatorBlendPixelDataAlphaMask(writer),
2994
+ ...mutatorBlendPixelDataBinaryMask(writer),
2995
+ ...mutatorClear(writer),
2996
+ ...mutatorFill(writer),
2997
+ ...mutatorFillBinaryMask(writer),
2998
+ ...mutatorFillRect(writer),
2999
+ ...mutatorInvert(writer)
3000
+ };
3001
+ }
3002
+
3003
+ // src/ImageData/resizeImageData.ts
3004
+ function resizeImageData(target, newWidth, newHeight, offsetX = 0, offsetY = 0) {
3005
+ const result = new ImageData(newWidth, newHeight);
3006
+ const {
3007
+ width: oldW,
3008
+ height: oldH,
3009
+ data: oldData
3010
+ } = target;
3011
+ const newData = result.data;
3012
+ const x0 = Math.max(0, offsetX);
3013
+ const y0 = Math.max(0, offsetY);
3014
+ const x1 = Math.min(newWidth, offsetX + oldW);
3015
+ const y1 = Math.min(newHeight, offsetY + oldH);
3016
+ if (x1 <= x0 || y1 <= y0) {
3017
+ return result;
3337
3018
  }
3338
- let current = dst32[index];
3339
- let next = blendFn(finalColor, current);
3340
- if (current !== next) {
3341
- dst32[index] = next;
3342
- return true;
3019
+ const rowCount = y1 - y0;
3020
+ const rowLen = (x1 - x0) * 4;
3021
+ for (let row = 0; row < rowCount; row++) {
3022
+ const dstY = y0 + row;
3023
+ const srcY = dstY - offsetY;
3024
+ const srcX = x0 - offsetX;
3025
+ const dstStart = (dstY * newWidth + x0) * 4;
3026
+ const srcStart = (srcY * oldW + srcX) * 4;
3027
+ newData.set(oldData.subarray(srcStart, srcStart + rowLen), dstStart);
3343
3028
  }
3344
- return false;
3029
+ return result;
3345
3030
  }
3346
3031
 
3347
- // src/History/PixelMutator/mutatorBlendPixel.ts
3348
- var defaults13 = {
3349
- blendPixel
3350
- };
3351
- var mutatorBlendPixel = ((writer, deps = defaults13) => {
3352
- const {
3353
- blendPixel: blendPixel2 = defaults13.blendPixel
3354
- } = deps;
3355
- return {
3356
- blendPixel(x, y, color, alpha, blendFn) {
3357
- const didChange = writer.accumulator.storePixelBeforeState(x, y);
3358
- return didChange(blendPixel2(writer.config.target, x, y, color, alpha, blendFn));
3359
- }
3032
+ // src/Internal/helpers.ts
3033
+ var macro_halfAndFloor = (value) => value >> 1;
3034
+
3035
+ // src/Rect/trimRectBounds.ts
3036
+ function trimRectBounds(x, y, w, h, targetWidth, targetHeight, out) {
3037
+ const res = out ?? {
3038
+ x: 0,
3039
+ y: 0,
3040
+ w: 0,
3041
+ h: 0
3360
3042
  };
3361
- });
3043
+ const left = Math.max(0, x);
3044
+ const top = Math.max(0, y);
3045
+ const right = Math.min(targetWidth, x + w);
3046
+ const bottom = Math.min(targetHeight, y + h);
3047
+ res.x = left;
3048
+ res.y = top;
3049
+ res.w = Math.max(0, right - left);
3050
+ res.h = Math.max(0, bottom - top);
3051
+ return res;
3052
+ }
3362
3053
 
3363
- // src/PixelData/blendPixelData.ts
3364
- function blendPixelData(dst, src, opts = {}) {
3365
- const {
3366
- x: targetX = 0,
3367
- y: targetY = 0,
3368
- sx: sourceX = 0,
3369
- sy: sourceY = 0,
3370
- w: width = src.width,
3371
- h: height = src.height,
3372
- alpha: globalAlpha = 255,
3373
- blendFn = sourceOverPerfect
3374
- } = opts;
3375
- if (globalAlpha === 0) return false;
3376
- let x = targetX;
3377
- let y = targetY;
3378
- let sx = sourceX;
3379
- let sy = sourceY;
3380
- let w = width;
3381
- let h = height;
3382
- if (sx < 0) {
3383
- x -= sx;
3384
- w += sx;
3385
- sx = 0;
3386
- }
3387
- if (sy < 0) {
3388
- y -= sy;
3389
- h += sy;
3390
- sy = 0;
3391
- }
3392
- w = Math.min(w, src.width - sx);
3393
- h = Math.min(h, src.height - sy);
3394
- if (x < 0) {
3395
- sx -= x;
3396
- w += x;
3397
- x = 0;
3398
- }
3399
- if (y < 0) {
3400
- sy -= y;
3401
- h += y;
3402
- y = 0;
3403
- }
3404
- const actualW = Math.min(w, dst.width - x);
3405
- const actualH = Math.min(h, dst.height - y);
3406
- if (actualW <= 0 || actualH <= 0) return false;
3407
- const dst32 = dst.data32;
3408
- const src32 = src.data32;
3409
- const dw = dst.width;
3410
- const sw = src.width;
3411
- let dIdx = y * dw + x | 0;
3412
- let sIdx = sy * sw + sx | 0;
3413
- const dStride = dw - actualW | 0;
3414
- const sStride = sw - actualW | 0;
3415
- const isOpaque = globalAlpha === 255;
3416
- const isOverwrite = blendFn.isOverwrite;
3417
- let didChange = false;
3418
- for (let iy = 0; iy < actualH; iy++) {
3419
- for (let ix = 0; ix < actualW; ix++) {
3420
- const srcCol = src32[sIdx];
3421
- const srcAlpha = srcCol >>> 24;
3422
- if (srcAlpha === 0 && !isOverwrite) {
3423
- dIdx++;
3424
- sIdx++;
3425
- continue;
3426
- }
3427
- let finalCol = srcCol;
3428
- if (!isOpaque) {
3429
- const a = srcAlpha * globalAlpha + 128 >> 8;
3430
- if (a === 0 && !isOverwrite) {
3431
- dIdx++;
3432
- sIdx++;
3433
- continue;
3434
- }
3435
- finalCol = (srcCol & 16777215 | a << 24) >>> 0;
3436
- }
3437
- const current = dst32[dIdx];
3438
- const next = blendFn(finalCol, dst32[dIdx]);
3439
- if (current !== next) {
3440
- dst32[dIdx] = next;
3441
- didChange = true;
3442
- }
3443
- dIdx++;
3444
- sIdx++;
3445
- }
3446
- dIdx += dStride;
3447
- sIdx += sStride;
3448
- }
3449
- return didChange;
3450
- }
3451
-
3452
- // src/History/PixelMutator/mutatorBlendPixelData.ts
3453
- var defaults14 = {
3454
- blendPixelData
3455
- };
3456
- var mutatorBlendPixelData = ((writer, deps = defaults14) => {
3457
- const {
3458
- blendPixelData: blendPixelData2 = defaults14.blendPixelData
3459
- } = deps;
3460
- return {
3461
- blendPixelData(src, opts = {}) {
3462
- const {
3463
- x = 0,
3464
- y = 0,
3465
- w = src.width,
3466
- h = src.height
3467
- } = opts;
3468
- const didChange = writer.accumulator.storeRegionBeforeState(x, y, w, h);
3469
- return didChange(blendPixelData2(writer.config.target, src, opts));
3470
- }
3471
- };
3472
- });
3473
-
3474
- // src/PixelData/blendPixelDataAlphaMask.ts
3475
- function blendPixelDataAlphaMask(dst, src, alphaMask, opts = {}) {
3476
- const {
3477
- x: targetX = 0,
3478
- y: targetY = 0,
3479
- sx: sourceX = 0,
3480
- sy: sourceY = 0,
3481
- w: width = src.width,
3482
- h: height = src.height,
3483
- alpha: globalAlpha = 255,
3484
- blendFn = sourceOverPerfect,
3485
- mx = 0,
3486
- my = 0,
3487
- invertMask = false
3488
- } = opts;
3489
- if (globalAlpha === 0) return false;
3490
- let x = targetX;
3491
- let y = targetY;
3492
- let sx = sourceX;
3493
- let sy = sourceY;
3494
- let w = width;
3495
- let h = height;
3496
- if (sx < 0) {
3497
- x -= sx;
3498
- w += sx;
3499
- sx = 0;
3500
- }
3501
- if (sy < 0) {
3502
- y -= sy;
3503
- h += sy;
3504
- sy = 0;
3505
- }
3506
- w = Math.min(w, src.width - sx);
3507
- h = Math.min(h, src.height - sy);
3508
- if (x < 0) {
3509
- sx -= x;
3510
- w += x;
3511
- x = 0;
3512
- }
3513
- if (y < 0) {
3514
- sy -= y;
3515
- h += y;
3516
- y = 0;
3517
- }
3518
- const actualW = Math.min(w, dst.width - x);
3519
- const actualH = Math.min(h, dst.height - y);
3520
- if (actualW <= 0 || actualH <= 0) return false;
3521
- const dw = dst.width;
3522
- const sw = src.width;
3523
- const mPitch = alphaMask.w;
3524
- const maskData = alphaMask.data;
3525
- const dx = x - targetX | 0;
3526
- const dy = y - targetY | 0;
3527
- const dst32 = dst.data32;
3528
- const src32 = src.data32;
3529
- let dIdx = y * dw + x | 0;
3530
- let sIdx = sy * sw + sx | 0;
3531
- let mIdx = (my + dy) * mPitch + (mx + dx) | 0;
3532
- const dStride = dw - actualW | 0;
3533
- const sStride = sw - actualW | 0;
3534
- const mStride = mPitch - actualW | 0;
3535
- const isOpaque = globalAlpha === 255;
3536
- const isOverwrite = blendFn.isOverwrite || false;
3537
- let didChange = false;
3538
- for (let iy = 0; iy < actualH; iy++) {
3539
- for (let ix = 0; ix < actualW; ix++) {
3540
- const mVal = maskData[mIdx];
3541
- const effM = invertMask ? 255 - mVal : mVal;
3542
- if (effM === 0) {
3543
- dIdx++;
3544
- sIdx++;
3545
- mIdx++;
3546
- continue;
3547
- }
3548
- const srcCol = src32[sIdx];
3549
- const srcAlpha = srcCol >>> 24;
3550
- if (srcAlpha === 0 && !isOverwrite) {
3551
- dIdx++;
3552
- sIdx++;
3553
- mIdx++;
3554
- continue;
3555
- }
3556
- let weight = globalAlpha;
3557
- if (isOpaque) {
3558
- weight = effM;
3559
- } else if (effM !== 255) {
3560
- weight = effM * globalAlpha + 128 >> 8;
3561
- }
3562
- if (weight === 0) {
3563
- dIdx++;
3564
- sIdx++;
3565
- mIdx++;
3566
- continue;
3567
- }
3568
- let finalCol = srcCol;
3569
- if (weight < 255) {
3570
- const a = srcAlpha * weight + 128 >> 8;
3571
- if (a === 0 && !isOverwrite) {
3572
- dIdx++;
3573
- sIdx++;
3574
- mIdx++;
3575
- continue;
3576
- }
3577
- finalCol = (srcCol & 16777215 | a << 24) >>> 0;
3578
- }
3579
- const current = dst32[dIdx];
3580
- const next = blendFn(finalCol, dst32[dIdx]);
3581
- if (current !== next) {
3582
- dst32[dIdx] = next;
3583
- didChange = true;
3584
- }
3585
- dIdx++;
3586
- sIdx++;
3587
- mIdx++;
3588
- }
3589
- dIdx += dStride;
3590
- sIdx += sStride;
3591
- mIdx += mStride;
3592
- }
3593
- return didChange;
3594
- }
3595
-
3596
- // src/History/PixelMutator/mutatorBlendPixelDataAlphaMask.ts
3597
- var defaults15 = {
3598
- blendPixelDataAlphaMask
3599
- };
3600
- var mutatorBlendPixelDataAlphaMask = ((writer, deps = defaults15) => {
3601
- const {
3602
- blendPixelDataAlphaMask: blendPixelDataAlphaMask2 = defaults15.blendPixelDataAlphaMask
3603
- } = deps;
3604
- return {
3605
- blendPixelDataAlphaMask(src, mask, opts = {}) {
3606
- const x = opts.x ?? 0;
3607
- const y = opts.y ?? 0;
3608
- const w = opts.w ?? src.width;
3609
- const h = opts.h ?? src.height;
3610
- const didChange = writer.accumulator.storeRegionBeforeState(x, y, w, h);
3611
- return didChange(blendPixelDataAlphaMask2(writer.config.target, src, mask, opts));
3612
- }
3613
- };
3614
- });
3615
-
3616
- // src/PixelData/blendPixelDataBinaryMask.ts
3617
- function blendPixelDataBinaryMask(dst, src, binaryMask, opts = {}) {
3618
- const {
3619
- x: targetX = 0,
3620
- y: targetY = 0,
3621
- sx: sourceX = 0,
3622
- sy: sourceY = 0,
3623
- w: width = src.width,
3624
- h: height = src.height,
3625
- alpha: globalAlpha = 255,
3626
- blendFn = sourceOverPerfect,
3627
- mx = 0,
3628
- my = 0,
3629
- invertMask = false
3630
- } = opts;
3631
- if (globalAlpha === 0) return false;
3632
- let x = targetX;
3633
- let y = targetY;
3634
- let sx = sourceX;
3635
- let sy = sourceY;
3636
- let w = width;
3637
- let h = height;
3638
- if (sx < 0) {
3639
- x -= sx;
3640
- w += sx;
3641
- sx = 0;
3642
- }
3643
- if (sy < 0) {
3644
- y -= sy;
3645
- h += sy;
3646
- sy = 0;
3647
- }
3648
- w = Math.min(w, src.width - sx);
3649
- h = Math.min(h, src.height - sy);
3650
- if (x < 0) {
3651
- sx -= x;
3652
- w += x;
3653
- x = 0;
3654
- }
3655
- if (y < 0) {
3656
- sy -= y;
3657
- h += y;
3658
- y = 0;
3054
+ // src/Paint/PaintBuffer.ts
3055
+ var PaintBuffer = class {
3056
+ constructor(config, tilePool) {
3057
+ this.config = config;
3058
+ this.tilePool = tilePool;
3059
+ this.lookup = [];
3659
3060
  }
3660
- const actualW = Math.min(w, dst.width - x);
3661
- const actualH = Math.min(h, dst.height - y);
3662
- if (actualW <= 0 || actualH <= 0) return false;
3663
- const dx = x - targetX | 0;
3664
- const dy = y - targetY | 0;
3665
- const dst32 = dst.data32;
3666
- const src32 = src.data32;
3667
- const dw = dst.width;
3668
- const sw = src.width;
3669
- const mPitch = binaryMask.w;
3670
- const maskData = binaryMask.data;
3671
- let dIdx = y * dw + x | 0;
3672
- let sIdx = sy * sw + sx | 0;
3673
- let mIdx = (my + dy) * mPitch + (mx + dx) | 0;
3674
- const dStride = dw - actualW | 0;
3675
- const sStride = sw - actualW | 0;
3676
- const mStride = mPitch - actualW | 0;
3677
- const skipVal = invertMask ? 1 : 0;
3678
- const isOpaque = globalAlpha === 255;
3679
- const isOverwrite = blendFn.isOverwrite || false;
3680
- let didChange = false;
3681
- for (let iy = 0; iy < actualH; iy++) {
3682
- for (let ix = 0; ix < actualW; ix++) {
3683
- if (maskData[mIdx] === skipVal) {
3684
- dIdx++;
3685
- sIdx++;
3686
- mIdx++;
3687
- continue;
3688
- }
3689
- const srcCol = src32[sIdx];
3690
- const srcAlpha = srcCol >>> 24;
3691
- if (srcAlpha === 0 && !isOverwrite) {
3692
- dIdx++;
3693
- sIdx++;
3694
- mIdx++;
3695
- continue;
3696
- }
3697
- let finalCol = srcCol;
3698
- if (!isOpaque) {
3699
- const a = srcAlpha * globalAlpha + 128 >> 8;
3700
- if (a === 0 && !isOverwrite) {
3701
- dIdx++;
3702
- sIdx++;
3703
- mIdx++;
3704
- continue;
3705
- }
3706
- finalCol = (srcCol & 16777215 | a << 24) >>> 0;
3707
- }
3708
- const current = dst32[dIdx];
3709
- const next = blendFn(finalCol, dst32[dIdx]);
3710
- if (current !== next) {
3711
- dst32[dIdx] = next;
3712
- didChange = true;
3061
+ lookup;
3062
+ scratchBounds = {
3063
+ x: 0,
3064
+ y: 0,
3065
+ w: 0,
3066
+ h: 0
3067
+ };
3068
+ eachTileInBounds(bounds, callback) {
3069
+ const {
3070
+ tileShift,
3071
+ targetColumns,
3072
+ targetRows,
3073
+ tileSize
3074
+ } = this.config;
3075
+ const x1 = Math.max(0, bounds.x >> tileShift);
3076
+ const y1 = Math.max(0, bounds.y >> tileShift);
3077
+ const x2 = Math.min(targetColumns - 1, bounds.x + bounds.w - 1 >> tileShift);
3078
+ const y2 = Math.min(targetRows - 1, bounds.y + bounds.h - 1 >> tileShift);
3079
+ if (x1 > x2 || y1 > y2) return;
3080
+ const lookup = this.lookup;
3081
+ const tilePool = this.tilePool;
3082
+ for (let ty = y1; ty <= y2; ty++) {
3083
+ const rowOffset = ty * targetColumns;
3084
+ const tileTop = ty << tileShift;
3085
+ for (let tx = x1; tx <= x2; tx++) {
3086
+ const id = rowOffset + tx;
3087
+ const tile = lookup[id] ?? (lookup[id] = tilePool.getTile(id, tx, ty));
3088
+ const tileLeft = tx << tileShift;
3089
+ const startX = bounds.x > tileLeft ? bounds.x : tileLeft;
3090
+ const startY = bounds.y > tileTop ? bounds.y : tileTop;
3091
+ const maskEndX = bounds.x + bounds.w;
3092
+ const tileEndX = tileLeft + tileSize;
3093
+ const endX = maskEndX < tileEndX ? maskEndX : tileEndX;
3094
+ const maskEndY = bounds.y + bounds.h;
3095
+ const tileEndY = tileTop + tileSize;
3096
+ const endY = maskEndY < tileEndY ? maskEndY : tileEndY;
3097
+ callback(tile, startX, startY, endX - startX, endY - startY);
3713
3098
  }
3714
- dIdx++;
3715
- sIdx++;
3716
- mIdx++;
3717
3099
  }
3718
- dIdx += dStride;
3719
- sIdx += sStride;
3720
- mIdx += mStride;
3721
3100
  }
3722
- return didChange;
3723
- }
3724
-
3725
- // src/History/PixelMutator/mutatorBlendPixelDataBinaryMask.ts
3726
- var defaults16 = {
3727
- blendPixelDataBinaryMask
3728
- };
3729
- var mutatorBlendPixelDataBinaryMask = ((writer, deps = defaults16) => {
3730
- const {
3731
- blendPixelDataBinaryMask: blendPixelDataBinaryMask2 = defaults16.blendPixelDataBinaryMask
3732
- } = deps;
3733
- return {
3734
- blendPixelDataBinaryMask(src, mask, opts = {}) {
3735
- const x = opts.x ?? 0;
3736
- const y = opts.y ?? 0;
3737
- const w = opts.w ?? src.width;
3738
- const h = opts.h ?? src.height;
3739
- const didChange = writer.accumulator.storeRegionBeforeState(x, y, w, h);
3740
- return didChange(blendPixelDataBinaryMask2(writer.config.target, src, mask, opts));
3741
- }
3742
- };
3743
- });
3744
-
3745
- // src/PixelData/fillPixelDataFast.ts
3746
- var SCRATCH_RECT = makeClippedRect();
3747
- function fillPixelDataFast(dst, color, _x, _y, _w, _h) {
3748
- let x;
3749
- let y;
3750
- let w;
3751
- let h;
3752
- if (typeof _x === "object") {
3753
- x = _x.x ?? 0;
3754
- y = _x.y ?? 0;
3755
- w = _x.w ?? dst.width;
3756
- h = _x.h ?? dst.height;
3757
- } else if (typeof _x === "number") {
3758
- x = _x;
3759
- y = _y;
3760
- w = _w;
3761
- h = _h;
3762
- } else {
3763
- x = 0;
3764
- y = 0;
3765
- w = dst.width;
3766
- h = dst.height;
3101
+ writePaintAlphaMaskStroke(color, brush, x0, y0, x1, y1) {
3102
+ const cA = color >>> 24;
3103
+ if (cA === 0) return false;
3104
+ const {
3105
+ tileShift,
3106
+ tileMask,
3107
+ target
3108
+ } = this.config;
3109
+ const {
3110
+ w: bW,
3111
+ h: bH,
3112
+ data: bD,
3113
+ centerOffsetX,
3114
+ centerOffsetY
3115
+ } = brush;
3116
+ const cRGB = color & 16777215;
3117
+ const scratch = this.scratchBounds;
3118
+ let changed = false;
3119
+ forEachLinePoint(x0, y0, x1, y1, (px, py) => {
3120
+ const topLeftX = Math.floor(px + centerOffsetX);
3121
+ const topLeftY = Math.floor(py + centerOffsetY);
3122
+ trimRectBounds(topLeftX, topLeftY, bW, bH, target.width, target.height, scratch);
3123
+ if (scratch.w <= 0 || scratch.h <= 0) return;
3124
+ this.eachTileInBounds(scratch, (tile, bX, bY, bW_t, bH_t) => {
3125
+ const d32 = tile.data32;
3126
+ let tileChanged = false;
3127
+ for (let i = 0; i < bH_t; i++) {
3128
+ const canvasY = bY + i;
3129
+ const bOff = (canvasY - topLeftY) * bW;
3130
+ const tOff = (canvasY & tileMask) << tileShift;
3131
+ const dS = tOff + (bX & tileMask);
3132
+ for (let j = 0; j < bW_t; j++) {
3133
+ const canvasX = bX + j;
3134
+ const brushA = bD[bOff + (canvasX - topLeftX)];
3135
+ if (brushA === 0) continue;
3136
+ const t = cA * brushA + 128;
3137
+ const blendedA = t + (t >> 8) >> 8;
3138
+ const idx = dS + j;
3139
+ const cur = d32[idx];
3140
+ if (brushA > cur >>> 24) {
3141
+ const next = (cRGB | blendedA << 24) >>> 0;
3142
+ if (cur !== next) {
3143
+ d32[idx] = next;
3144
+ tileChanged = true;
3145
+ }
3146
+ }
3147
+ }
3148
+ }
3149
+ if (tileChanged) changed = true;
3150
+ });
3151
+ });
3152
+ return changed;
3767
3153
  }
3768
- const clip = resolveRectClipping(x, y, w, h, dst.width, dst.height, SCRATCH_RECT);
3769
- if (!clip.inBounds) return;
3770
- const {
3771
- x: finalX,
3772
- y: finalY,
3773
- w: actualW,
3774
- h: actualH
3775
- } = clip;
3776
- const dst32 = dst.data32;
3777
- const dw = dst.width;
3778
- if (actualW === dw && actualH === dst.height && finalX === 0 && finalY === 0) {
3779
- dst32.fill(color);
3780
- return;
3154
+ writePaintBinaryMaskStroke(color, brush, x0, y0, x1, y1) {
3155
+ const alphaIsZero = color >>> 24 === 0;
3156
+ if (alphaIsZero) return false;
3157
+ const {
3158
+ tileShift,
3159
+ tileMask,
3160
+ target
3161
+ } = this.config;
3162
+ const {
3163
+ w: bW,
3164
+ h: bH,
3165
+ data: bD,
3166
+ centerOffsetX,
3167
+ centerOffsetY
3168
+ } = brush;
3169
+ const scratch = this.scratchBounds;
3170
+ let changed = false;
3171
+ forEachLinePoint(x0, y0, x1, y1, (px, py) => {
3172
+ const topLeftX = Math.floor(px + centerOffsetX);
3173
+ const topLeftY = Math.floor(py + centerOffsetY);
3174
+ trimRectBounds(topLeftX, topLeftY, bW, bH, target.width, target.height, scratch);
3175
+ if (scratch.w <= 0 || scratch.h <= 0) return;
3176
+ this.eachTileInBounds(scratch, (tile, bX, bY, bW_t, bH_t) => {
3177
+ const d32 = tile.data32;
3178
+ let tileChanged = false;
3179
+ for (let i = 0; i < bH_t; i++) {
3180
+ const canvasY = bY + i;
3181
+ const bOff = (canvasY - topLeftY) * bW;
3182
+ const tOff = (canvasY & tileMask) << tileShift;
3183
+ const dS = tOff + (bX & tileMask);
3184
+ for (let j = 0; j < bW_t; j++) {
3185
+ const canvasX = bX + j;
3186
+ if (bD[bOff + (canvasX - topLeftX)]) {
3187
+ const idx = dS + j;
3188
+ if (d32[idx] !== color) {
3189
+ d32[idx] = color;
3190
+ tileChanged = true;
3191
+ }
3192
+ }
3193
+ }
3194
+ }
3195
+ if (tileChanged) changed = true;
3196
+ });
3197
+ });
3198
+ return changed;
3199
+ }
3200
+ writeRectStroke(color, brushWidth, brushHeight, x0, y0, x1, y1) {
3201
+ const alphaIsZero = color >>> 24 === 0;
3202
+ if (alphaIsZero) return false;
3203
+ const config = this.config;
3204
+ const tileShift = config.tileShift;
3205
+ const tileMask = config.tileMask;
3206
+ const target = config.target;
3207
+ const scratch = this.scratchBounds;
3208
+ const centerOffsetX = macro_halfAndFloor(brushWidth - 1);
3209
+ const centerOffsetY = macro_halfAndFloor(brushHeight - 1);
3210
+ let changed = false;
3211
+ forEachLinePoint(x0, y0, x1, y1, (px, py) => {
3212
+ const topLeftX = Math.floor(px + centerOffsetX);
3213
+ const topLeftY = Math.floor(py + centerOffsetY);
3214
+ trimRectBounds(topLeftX, topLeftY, brushWidth, brushHeight, target.width, target.height, scratch);
3215
+ if (scratch.w <= 0 || scratch.h <= 0) return;
3216
+ this.eachTileInBounds(scratch, (tile, bX, bY, bW_t, bH_t) => {
3217
+ const d32 = tile.data32;
3218
+ let tileChanged = false;
3219
+ for (let i = 0; i < bH_t; i++) {
3220
+ const canvasY = bY + i;
3221
+ const tOff = (canvasY & tileMask) << tileShift;
3222
+ const dS = tOff + (bX & tileMask);
3223
+ for (let j = 0; j < bW_t; j++) {
3224
+ const idx = dS + j;
3225
+ if (d32[idx] !== color) {
3226
+ d32[idx] = color;
3227
+ tileChanged = true;
3228
+ }
3229
+ }
3230
+ }
3231
+ if (tileChanged) {
3232
+ changed = true;
3233
+ }
3234
+ });
3235
+ });
3236
+ return changed;
3781
3237
  }
3782
- for (let iy = 0; iy < actualH; iy++) {
3783
- const start = (finalY + iy) * dw + finalX;
3784
- const end = start + actualW;
3785
- dst32.fill(color, start, end);
3238
+ clear() {
3239
+ this.tilePool.releaseTiles(this.lookup);
3786
3240
  }
3787
- }
3241
+ };
3788
3242
 
3789
- // src/History/PixelMutator/mutatorClear.ts
3790
- var defaults17 = {
3791
- fillPixelData: fillPixelDataFast
3243
+ // src/PixelTile/PixelTile.ts
3244
+ var PixelTile = class {
3245
+ constructor(id, tx, ty, tileSize, tileArea) {
3246
+ this.id = id;
3247
+ this.tx = tx;
3248
+ this.ty = ty;
3249
+ this.width = this.height = tileSize;
3250
+ this.data32 = new Uint32Array(tileArea);
3251
+ const data8 = new Uint8ClampedArray(this.data32.buffer);
3252
+ this.imageData = new ImageData(data8, tileSize, tileSize);
3253
+ }
3254
+ data32;
3255
+ width;
3256
+ height;
3257
+ imageData;
3792
3258
  };
3793
- var mutatorClear = ((writer, deps = defaults17) => {
3794
- const {
3795
- fillPixelData: fillPixelData2 = defaults17.fillPixelData
3796
- } = deps;
3797
- return {
3798
- clear(rect = {}) {
3799
- const target = writer.config.target;
3800
- const x = rect.x ?? 0;
3801
- const y = rect.y ?? 0;
3802
- const w = rect.w ?? target.width;
3803
- const h = rect.h ?? target.height;
3804
- writer.accumulator.storeRegionBeforeState(x, y, w, h);
3805
- fillPixelData2(target, 0, x, y, w, h);
3806
- }
3807
- };
3808
- });
3809
3259
 
3810
- // src/PixelData/fillPixelData.ts
3811
- var SCRATCH_RECT2 = makeClippedRect();
3812
- function fillPixelData(dst, color, _x, _y, _w, _h) {
3813
- let x;
3814
- let y;
3815
- let w;
3816
- let h;
3817
- if (typeof _x === "object") {
3818
- x = _x.x ?? 0;
3819
- y = _x.y ?? 0;
3820
- w = _x.w ?? dst.width;
3821
- h = _x.h ?? dst.height;
3822
- } else if (typeof _x === "number") {
3823
- x = _x;
3824
- y = _y;
3825
- w = _w;
3826
- h = _h;
3827
- } else {
3828
- x = 0;
3829
- y = 0;
3830
- w = dst.width;
3831
- h = dst.height;
3260
+ // src/PixelTile/PixelTilePool.ts
3261
+ var PixelTilePool = class {
3262
+ pool;
3263
+ tileSize;
3264
+ tileArea;
3265
+ constructor(config) {
3266
+ this.pool = [];
3267
+ this.tileSize = config.tileSize;
3268
+ this.tileArea = config.tileArea;
3832
3269
  }
3833
- const clip = resolveRectClipping(x, y, w, h, dst.width, dst.height, SCRATCH_RECT2);
3834
- if (!clip.inBounds) return false;
3835
- const {
3836
- x: finalX,
3837
- y: finalY,
3838
- w: actualW,
3839
- h: actualH
3840
- } = clip;
3841
- const dst32 = dst.data32;
3842
- const dw = dst.width;
3843
- let hasChanged = false;
3844
- for (let iy = 0; iy < actualH; iy++) {
3845
- const rowOffset = (finalY + iy) * dw;
3846
- const start = rowOffset + finalX;
3847
- const end = start + actualW;
3848
- for (let i = start; i < end; i++) {
3849
- if (dst32[i] !== color) {
3850
- dst32[i] = color;
3851
- hasChanged = true;
3270
+ getTile(id, tx, ty) {
3271
+ let tile = this.pool.pop();
3272
+ if (tile) {
3273
+ tile.id = id;
3274
+ tile.tx = tx;
3275
+ tile.ty = ty;
3276
+ tile.data32.fill(0);
3277
+ return tile;
3278
+ }
3279
+ return new PixelTile(id, tx, ty, this.tileSize, this.tileArea);
3280
+ }
3281
+ releaseTile(tile) {
3282
+ this.pool.push(tile);
3283
+ }
3284
+ releaseTiles(tiles) {
3285
+ let length = tiles.length;
3286
+ for (let i = 0; i < length; i++) {
3287
+ let tile = tiles[i];
3288
+ if (tile) {
3289
+ this.pool.push(tile);
3852
3290
  }
3853
3291
  }
3292
+ tiles.length = 0;
3854
3293
  }
3855
- return hasChanged;
3856
- }
3857
-
3858
- // src/History/PixelMutator/mutatorFill.ts
3859
- var defaults18 = {
3860
- fillPixelData
3861
3294
  };
3862
- var mutatorFill = ((writer, deps = defaults18) => {
3863
- const {
3864
- fillPixelData: fillPixelData2 = defaults18.fillPixelData
3865
- } = deps;
3866
- return {
3867
- fill(color, x = 0, y = 0, w = writer.config.target.width, h = writer.config.target.height) {
3868
- const target = writer.config.target;
3869
- const didChange = writer.accumulator.storeRegionBeforeState(x, y, w, h);
3870
- return didChange(fillPixelData2(target, color, x, y, w, h));
3871
- }
3295
+
3296
+ // src/History/PixelWriter.ts
3297
+ var PixelWriter = class {
3298
+ historyManager;
3299
+ accumulator;
3300
+ historyActionFactory;
3301
+ config;
3302
+ pixelTilePool;
3303
+ paintBuffer;
3304
+ mutator;
3305
+ blendPixelDataOpts = {
3306
+ alpha: 255,
3307
+ blendFn: sourceOverPerfect,
3308
+ x: 0,
3309
+ y: 0,
3310
+ w: 0,
3311
+ h: 0
3872
3312
  };
3873
- });
3874
- var mutatorFillRect = ((writer, deps = defaults18) => {
3875
- const {
3876
- fillPixelData: fillPixelData2 = defaults18.fillPixelData
3877
- } = deps;
3878
- return {
3879
- fillRect(color, rect) {
3880
- const target = writer.config.target;
3881
- const didChange = writer.accumulator.storeRegionBeforeState(rect.x, rect.y, rect.w, rect.h);
3882
- return didChange(fillPixelData2(target, color, rect.x, rect.y, rect.w, rect.h));
3313
+ _inProgress = false;
3314
+ constructor(target, mutatorFactory, {
3315
+ tileSize = 256,
3316
+ maxHistorySteps = 50,
3317
+ historyManager = new HistoryManager(maxHistorySteps),
3318
+ historyActionFactory = makeHistoryAction,
3319
+ pixelTilePool,
3320
+ accumulator
3321
+ } = {}) {
3322
+ this.config = new PixelEngineConfig(tileSize, target);
3323
+ this.historyManager = historyManager;
3324
+ this.pixelTilePool = pixelTilePool ?? new PixelTilePool(this.config);
3325
+ this.accumulator = accumulator ?? new PixelAccumulator(this.config, this.pixelTilePool);
3326
+ this.historyActionFactory = historyActionFactory;
3327
+ this.mutator = mutatorFactory(this);
3328
+ this.paintBuffer = new PaintBuffer(this.config, this.pixelTilePool);
3329
+ }
3330
+ /**
3331
+ * Executes `transaction` and commits the resulting pixel changes as a single
3332
+ * undoable history action.
3333
+ *
3334
+ * - If `transaction` throws, all accumulated changes are rolled back and the error
3335
+ * is re-thrown. No action is committed.
3336
+ * - If `transaction` completes without modifying any pixels, no action is committed.
3337
+ * - `withHistory` is not re-entrant. Calling it again from inside `transaction` will
3338
+ * throw immediately to prevent silent data loss from a nested extractPatch.
3339
+ *
3340
+ * @param transaction Callback to be executed inside the transaction.
3341
+ * @param after Called after both undo and redo — use for generic change notifications.
3342
+ * @param afterUndo Called after undo only — use for dimension or state changes specific to undo.
3343
+ * @param afterRedo Called after redo only.
3344
+ */
3345
+ withHistory(transaction, after, afterUndo, afterRedo) {
3346
+ if (this._inProgress) {
3347
+ throw new Error("withHistory is not re-entrant \u2014 commit or rollback the current operation first");
3883
3348
  }
3884
- };
3885
- });
3349
+ this._inProgress = true;
3350
+ try {
3351
+ transaction(this.mutator);
3352
+ } catch (e) {
3353
+ this.accumulator.rollbackAfterError();
3354
+ throw e;
3355
+ } finally {
3356
+ this._inProgress = false;
3357
+ }
3358
+ if (this.accumulator.beforeTiles.length === 0) return;
3359
+ const patch = this.accumulator.extractPatch();
3360
+ const action = this.historyActionFactory(this, patch, after, afterUndo, afterRedo);
3361
+ this.historyManager.commit(action);
3362
+ }
3363
+ resize(newWidth, newHeight, offsetX = 0, offsetY = 0, after, afterUndo, afterRedo, resizeImageDataFn = resizeImageData) {
3364
+ if (this._inProgress) {
3365
+ throw new Error("Cannot resize inside a withHistory callback");
3366
+ }
3367
+ if (this.accumulator.beforeTiles.length > 0) {
3368
+ throw new Error("Cannot resize with an open accumulator \u2014 commit or rollback first");
3369
+ }
3370
+ const config = this.config;
3371
+ const target = config.target;
3372
+ const beforeImageData = target.imageData;
3373
+ const afterImageData = resizeImageDataFn(beforeImageData, newWidth, newHeight, offsetX, offsetY);
3374
+ target.set(afterImageData);
3375
+ this.historyManager.commit({
3376
+ undo: () => {
3377
+ target.set(beforeImageData);
3378
+ afterUndo?.(beforeImageData);
3379
+ after?.(beforeImageData);
3380
+ },
3381
+ redo: () => {
3382
+ target.set(afterImageData);
3383
+ afterRedo?.(afterImageData);
3384
+ after?.(afterImageData);
3385
+ }
3386
+ });
3387
+ }
3388
+ commitPaintBuffer(alpha = 255, blendFn = sourceOverPerfect, blendPixelDataFn = blendPixelData) {
3389
+ const paintBuffer = this.paintBuffer;
3390
+ const tileShift = paintBuffer.config.tileShift;
3391
+ const lookup = paintBuffer.lookup;
3392
+ const opts = this.blendPixelDataOpts;
3393
+ opts.alpha = alpha;
3394
+ opts.blendFn = blendFn;
3395
+ for (let i = 0; i < lookup.length; i++) {
3396
+ const tile = lookup[i];
3397
+ if (tile) {
3398
+ const didChange = this.accumulator.storeTileBeforeState(tile.id, tile.tx, tile.ty);
3399
+ const dx = tile.tx << tileShift;
3400
+ const dy = tile.ty << tileShift;
3401
+ opts.x = dx;
3402
+ opts.y = dy;
3403
+ opts.w = tile.width;
3404
+ opts.h = tile.height;
3405
+ didChange(blendPixelDataFn(this.config.target, tile, opts));
3406
+ }
3407
+ }
3408
+ paintBuffer.clear();
3409
+ }
3410
+ };
3886
3411
 
3887
- // src/PixelData/fillPixelDataBinaryMask.ts
3888
- var SCRATCH_RECT3 = makeClippedRect();
3889
- function fillPixelDataBinaryMask(dst, color, mask, alpha = 255, x = 0, y = 0) {
3890
- if (alpha === 0) return false;
3891
- const maskW = mask.w;
3892
- const maskH = mask.h;
3893
- const clip = resolveRectClipping(x, y, maskW, maskH, dst.width, dst.height, SCRATCH_RECT3);
3894
- if (!clip.inBounds) return false;
3412
+ // src/PixelData/applyAlphaMaskToPixelData.ts
3413
+ function applyAlphaMaskToPixelData(dst, mask, opts = {}) {
3895
3414
  const {
3896
- x: finalX,
3897
- y: finalY,
3898
- w: actualW,
3899
- h: actualH
3900
- } = clip;
3901
- const maskData = mask.data;
3415
+ x: targetX = 0,
3416
+ y: targetY = 0,
3417
+ w: width = dst.width,
3418
+ h: height = dst.height,
3419
+ alpha: globalAlpha = 255,
3420
+ mx = 0,
3421
+ my = 0,
3422
+ invertMask = false
3423
+ } = opts;
3424
+ if (globalAlpha === 0) return false;
3425
+ let x = targetX;
3426
+ let y = targetY;
3427
+ let w = width;
3428
+ let h = height;
3429
+ if (x < 0) {
3430
+ w += x;
3431
+ x = 0;
3432
+ }
3433
+ if (y < 0) {
3434
+ h += y;
3435
+ y = 0;
3436
+ }
3437
+ w = Math.min(w, dst.width - x);
3438
+ h = Math.min(h, dst.height - y);
3439
+ if (w <= 0) return false;
3440
+ if (h <= 0) return false;
3441
+ const mPitch = mask.w;
3442
+ if (mPitch <= 0) return false;
3443
+ const startX = mx + (x - targetX);
3444
+ const startY = my + (y - targetY);
3445
+ const sX0 = Math.max(0, startX);
3446
+ const sY0 = Math.max(0, startY);
3447
+ const sX1 = Math.min(mPitch, startX + w);
3448
+ const sY1 = Math.min(mask.h, startY + h);
3449
+ const finalW = sX1 - sX0;
3450
+ const finalH = sY1 - sY0;
3451
+ if (finalW <= 0) return false;
3452
+ if (finalH <= 0) return false;
3453
+ const xShift = sX0 - startX;
3454
+ const yShift = sY0 - startY;
3902
3455
  const dst32 = dst.data32;
3903
3456
  const dw = dst.width;
3904
- let finalCol = color;
3905
- if (alpha < 255) {
3906
- const baseSrcAlpha = color >>> 24;
3907
- const colorRGB = color & 16777215;
3908
- const a = baseSrcAlpha * alpha + 128 >> 8;
3909
- finalCol = (colorRGB | a << 24) >>> 0;
3910
- }
3911
- let hasChanged = false;
3912
- for (let iy = 0; iy < actualH; iy++) {
3913
- const currentY = finalY + iy;
3914
- const maskY = currentY - y;
3915
- const maskOffset = maskY * maskW;
3916
- const dstRowOffset = currentY * dw;
3917
- for (let ix = 0; ix < actualW; ix++) {
3918
- const currentX = finalX + ix;
3919
- const maskX = currentX - x;
3920
- const maskIndex = maskOffset + maskX;
3921
- if (maskData[maskIndex]) {
3922
- const current = dst32[dstRowOffset + currentX];
3923
- if (current !== finalCol) {
3924
- dst32[dstRowOffset + currentX] = finalCol;
3925
- hasChanged = true;
3457
+ const dStride = dw - finalW;
3458
+ const mStride = mPitch - finalW;
3459
+ const maskData = mask.data;
3460
+ let dIdx = (y + yShift) * dw + (x + xShift);
3461
+ let mIdx = sY0 * mPitch + sX0;
3462
+ let didChange = false;
3463
+ for (let iy = 0; iy < h; iy++) {
3464
+ for (let ix = 0; ix < w; ix++) {
3465
+ const mVal = maskData[mIdx];
3466
+ const effectiveM = invertMask ? 255 - mVal : mVal;
3467
+ let weight = 0;
3468
+ if (effectiveM === 0) {
3469
+ weight = 0;
3470
+ } else if (effectiveM === 255) {
3471
+ weight = globalAlpha;
3472
+ } else if (globalAlpha === 255) {
3473
+ weight = effectiveM;
3474
+ } else {
3475
+ weight = effectiveM * globalAlpha + 128 >> 8;
3476
+ }
3477
+ if (weight === 0) {
3478
+ dst32[dIdx] = (dst32[dIdx] & 16777215) >>> 0;
3479
+ didChange = true;
3480
+ } else if (weight !== 255) {
3481
+ const d = dst32[dIdx];
3482
+ const da = d >>> 24;
3483
+ if (da !== 0) {
3484
+ const finalAlpha = da === 255 ? weight : da * weight + 128 >> 8;
3485
+ const current = dst32[dIdx];
3486
+ const next = (d & 16777215 | finalAlpha << 24) >>> 0;
3487
+ if (current !== next) {
3488
+ dst32[dIdx] = next;
3489
+ didChange = true;
3490
+ }
3926
3491
  }
3927
3492
  }
3493
+ dIdx++;
3494
+ mIdx++;
3928
3495
  }
3496
+ dIdx += dStride;
3497
+ mIdx += mStride;
3929
3498
  }
3930
- return hasChanged;
3499
+ return didChange;
3931
3500
  }
3932
3501
 
3933
- // src/History/PixelMutator/mutatorFillBinaryMask.ts
3934
- var defaults19 = {
3935
- fillPixelDataBinaryMask
3502
+ // src/History/PixelMutator/mutatorApplyAlphaMask.ts
3503
+ var defaults11 = {
3504
+ applyAlphaMaskToPixelData
3936
3505
  };
3937
- var mutatorFillBinaryMask = ((writer, deps = defaults19) => {
3506
+ var mutatorApplyAlphaMask = ((writer, deps = defaults11) => {
3938
3507
  const {
3939
- fillPixelDataBinaryMask: fillPixelDataBinaryMask2 = defaults19.fillPixelDataBinaryMask
3508
+ applyAlphaMaskToPixelData: applyAlphaMaskToPixelData2 = defaults11.applyAlphaMaskToPixelData
3940
3509
  } = deps;
3941
3510
  return {
3942
- fillBinaryMask(color, mask, alpha = 255, x = 0, y = 0) {
3943
- const didChange = writer.accumulator.storeRegionBeforeState(x, y, mask.w, mask.h);
3944
- return didChange(fillPixelDataBinaryMask2(writer.config.target, color, mask, alpha, x, y));
3511
+ applyAlphaMask(mask, opts = {}) {
3512
+ let target = writer.config.target;
3513
+ const {
3514
+ x = 0,
3515
+ y = 0,
3516
+ w = target.width,
3517
+ h = target.height
3518
+ } = opts;
3519
+ const didChange = writer.accumulator.storeRegionBeforeState(x, y, w, h);
3520
+ return didChange(applyAlphaMaskToPixelData2(target, mask, opts));
3945
3521
  }
3946
3522
  };
3947
3523
  });
3948
3524
 
3949
- // src/PixelData/invertPixelData.ts
3950
- var SCRATCH_RECT4 = makeClippedRect();
3951
- function invertPixelData(pixelData, opts = {}) {
3952
- const dst = pixelData;
3525
+ // src/PixelData/applyBinaryMaskToPixelData.ts
3526
+ function applyBinaryMaskToPixelData(dst, mask, opts = {}) {
3953
3527
  const {
3954
3528
  x: targetX = 0,
3955
3529
  y: targetY = 0,
3956
- w: width = pixelData.width,
3957
- h: height = pixelData.height,
3958
- mask,
3530
+ w: width = dst.width,
3531
+ h: height = dst.height,
3532
+ alpha: globalAlpha = 255,
3959
3533
  mx = 0,
3960
3534
  my = 0,
3961
3535
  invertMask = false
3962
3536
  } = opts;
3963
- const clip = resolveRectClipping(targetX, targetY, width, height, dst.width, dst.height, SCRATCH_RECT4);
3964
- if (!clip.inBounds) return false;
3965
- const {
3966
- x,
3967
- y,
3968
- w: actualW,
3969
- h: actualH
3970
- } = clip;
3537
+ if (globalAlpha === 0) return false;
3538
+ let x = targetX;
3539
+ let y = targetY;
3540
+ let w = width;
3541
+ let h = height;
3542
+ if (x < 0) {
3543
+ w += x;
3544
+ x = 0;
3545
+ }
3546
+ if (y < 0) {
3547
+ h += y;
3548
+ y = 0;
3549
+ }
3550
+ w = Math.min(w, dst.width - x);
3551
+ h = Math.min(h, dst.height - y);
3552
+ if (w <= 0 || h <= 0) return false;
3553
+ const mPitch = mask.w;
3554
+ if (mPitch <= 0) return false;
3555
+ const startX = mx + (x - targetX);
3556
+ const startY = my + (y - targetY);
3557
+ const sX0 = Math.max(0, startX);
3558
+ const sY0 = Math.max(0, startY);
3559
+ const sX1 = Math.min(mPitch, startX + w);
3560
+ const sY1 = Math.min(mask.h, startY + h);
3561
+ const finalW = sX1 - sX0;
3562
+ const finalH = sY1 - sY0;
3563
+ if (finalW <= 0 || finalH <= 0) {
3564
+ return false;
3565
+ }
3566
+ const xShift = sX0 - startX;
3567
+ const yShift = sY0 - startY;
3971
3568
  const dst32 = dst.data32;
3972
3569
  const dw = dst.width;
3973
- const mPitch = mask?.w ?? width;
3974
- const dx = x - targetX;
3975
- const dy = y - targetY;
3976
- let dIdx = y * dw + x;
3977
- let mIdx = (my + dy) * mPitch + (mx + dx);
3978
- const dStride = dw - actualW;
3979
- const mStride = mPitch - actualW;
3980
- if (mask) {
3981
- const maskData = mask.data;
3982
- for (let iy = 0; iy < actualH; iy++) {
3983
- for (let ix = 0; ix < actualW; ix++) {
3984
- const mVal = maskData[mIdx];
3985
- const isHit = invertMask ? mVal === 0 : mVal === 1;
3986
- if (isHit) {
3987
- dst32[dIdx] = dst32[dIdx] ^ 16777215;
3570
+ const dStride = dw - finalW;
3571
+ const mStride = mPitch - finalW;
3572
+ const maskData = mask.data;
3573
+ let dIdx = (y + yShift) * dw + (x + xShift);
3574
+ let mIdx = sY0 * mPitch + sX0;
3575
+ let didChange = false;
3576
+ for (let iy = 0; iy < finalH; iy++) {
3577
+ for (let ix = 0; ix < finalW; ix++) {
3578
+ const mVal = maskData[mIdx];
3579
+ const isMaskedOut = invertMask ? mVal !== 0 : mVal === 0;
3580
+ if (isMaskedOut) {
3581
+ const current = dst32[dIdx];
3582
+ const next = (current & 16777215) >>> 0;
3583
+ if (current !== next) {
3584
+ dst32[dIdx] = next;
3585
+ didChange = true;
3586
+ }
3587
+ } else if (globalAlpha !== 255) {
3588
+ const d = dst32[dIdx];
3589
+ const da = d >>> 24;
3590
+ if (da !== 0) {
3591
+ const finalAlpha = da === 255 ? globalAlpha : da * globalAlpha + 128 >> 8;
3592
+ const next = (d & 16777215 | finalAlpha << 24) >>> 0;
3593
+ if (d !== next) {
3594
+ dst32[dIdx] = next;
3595
+ didChange = true;
3596
+ }
3988
3597
  }
3989
- dIdx++;
3990
- mIdx++;
3991
- }
3992
- dIdx += dStride;
3993
- mIdx += mStride;
3994
- }
3995
- } else {
3996
- for (let iy = 0; iy < actualH; iy++) {
3997
- for (let ix = 0; ix < actualW; ix++) {
3998
- dst32[dIdx] = dst32[dIdx] ^ 16777215;
3999
- dIdx++;
4000
3598
  }
4001
- dIdx += dStride;
3599
+ dIdx++;
3600
+ mIdx++;
4002
3601
  }
3602
+ dIdx += dStride;
3603
+ mIdx += mStride;
4003
3604
  }
4004
- return true;
3605
+ return didChange;
4005
3606
  }
4006
3607
 
4007
- // src/History/PixelMutator/mutatorInvert.ts
4008
- var defaults20 = {
4009
- invertPixelData
3608
+ // src/History/PixelMutator/mutatorApplyBinaryMask.ts
3609
+ var defaults12 = {
3610
+ applyBinaryMaskToPixelData
4010
3611
  };
4011
- var mutatorInvert = ((writer, deps = defaults20) => {
3612
+ var mutatorApplyBinaryMask = ((writer, deps = defaults12) => {
4012
3613
  const {
4013
- invertPixelData: invertPixelData2 = defaults20.invertPixelData
3614
+ applyBinaryMaskToPixelData: applyBinaryMaskToPixelData2 = defaults12.applyBinaryMaskToPixelData
4014
3615
  } = deps;
4015
3616
  return {
4016
- invert(opts = {}) {
4017
- const target = writer.config.target;
3617
+ applyBinaryMask(mask, opts = {}) {
3618
+ let target = writer.config.target;
4018
3619
  const {
4019
3620
  x = 0,
4020
3621
  y = 0,
@@ -4022,126 +3623,11 @@ var mutatorInvert = ((writer, deps = defaults20) => {
4022
3623
  h = target.height
4023
3624
  } = opts;
4024
3625
  const didChange = writer.accumulator.storeRegionBeforeState(x, y, w, h);
4025
- return didChange(invertPixelData2(target, opts));
3626
+ return didChange(applyBinaryMaskToPixelData2(target, mask, opts));
4026
3627
  }
4027
3628
  };
4028
3629
  });
4029
3630
 
4030
- // src/History/PixelMutator.ts
4031
- function makeFullPixelMutator(writer) {
4032
- return {
4033
- // @sort
4034
- ...mutatorApplyAlphaMask(writer),
4035
- ...mutatorApplyBinaryMask(writer),
4036
- ...mutatorApplyCircleBrushStroke(writer),
4037
- ...mutatorApplyCirclePencil(writer),
4038
- ...mutatorApplyCirclePencilStroke(writer),
4039
- ...mutatorApplyRectBrush(writer),
4040
- ...mutatorApplyRectBrushStroke(writer),
4041
- ...mutatorApplyRectPencil(writer),
4042
- ...mutatorApplyRectPencilStroke(writer),
4043
- ...mutatorBlendColor(writer),
4044
- ...mutatorBlendColorCircleMask(writer),
4045
- ...mutatorBlendPixel(writer),
4046
- ...mutatorBlendPixelData(writer),
4047
- ...mutatorBlendPixelDataAlphaMask(writer),
4048
- ...mutatorBlendPixelDataBinaryMask(writer),
4049
- ...mutatorClear(writer),
4050
- ...mutatorFill(writer),
4051
- ...mutatorFillBinaryMask(writer),
4052
- ...mutatorFillRect(writer),
4053
- ...mutatorInvert(writer)
4054
- };
4055
- }
4056
-
4057
- // src/PixelTile/PixelTile.ts
4058
- var PixelTile = class {
4059
- constructor(id, tx, ty, tileSize, tileArea) {
4060
- this.id = id;
4061
- this.tx = tx;
4062
- this.ty = ty;
4063
- this.width = this.height = tileSize;
4064
- this.data32 = new Uint32Array(tileArea);
4065
- const data8 = new Uint8ClampedArray(this.data32.buffer);
4066
- this.imageData = new ImageData(data8, tileSize, tileSize);
4067
- }
4068
- data32;
4069
- width;
4070
- height;
4071
- imageData;
4072
- };
4073
-
4074
- // src/PixelTile/PixelTilePool.ts
4075
- var PixelTilePool = class {
4076
- pool;
4077
- tileSize;
4078
- tileArea;
4079
- constructor(config) {
4080
- this.pool = [];
4081
- this.tileSize = config.tileSize;
4082
- this.tileArea = config.tileArea;
4083
- }
4084
- getTile(id, tx, ty) {
4085
- let tile = this.pool.pop();
4086
- if (tile) {
4087
- tile.id = id;
4088
- tile.tx = tx;
4089
- tile.ty = ty;
4090
- tile.data32.fill(0);
4091
- return tile;
4092
- }
4093
- return new PixelTile(id, tx, ty, this.tileSize, this.tileArea);
4094
- }
4095
- releaseTile(tile) {
4096
- this.pool.push(tile);
4097
- }
4098
- releaseTiles(tiles) {
4099
- let length = tiles.length;
4100
- for (let i = 0; i < length; i++) {
4101
- let tile = tiles[i];
4102
- if (tile) {
4103
- this.pool.push(tile);
4104
- }
4105
- }
4106
- tiles.length = 0;
4107
- }
4108
- };
4109
-
4110
- // src/History/PixelWriter.ts
4111
- var PixelWriter = class {
4112
- historyManager;
4113
- accumulator;
4114
- historyActionFactory;
4115
- config;
4116
- mutator;
4117
- constructor(target, mutatorFactory, {
4118
- tileSize = 256,
4119
- maxHistorySteps = 50,
4120
- historyManager = new HistoryManager(maxHistorySteps),
4121
- historyActionFactory = makeHistoryAction,
4122
- pixelTilePool
4123
- } = {}) {
4124
- this.config = new PixelEngineConfig(tileSize, target);
4125
- this.historyManager = historyManager;
4126
- pixelTilePool ??= new PixelTilePool(this.config);
4127
- this.accumulator = new PixelAccumulator(this.config, pixelTilePool);
4128
- this.historyActionFactory = historyActionFactory;
4129
- this.mutator = mutatorFactory(this);
4130
- }
4131
- withHistory(cb, after, afterUndo, afterRedo) {
4132
- try {
4133
- cb(this.mutator);
4134
- } catch (e) {
4135
- this.accumulator.rollback();
4136
- throw e;
4137
- }
4138
- if (this.accumulator.beforeTiles.length === 0) return;
4139
- const patch = this.accumulator.extractPatch();
4140
- const action = this.historyActionFactory(this, patch, after, afterUndo, afterRedo);
4141
- this.historyManager.commit(action);
4142
- }
4143
- };
4144
-
4145
3631
  // src/ImageData/copyImageData.ts
4146
3632
  function copyImageData({
4147
3633
  data,
@@ -4263,35 +3749,6 @@ function resampleImageData(source, factor) {
4263
3749
  return new ImageData(uint8ClampedArray, width, height);
4264
3750
  }
4265
3751
 
4266
- // src/ImageData/resizeImageData.ts
4267
- function resizeImageData(target, newWidth, newHeight, offsetX = 0, offsetY = 0) {
4268
- const result = new ImageData(newWidth, newHeight);
4269
- const {
4270
- width: oldW,
4271
- height: oldH,
4272
- data: oldData
4273
- } = target;
4274
- const newData = result.data;
4275
- const x0 = Math.max(0, offsetX);
4276
- const y0 = Math.max(0, offsetY);
4277
- const x1 = Math.min(newWidth, offsetX + oldW);
4278
- const y1 = Math.min(newHeight, offsetY + oldH);
4279
- if (x1 <= x0 || y1 <= y0) {
4280
- return result;
4281
- }
4282
- const rowCount = y1 - y0;
4283
- const rowLen = (x1 - x0) * 4;
4284
- for (let row = 0; row < rowCount; row++) {
4285
- const dstY = y0 + row;
4286
- const srcY = dstY - offsetY;
4287
- const srcX = x0 - offsetX;
4288
- const dstStart = (dstY * newWidth + x0) * 4;
4289
- const srcStart = (srcY * oldW + srcX) * 4;
4290
- newData.set(oldData.subarray(srcStart, srcStart + rowLen), dstStart);
4291
- }
4292
- return result;
4293
- }
4294
-
4295
3752
  // src/ImageData/ReusableImageData.ts
4296
3753
  function makeReusableImageData() {
4297
3754
  let imageData = null;
@@ -4706,62 +4163,6 @@ function makeBinaryMask(w, h, data) {
4706
4163
  };
4707
4164
  }
4708
4165
 
4709
- // src/Mask/CircleAlphaMask.ts
4710
- function makeCircleAlphaMask(size, fallOff = () => 1) {
4711
- const area = size * size;
4712
- const data = new Uint8Array(area);
4713
- const radius = size / 2;
4714
- const invR = 1 / radius;
4715
- const minOffset = -Math.ceil(radius - 0.5);
4716
- for (let y = 0; y < size; y++) {
4717
- for (let x = 0; x < size; x++) {
4718
- const dx = x - radius + 0.5;
4719
- const dy = y - radius + 0.5;
4720
- const distSqr = dx * dx + dy * dy;
4721
- if (distSqr <= radius * radius) {
4722
- const dist = Math.sqrt(distSqr);
4723
- data[y * size + x] = fallOff(1 - dist * invR) * 255 | 0;
4724
- }
4725
- }
4726
- }
4727
- return {
4728
- type: 0 /* ALPHA */,
4729
- data,
4730
- w: size,
4731
- h: size,
4732
- radius,
4733
- size,
4734
- minOffset
4735
- };
4736
- }
4737
-
4738
- // src/Mask/CircleBinaryMask.ts
4739
- function makeCircleBinaryMask(size) {
4740
- const area = size * size;
4741
- const data = new Uint8Array(area);
4742
- const radius = size / 2;
4743
- const minOffset = -Math.ceil(radius - 0.5);
4744
- for (let y = 0; y < size; y++) {
4745
- for (let x = 0; x < size; x++) {
4746
- const dx = x - radius + 0.5;
4747
- const dy = y - radius + 0.5;
4748
- const distSqr = dx * dx + dy * dy;
4749
- if (distSqr <= radius * radius) {
4750
- data[y * size + x] = 1;
4751
- }
4752
- }
4753
- }
4754
- return {
4755
- type: 1 /* BINARY */,
4756
- data,
4757
- w: size,
4758
- h: size,
4759
- radius,
4760
- size,
4761
- minOffset
4762
- };
4763
- }
4764
-
4765
4166
  // src/Mask/applyBinaryMaskToAlphaMask.ts
4766
4167
  function applyBinaryMaskToAlphaMask(alphaMaskDst, binaryMaskSrc, opts = {}) {
4767
4168
  const {
@@ -5216,6 +4617,162 @@ var PixelData = class {
5216
4617
  }
5217
4618
  };
5218
4619
 
4620
+ // src/PixelData/blendColorPixelDataAlphaMask.ts
4621
+ function blendColorPixelDataAlphaMask(dst, color, mask, opts = {}) {
4622
+ const targetX = opts.x ?? 0;
4623
+ const targetY = opts.y ?? 0;
4624
+ const w = opts.w ?? mask.w;
4625
+ const h = opts.h ?? mask.h;
4626
+ const globalAlpha = opts.alpha ?? 255;
4627
+ const blendFn = opts.blendFn ?? sourceOverPerfect;
4628
+ const mx = opts.mx ?? 0;
4629
+ const my = opts.my ?? 0;
4630
+ const invertMask = opts.invertMask ?? false;
4631
+ if (globalAlpha === 0) return false;
4632
+ const baseSrcAlpha = color >>> 24;
4633
+ const isOverwrite = blendFn.isOverwrite || false;
4634
+ if (baseSrcAlpha === 0 && !isOverwrite) return false;
4635
+ let x = targetX;
4636
+ let y = targetY;
4637
+ let actualW = w;
4638
+ let actualH = h;
4639
+ if (x < 0) {
4640
+ actualW += x;
4641
+ x = 0;
4642
+ }
4643
+ if (y < 0) {
4644
+ actualH += y;
4645
+ y = 0;
4646
+ }
4647
+ actualW = Math.min(actualW, dst.width - x);
4648
+ actualH = Math.min(actualH, dst.height - y);
4649
+ if (actualW <= 0 || actualH <= 0) return false;
4650
+ const dx = x - targetX | 0;
4651
+ const dy = y - targetY | 0;
4652
+ const dst32 = dst.data32;
4653
+ const dw = dst.width;
4654
+ const mPitch = mask.w;
4655
+ const maskData = mask.data;
4656
+ let dIdx = y * dw + x | 0;
4657
+ let mIdx = (my + dy) * mPitch + (mx + dx) | 0;
4658
+ const dStride = dw - actualW | 0;
4659
+ const mStride = mPitch - actualW | 0;
4660
+ const isOpaque = globalAlpha === 255;
4661
+ const colorRGB = color & 16777215;
4662
+ let didChange = false;
4663
+ for (let iy = 0; iy < actualH; iy++) {
4664
+ for (let ix = 0; ix < actualW; ix++) {
4665
+ const mVal = maskData[mIdx];
4666
+ const effM = invertMask ? 255 - mVal : mVal;
4667
+ if (effM === 0) {
4668
+ dIdx++;
4669
+ mIdx++;
4670
+ continue;
4671
+ }
4672
+ let weight = globalAlpha;
4673
+ if (isOpaque) {
4674
+ weight = effM;
4675
+ } else if (effM !== 255) {
4676
+ weight = effM * globalAlpha + 128 >> 8;
4677
+ }
4678
+ if (weight === 0) {
4679
+ dIdx++;
4680
+ mIdx++;
4681
+ continue;
4682
+ }
4683
+ let finalCol = color;
4684
+ if (weight < 255) {
4685
+ const a = baseSrcAlpha * weight + 128 >> 8;
4686
+ if (a === 0 && !isOverwrite) {
4687
+ dIdx++;
4688
+ mIdx++;
4689
+ continue;
4690
+ }
4691
+ finalCol = (colorRGB | a << 24) >>> 0;
4692
+ }
4693
+ const current = dst32[dIdx];
4694
+ const next = blendFn(finalCol, current);
4695
+ if (current !== next) {
4696
+ dst32[dIdx] = next;
4697
+ didChange = true;
4698
+ }
4699
+ dIdx++;
4700
+ mIdx++;
4701
+ }
4702
+ dIdx += dStride;
4703
+ mIdx += mStride;
4704
+ }
4705
+ return didChange;
4706
+ }
4707
+
4708
+ // src/PixelData/blendColorPixelDataBinaryMask.ts
4709
+ function blendColorPixelDataBinaryMask(dst, color, mask, opts = {}) {
4710
+ const targetX = opts.x ?? 0;
4711
+ const targetY = opts.y ?? 0;
4712
+ let w = opts.w ?? mask.w;
4713
+ let h = opts.h ?? mask.h;
4714
+ const globalAlpha = opts.alpha ?? 255;
4715
+ const blendFn = opts.blendFn ?? sourceOverPerfect;
4716
+ const mx = opts.mx ?? 0;
4717
+ const my = opts.my ?? 0;
4718
+ const invertMask = opts.invertMask ?? false;
4719
+ if (globalAlpha === 0) return false;
4720
+ const baseSrcAlpha = color >>> 24;
4721
+ const isOverwrite = blendFn.isOverwrite || false;
4722
+ if (baseSrcAlpha === 0 && !isOverwrite) return false;
4723
+ let x = targetX;
4724
+ let y = targetY;
4725
+ if (x < 0) {
4726
+ w += x;
4727
+ x = 0;
4728
+ }
4729
+ if (y < 0) {
4730
+ h += y;
4731
+ y = 0;
4732
+ }
4733
+ const actualW = Math.min(w, dst.width - x);
4734
+ const actualH = Math.min(h, dst.height - y);
4735
+ if (actualW <= 0 || actualH <= 0) return false;
4736
+ let baseColorWithGlobalAlpha = color;
4737
+ if (globalAlpha < 255) {
4738
+ const a = baseSrcAlpha * globalAlpha + 128 >> 8;
4739
+ if (a === 0 && !isOverwrite) return false;
4740
+ baseColorWithGlobalAlpha = (color & 16777215 | a << 24) >>> 0;
4741
+ }
4742
+ const dx = x - targetX | 0;
4743
+ const dy = y - targetY | 0;
4744
+ const dst32 = dst.data32;
4745
+ const dw = dst.width;
4746
+ const mPitch = mask.w;
4747
+ const maskData = mask.data;
4748
+ let dIdx = y * dw + x | 0;
4749
+ let mIdx = (my + dy) * mPitch + (mx + dx) | 0;
4750
+ const dStride = dw - actualW | 0;
4751
+ const mStride = mPitch - actualW | 0;
4752
+ const skipVal = invertMask ? 1 : 0;
4753
+ let didChange = false;
4754
+ for (let iy = 0; iy < actualH; iy++) {
4755
+ for (let ix = 0; ix < actualW; ix++) {
4756
+ if (maskData[mIdx] === skipVal) {
4757
+ dIdx++;
4758
+ mIdx++;
4759
+ continue;
4760
+ }
4761
+ const current = dst32[dIdx];
4762
+ const next = blendFn(baseColorWithGlobalAlpha, current);
4763
+ if (current !== next) {
4764
+ dst32[dIdx] = next;
4765
+ didChange = true;
4766
+ }
4767
+ dIdx++;
4768
+ mIdx++;
4769
+ }
4770
+ dIdx += dStride;
4771
+ mIdx += mStride;
4772
+ }
4773
+ return didChange;
4774
+ }
4775
+
5219
4776
  // src/PixelData/blendPixelDataPaintBuffer.ts
5220
4777
  var SCRATCH_OPTS = {
5221
4778
  x: 0,
@@ -5462,103 +5019,137 @@ function writePixelDataBuffer(target, data, _x, _y, _w, _h) {
5462
5019
  }
5463
5020
  }
5464
5021
 
5465
- // src/PixelTile/PaintBuffer.ts
5466
- var PaintBuffer = class {
5467
- constructor(config, tilePool) {
5468
- this.config = config;
5469
- this.tilePool = tilePool;
5470
- this.lookup = [];
5022
+ // src/PixelData/writePaintBufferToPixelData.ts
5023
+ function writePaintBufferToPixelData(target, paintBuffer, writePixelDataBufferFn = writePixelDataBuffer) {
5024
+ const tileShift = paintBuffer.config.tileShift;
5025
+ const lookup = paintBuffer.lookup;
5026
+ for (let i = 0; i < lookup.length; i++) {
5027
+ const tile = lookup[i];
5028
+ if (tile) {
5029
+ const dx = tile.tx << tileShift;
5030
+ const dy = tile.ty << tileShift;
5031
+ writePixelDataBufferFn(target, tile.data32, dx, dy, tile.width, tile.height);
5032
+ }
5471
5033
  }
5472
- lookup;
5473
- processMaskTiles(mask, callback) {
5474
- const {
5475
- tileShift,
5476
- targetColumns
5477
- } = this.config;
5478
- const x1 = mask.x >> tileShift;
5479
- const y1 = mask.y >> tileShift;
5480
- const x2 = mask.x + mask.w - 1 >> tileShift;
5481
- const y2 = mask.y + mask.h - 1 >> tileShift;
5482
- for (let ty = y1; ty <= y2; ty++) {
5483
- const tileRowIndex = ty * targetColumns;
5484
- const tileTop = ty << tileShift;
5485
- for (let tx = x1; tx <= x2; tx++) {
5486
- const id = tileRowIndex + tx;
5487
- let tile = this.lookup[id];
5488
- if (!tile) {
5489
- tile = this.tilePool.getTile(id, tx, ty);
5490
- this.lookup[id] = tile;
5034
+ }
5035
+
5036
+ // src/Paint/makeCirclePaintAlphaMask.ts
5037
+ function makeCirclePaintAlphaMask(size, fallOff = (d) => d) {
5038
+ const area = size * size;
5039
+ const data = new Uint8Array(area);
5040
+ const radius = size / 2;
5041
+ const invR = 1 / radius;
5042
+ const centerOffset = -Math.ceil(radius - 0.5);
5043
+ for (let y = 0; y < size; y++) {
5044
+ const rowOffset = y * size;
5045
+ const dy = y - radius + 0.5;
5046
+ const dy2 = dy * dy;
5047
+ for (let x = 0; x < size; x++) {
5048
+ const dx = x - radius + 0.5;
5049
+ const distSqr = dx * dx + dy2;
5050
+ if (distSqr <= radius * radius) {
5051
+ const dist = Math.sqrt(distSqr) * invR;
5052
+ const strength = fallOff(1 - dist);
5053
+ if (strength > 0) {
5054
+ const intensity = strength * 255 | 0;
5055
+ data[rowOffset + x] = Math.max(0, Math.min(255, intensity));
5491
5056
  }
5492
- const tileLeft = tx << tileShift;
5493
- const startX = Math.max(mask.x, tileLeft);
5494
- const endX = Math.min(mask.x + mask.w, tileLeft + this.config.tileSize);
5495
- const startY = Math.max(mask.y, tileTop);
5496
- const endY = Math.min(mask.y + mask.h, tileTop + this.config.tileSize);
5497
- callback(tile, startX, startY, endX - startX, endY - startY, startX - mask.x, startY - mask.y);
5498
5057
  }
5499
5058
  }
5500
5059
  }
5501
- writeColorBinaryMaskRect(color, mask) {
5502
- const {
5503
- tileShift,
5504
- tileMask
5505
- } = this.config;
5506
- const maskData = mask.data;
5507
- const maskW = mask.w;
5508
- this.processMaskTiles(mask, (tile, bX, bY, bW, bH, mX, mY) => {
5509
- const data32 = tile.data32;
5510
- const startTileX = bX & tileMask;
5511
- for (let i = 0; i < bH; i++) {
5512
- const tileY = bY + i & tileMask;
5513
- const maskY = mY + i;
5514
- const tileRowOffset = tileY << tileShift;
5515
- const maskRowOffset = maskY * maskW;
5516
- const destStart = tileRowOffset + startTileX;
5517
- const maskStart = maskRowOffset + mX;
5518
- for (let j = 0; j < bW; j++) {
5519
- if (maskData[maskStart + j]) {
5520
- data32[destStart + j] = color;
5521
- }
5522
- }
5060
+ return {
5061
+ type: 0 /* ALPHA */,
5062
+ data,
5063
+ w: size,
5064
+ h: size,
5065
+ centerOffsetX: centerOffset,
5066
+ centerOffsetY: centerOffset
5067
+ };
5068
+ }
5069
+
5070
+ // src/Paint/makeCirclePaintBinaryMask.ts
5071
+ function makeCirclePaintBinaryMask(size) {
5072
+ const area = size * size;
5073
+ const data = new Uint8Array(area);
5074
+ const radius = size / 2;
5075
+ const centerOffset = -Math.ceil(radius - 0.5);
5076
+ for (let y = 0; y < size; y++) {
5077
+ for (let x = 0; x < size; x++) {
5078
+ const dx = x - radius + 0.5;
5079
+ const dy = y - radius + 0.5;
5080
+ const distSqr = dx * dx + dy * dy;
5081
+ if (distSqr <= radius * radius) {
5082
+ data[y * size + x] = 1;
5523
5083
  }
5524
- });
5084
+ }
5525
5085
  }
5526
- writeColorAlphaMaskRect(color, mask) {
5527
- const {
5528
- tileShift,
5529
- tileMask
5530
- } = this.config;
5531
- const maskData = mask.data;
5532
- const maskW = mask.w;
5533
- const colorRGB = color & 16777215;
5534
- const colorA = color >>> 24;
5535
- this.processMaskTiles(mask, (tile, bX, bY, bW, bH, mX, mY) => {
5536
- const data32 = tile.data32;
5537
- const startTileX = bX & tileMask;
5538
- for (let i = 0; i < bH; i++) {
5539
- const tileY = bY + i & tileMask;
5540
- const maskY = mY + i;
5541
- const tileRowOffset = tileY << tileShift;
5542
- const maskRowOffset = maskY * maskW;
5543
- const destStart = tileRowOffset + startTileX;
5544
- const maskStart = maskRowOffset + mX;
5545
- for (let j = 0; j < bW; j++) {
5546
- const maskA = maskData[maskStart + j];
5547
- if (maskA > 0) {
5548
- const finalA = colorA * maskA + 128 >> 8;
5549
- data32[destStart + j] = (colorRGB | finalA << 24) >>> 0;
5550
- }
5551
- }
5086
+ return {
5087
+ type: 1 /* BINARY */,
5088
+ data,
5089
+ w: size,
5090
+ h: size,
5091
+ centerOffsetX: centerOffset,
5092
+ centerOffsetY: centerOffset
5093
+ };
5094
+ }
5095
+
5096
+ // src/Paint/makePaintMask.ts
5097
+ function makePaintBinaryMask(mask) {
5098
+ return {
5099
+ type: 1 /* BINARY */,
5100
+ data: mask.data,
5101
+ w: mask.w,
5102
+ h: mask.h,
5103
+ centerOffsetX: -(mask.w >> 1),
5104
+ centerOffsetY: -(mask.h >> 1)
5105
+ };
5106
+ }
5107
+ function makePaintAlphaMask(mask) {
5108
+ return {
5109
+ type: 0 /* ALPHA */,
5110
+ data: mask.data,
5111
+ w: mask.w,
5112
+ h: mask.h,
5113
+ centerOffsetX: -(mask.w >> 1),
5114
+ centerOffsetY: -(mask.h >> 1)
5115
+ };
5116
+ }
5117
+
5118
+ // src/Paint/makeRectFalloffPaintAlphaMask.ts
5119
+ function makeRectFalloffPaintAlphaMask(width, height, fallOff = (d) => d) {
5120
+ const fPx = Math.floor(width / 2);
5121
+ const fPy = Math.floor(height / 2);
5122
+ const invHalfW = 2 / width;
5123
+ const invHalfH = 2 / height;
5124
+ const offX = width % 2 === 0 ? 0.5 : 0;
5125
+ const offY = height % 2 === 0 ? 0.5 : 0;
5126
+ const area = width * height;
5127
+ const data = new Uint8Array(area);
5128
+ for (let y = 0; y < height; y++) {
5129
+ const dy = Math.abs(y - fPy + offY) * invHalfH;
5130
+ const rowOffset = y * width;
5131
+ for (let x = 0; x < width; x++) {
5132
+ const dx = Math.abs(x - fPx + offX) * invHalfW;
5133
+ const dist = dx > dy ? dx : dy;
5134
+ const strength = fallOff(1 - dist);
5135
+ if (strength > 0) {
5136
+ const intensity = strength * 255 | 0;
5137
+ data[rowOffset + x] = Math.max(0, Math.min(255, intensity));
5552
5138
  }
5553
- });
5554
- }
5555
- clear() {
5556
- this.tilePool.releaseTiles(this.lookup);
5139
+ }
5557
5140
  }
5558
- };
5141
+ return {
5142
+ type: 0 /* ALPHA */,
5143
+ data,
5144
+ w: width,
5145
+ h: height,
5146
+ centerOffsetX: -macro_halfAndFloor(width),
5147
+ centerOffsetY: -macro_halfAndFloor(height)
5148
+ };
5149
+ }
5559
5150
 
5560
- // src/PixelTile/PaintBufferRenderer.ts
5561
- function makePaintBufferRenderer(paintBuffer, offscreenCanvasClass = OffscreenCanvas) {
5151
+ // src/Paint/PaintBufferCanvasRenderer.ts
5152
+ function makePaintBufferCanvasRenderer(paintBuffer, offscreenCanvasClass = OffscreenCanvas) {
5562
5153
  const config = paintBuffer.config;
5563
5154
  const tileSize = config.tileSize;
5564
5155
  const tileShift = config.tileShift;
@@ -5567,16 +5158,20 @@ function makePaintBufferRenderer(paintBuffer, offscreenCanvasClass = OffscreenCa
5567
5158
  const ctx = canvas.getContext("2d");
5568
5159
  if (!ctx) throw new Error(CANVAS_CTX_FAILED);
5569
5160
  ctx.imageSmoothingEnabled = false;
5570
- return function drawPaintBuffer(target) {
5161
+ return function drawPaintBuffer(targetCtx, alpha = 255, compOperation = "source-over") {
5162
+ targetCtx.globalAlpha = alpha / 255;
5163
+ targetCtx.globalCompositeOperation = compOperation;
5571
5164
  for (let i = 0; i < lookup.length; i++) {
5572
5165
  const tile = lookup[i];
5573
5166
  if (tile) {
5574
5167
  const dx = tile.tx << tileShift;
5575
5168
  const dy = tile.ty << tileShift;
5576
5169
  ctx.putImageData(tile.imageData, 0, 0);
5577
- target.drawImage(canvas, dx, dy);
5170
+ targetCtx.drawImage(canvas, dx, dy);
5578
5171
  }
5579
5172
  }
5173
+ targetCtx.globalAlpha = 1;
5174
+ targetCtx.globalCompositeOperation = "source-over";
5580
5175
  };
5581
5176
  }
5582
5177
  // Annotate the CommonJS export names for ESM import in node:
@@ -5602,13 +5197,11 @@ function makePaintBufferRenderer(paintBuffer, offscreenCanvasClass = OffscreenCa
5602
5197
  applyBinaryMaskToAlphaMask,
5603
5198
  applyBinaryMaskToPixelData,
5604
5199
  applyPatchTiles,
5605
- applyRectBrushToPixelData,
5606
5200
  base64DecodeArrayBuffer,
5607
5201
  base64EncodeArrayBuffer,
5608
5202
  blendColorPixelData,
5609
5203
  blendColorPixelDataAlphaMask,
5610
5204
  blendColorPixelDataBinaryMask,
5611
- blendColorPixelDataCircleMask,
5612
5205
  blendPixel,
5613
5206
  blendPixelData,
5614
5207
  blendPixelDataAlphaMask,
@@ -5650,12 +5243,8 @@ function makePaintBufferRenderer(paintBuffer, offscreenCanvasClass = OffscreenCa
5650
5243
  fillPixelDataFast,
5651
5244
  floodFillSelection,
5652
5245
  forEachLinePoint,
5653
- getCircleBrushOrPencilBounds,
5654
- getCircleBrushOrPencilStrokeBounds,
5655
5246
  getImageDataFromClipboard,
5656
5247
  getIndexedImageColorCounts,
5657
- getRectBrushOrPencilBounds,
5658
- getRectBrushOrPencilStrokeBounds,
5659
5248
  getRectsBounds,
5660
5249
  getSupportedPixelFormats,
5661
5250
  hardLightFast,
@@ -5689,15 +5278,18 @@ function makePaintBufferRenderer(paintBuffer, offscreenCanvasClass = OffscreenCa
5689
5278
  makeBinaryMask,
5690
5279
  makeBlendModeRegistry,
5691
5280
  makeCanvasFrameRenderer,
5692
- makeCircleAlphaMask,
5693
- makeCircleBinaryMask,
5281
+ makeCirclePaintAlphaMask,
5282
+ makeCirclePaintBinaryMask,
5694
5283
  makeFastBlendModeRegistry,
5695
5284
  makeFullPixelMutator,
5696
5285
  makeHistoryAction,
5697
5286
  makeImageDataLike,
5698
- makePaintBufferRenderer,
5287
+ makePaintAlphaMask,
5288
+ makePaintBinaryMask,
5289
+ makePaintBufferCanvasRenderer,
5699
5290
  makePerfectBlendModeRegistry,
5700
5291
  makePixelCanvas,
5292
+ makeRectFalloffPaintAlphaMask,
5701
5293
  makeReusableCanvas,
5702
5294
  makeReusableImageData,
5703
5295
  makeReusableOffscreenCanvas,
@@ -5709,15 +5301,7 @@ function makePaintBufferRenderer(paintBuffer, offscreenCanvasClass = OffscreenCa
5709
5301
  multiplyPerfect,
5710
5302
  mutatorApplyAlphaMask,
5711
5303
  mutatorApplyBinaryMask,
5712
- mutatorApplyCircleBrushStroke,
5713
- mutatorApplyCirclePencil,
5714
- mutatorApplyCirclePencilStroke,
5715
- mutatorApplyRectBrush,
5716
- mutatorApplyRectBrushStroke,
5717
- mutatorApplyRectPencil,
5718
- mutatorApplyRectPencilStroke,
5719
5304
  mutatorBlendColor,
5720
- mutatorBlendColorCircleMask,
5721
5305
  mutatorBlendPixel,
5722
5306
  mutatorBlendPixelData,
5723
5307
  mutatorBlendPixelDataAlphaMask,
@@ -5757,6 +5341,7 @@ function makePaintBufferRenderer(paintBuffer, offscreenCanvasClass = OffscreenCa
5757
5341
  subtractFast,
5758
5342
  subtractPerfect,
5759
5343
  toBlendModeIndexAndName,
5344
+ trimMaskRectBounds,
5760
5345
  trimRectBounds,
5761
5346
  uInt32ArrayToImageData,
5762
5347
  uInt32ArrayToImageDataLike,
@@ -5772,6 +5357,7 @@ function makePaintBufferRenderer(paintBuffer, offscreenCanvasClass = OffscreenCa
5772
5357
  writeImageDataBuffer,
5773
5358
  writeImageDataToClipboard,
5774
5359
  writeImgBlobToClipboard,
5360
+ writePaintBufferToPixelData,
5775
5361
  writePixelDataBuffer
5776
5362
  });
5777
5363
  //# sourceMappingURL=index.dev.cjs.map