pixel-data-js 0.17.1 → 0.19.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 (78) hide show
  1. package/README.md +6 -1
  2. package/dist/index.dev.cjs +2747 -1397
  3. package/dist/index.dev.cjs.map +1 -1
  4. package/dist/index.dev.js +2725 -1403
  5. package/dist/index.dev.js.map +1 -1
  6. package/dist/index.prod.cjs +2747 -1397
  7. package/dist/index.prod.cjs.map +1 -1
  8. package/dist/index.prod.d.ts +401 -241
  9. package/dist/index.prod.js +2725 -1403
  10. package/dist/index.prod.js.map +1 -1
  11. package/package.json +21 -6
  12. package/src/Algorithm/forEachLinePoint.ts +36 -0
  13. package/src/BlendModes/BlendModeRegistry.ts +2 -0
  14. package/src/BlendModes/blend-modes-fast.ts +2 -2
  15. package/src/BlendModes/blend-modes-perfect.ts +5 -4
  16. package/src/BlendModes/toBlendModeIndexAndName.ts +41 -0
  17. package/src/History/PixelAccumulator.ts +2 -2
  18. package/src/History/PixelMutator/mutatorApplyAlphaMask.ts +30 -0
  19. package/src/History/PixelMutator/mutatorApplyBinaryMask.ts +30 -0
  20. package/src/History/PixelMutator/mutatorApplyCircleBrush.ts +59 -0
  21. package/src/History/PixelMutator/mutatorApplyCircleBrushStroke.ts +138 -0
  22. package/src/History/PixelMutator/mutatorApplyCirclePencil.ts +59 -0
  23. package/src/History/PixelMutator/mutatorApplyCirclePencilStroke.ts +131 -0
  24. package/src/History/PixelMutator/mutatorApplyRectBrush.ts +61 -0
  25. package/src/History/PixelMutator/mutatorApplyRectBrushStroke.ts +169 -0
  26. package/src/History/PixelMutator/mutatorApplyRectPencil.ts +62 -0
  27. package/src/History/PixelMutator/mutatorApplyRectPencilStroke.ts +149 -0
  28. package/src/History/PixelMutator/mutatorBlendColor.ts +9 -4
  29. package/src/History/PixelMutator/mutatorBlendPixelData.ts +10 -5
  30. package/src/History/PixelMutator/mutatorClear.ts +27 -0
  31. package/src/History/PixelMutator/{mutatorFillPixelData.ts → mutatorFill.ts} +9 -3
  32. package/src/History/PixelMutator/mutatorInvert.ts +10 -3
  33. package/src/History/PixelMutator.ts +27 -3
  34. package/src/History/PixelPatchTiles.ts +2 -2
  35. package/src/History/PixelWriter.ts +7 -3
  36. package/src/ImageData/ImageDataLike.ts +13 -0
  37. package/src/ImageData/ReusableImageData.ts +1 -4
  38. package/src/ImageData/extractImageDataBuffer.ts +22 -15
  39. package/src/ImageData/serialization.ts +4 -4
  40. package/src/ImageData/uInt32ArrayToImageData.ts +29 -0
  41. package/src/ImageData/writeImageData.ts +26 -18
  42. package/src/ImageData/writeImageDataBuffer.ts +30 -18
  43. package/src/IndexedImage/indexedImageToAverageColor.ts +1 -1
  44. package/src/Internal/resolveClipping.ts +140 -0
  45. package/src/Mask/applyBinaryMaskToAlphaMask.ts +89 -0
  46. package/src/Mask/copyMask.ts +1 -3
  47. package/src/Mask/mergeAlphaMasks.ts +81 -0
  48. package/src/Mask/mergeBinaryMasks.ts +89 -0
  49. package/src/PixelData/PixelBuffer32.ts +28 -0
  50. package/src/PixelData/PixelData.ts +38 -33
  51. package/src/PixelData/applyAlphaMaskToPixelData.ts +119 -0
  52. package/src/PixelData/applyBinaryMaskToPixelData.ts +111 -0
  53. package/src/PixelData/applyCircleBrushToPixelData.ts +58 -28
  54. package/src/PixelData/applyRectBrushToPixelData.ts +56 -73
  55. package/src/PixelData/blendColorPixelData.ts +18 -111
  56. package/src/PixelData/blendColorPixelDataAlphaMask.ts +111 -0
  57. package/src/PixelData/blendColorPixelDataBinaryMask.ts +89 -0
  58. package/src/PixelData/blendPixelData.ts +19 -107
  59. package/src/PixelData/blendPixelDataAlphaMask.ts +149 -0
  60. package/src/PixelData/blendPixelDataBinaryMask.ts +133 -0
  61. package/src/PixelData/clearPixelData.ts +2 -3
  62. package/src/PixelData/extractPixelData.ts +4 -4
  63. package/src/PixelData/extractPixelDataBuffer.ts +38 -26
  64. package/src/PixelData/fillPixelData.ts +18 -20
  65. package/src/PixelData/invertPixelData.ts +13 -21
  66. package/src/PixelData/pixelDataToAlphaMask.ts +2 -3
  67. package/src/PixelData/reflectPixelData.ts +3 -3
  68. package/src/PixelData/resamplePixelData.ts +2 -6
  69. package/src/PixelData/writePixelDataBuffer.ts +34 -20
  70. package/src/Rect/getCircleBrushOrPencilBounds.ts +43 -0
  71. package/src/Rect/getCircleBrushOrPencilStrokeBounds.ts +24 -0
  72. package/src/Rect/getRectBrushOrPencilBounds.ts +38 -0
  73. package/src/Rect/getRectBrushOrPencilStrokeBounds.ts +26 -0
  74. package/src/_types.ts +49 -33
  75. package/src/index.ts +47 -11
  76. package/src/History/PixelMutator/mutatorApplyMask.ts +0 -20
  77. package/src/Mask/mergeMasks.ts +0 -100
  78. package/src/PixelData/applyMaskToPixelData.ts +0 -129
@@ -23,23 +23,32 @@ __export(src_exports, {
23
23
  BASE_FAST_BLEND_MODE_FUNCTIONS: () => BASE_FAST_BLEND_MODE_FUNCTIONS,
24
24
  BASE_PERFECT_BLEND_MODE_FUNCTIONS: () => BASE_PERFECT_BLEND_MODE_FUNCTIONS,
25
25
  BaseBlendMode: () => BaseBlendMode,
26
+ CANVAS_CTX_FAILED: () => CANVAS_CTX_FAILED,
26
27
  HistoryManager: () => HistoryManager,
27
28
  IndexedImage: () => IndexedImage,
28
29
  MaskType: () => MaskType,
30
+ OFFSCREEN_CANVAS_CTX_FAILED: () => OFFSCREEN_CANVAS_CTX_FAILED,
29
31
  PixelAccumulator: () => PixelAccumulator,
32
+ PixelBuffer32: () => PixelBuffer32,
30
33
  PixelData: () => PixelData,
31
34
  PixelEngineConfig: () => PixelEngineConfig,
32
35
  PixelTile: () => PixelTile,
33
36
  PixelWriter: () => PixelWriter,
34
37
  UnsupportedFormatError: () => UnsupportedFormatError,
38
+ applyAlphaMaskToPixelData: () => applyAlphaMaskToPixelData,
39
+ applyBinaryMaskToAlphaMask: () => applyBinaryMaskToAlphaMask,
40
+ applyBinaryMaskToPixelData: () => applyBinaryMaskToPixelData,
35
41
  applyCircleBrushToPixelData: () => applyCircleBrushToPixelData,
36
- applyMaskToPixelData: () => applyMaskToPixelData,
37
42
  applyPatchTiles: () => applyPatchTiles,
38
43
  applyRectBrushToPixelData: () => applyRectBrushToPixelData,
39
44
  base64DecodeArrayBuffer: () => base64DecodeArrayBuffer,
40
45
  base64EncodeArrayBuffer: () => base64EncodeArrayBuffer,
41
46
  blendColorPixelData: () => blendColorPixelData,
47
+ blendColorPixelDataAlphaMask: () => blendColorPixelDataAlphaMask,
48
+ blendColorPixelDataBinaryMask: () => blendColorPixelDataBinaryMask,
42
49
  blendPixelData: () => blendPixelData,
50
+ blendPixelDataAlphaMask: () => blendPixelDataAlphaMask,
51
+ blendPixelDataBinaryMask: () => blendPixelDataBinaryMask,
43
52
  clearPixelData: () => clearPixelData,
44
53
  color32ToCssRGBA: () => color32ToCssRGBA,
45
54
  color32ToHex: () => color32ToHex,
@@ -72,9 +81,13 @@ __export(src_exports, {
72
81
  fileToImageData: () => fileToImageData,
73
82
  fillPixelData: () => fillPixelData,
74
83
  floodFillSelection: () => floodFillSelection,
84
+ forEachLinePoint: () => forEachLinePoint,
85
+ getCircleBrushOrPencilBounds: () => getCircleBrushOrPencilBounds,
86
+ getCircleBrushOrPencilStrokeBounds: () => getCircleBrushOrPencilStrokeBounds,
75
87
  getImageDataFromClipboard: () => getImageDataFromClipboard,
76
88
  getIndexedImageColorCounts: () => getIndexedImageColorCounts,
77
- getRectBrushBounds: () => getRectBrushBounds,
89
+ getRectBrushOrPencilBounds: () => getRectBrushOrPencilBounds,
90
+ getRectBrushOrPencilStrokeBounds: () => getRectBrushOrPencilStrokeBounds,
78
91
  getSupportedPixelFormats: () => getSupportedPixelFormats,
79
92
  hardLightFast: () => hardLightFast,
80
93
  hardLightPerfect: () => hardLightPerfect,
@@ -106,17 +119,29 @@ __export(src_exports, {
106
119
  makeBlendModeRegistry: () => makeBlendModeRegistry,
107
120
  makeFastBlendModeRegistry: () => makeFastBlendModeRegistry,
108
121
  makeFullPixelMutator: () => makeFullPixelMutator,
122
+ makeImageDataLike: () => makeImageDataLike,
109
123
  makePerfectBlendModeRegistry: () => makePerfectBlendModeRegistry,
110
124
  makePixelCanvas: () => makePixelCanvas,
111
125
  makeReusableCanvas: () => makeReusableCanvas,
112
126
  makeReusableImageData: () => makeReusableImageData,
113
- mergeMasks: () => mergeMasks,
127
+ mergeAlphaMasks: () => mergeAlphaMasks,
128
+ mergeBinaryMasks: () => mergeBinaryMasks,
114
129
  multiplyFast: () => multiplyFast,
115
130
  multiplyPerfect: () => multiplyPerfect,
116
- mutatorApplyMask: () => mutatorApplyMask,
131
+ mutatorApplyAlphaMask: () => mutatorApplyAlphaMask,
132
+ mutatorApplyBinaryMask: () => mutatorApplyBinaryMask,
133
+ mutatorApplyCircleBrush: () => mutatorApplyCircleBrush,
134
+ mutatorApplyCircleBrushStroke: () => mutatorApplyCircleBrushStroke,
135
+ mutatorApplyCirclePencil: () => mutatorApplyCirclePencil,
136
+ mutatorApplyCirclePencilStroke: () => mutatorApplyCirclePencilStroke,
137
+ mutatorApplyRectBrush: () => mutatorApplyRectBrush,
138
+ mutatorApplyRectBrushStroke: () => mutatorApplyRectBrushStroke,
139
+ mutatorApplyRectPencil: () => mutatorApplyRectPencil,
140
+ mutatorApplyRectPencilStroke: () => mutatorApplyRectPencilStroke,
117
141
  mutatorBlendColor: () => mutatorBlendColor,
118
142
  mutatorBlendPixel: () => mutatorBlendPixel,
119
143
  mutatorBlendPixelData: () => mutatorBlendPixelData,
144
+ mutatorClear: () => mutatorClear,
120
145
  mutatorFill: () => mutatorFill,
121
146
  mutatorInvert: () => mutatorInvert,
122
147
  overlayFast: () => overlayFast,
@@ -146,7 +171,10 @@ __export(src_exports, {
146
171
  sourceOverPerfect: () => sourceOverPerfect,
147
172
  subtractFast: () => subtractFast,
148
173
  subtractPerfect: () => subtractPerfect,
174
+ toBlendModeIndexAndName: () => toBlendModeIndexAndName,
149
175
  trimRectBounds: () => trimRectBounds,
176
+ uInt32ArrayToImageData: () => uInt32ArrayToImageData,
177
+ uInt32ArrayToImageDataLike: () => uInt32ArrayToImageDataLike,
150
178
  unpackAlpha: () => unpackAlpha,
151
179
  unpackBlue: () => unpackBlue,
152
180
  unpackColor: () => unpackColor,
@@ -163,617 +191,238 @@ __export(src_exports, {
163
191
  });
164
192
  module.exports = __toCommonJS(src_exports);
165
193
 
166
- // src/BlendModes/blend-modes.ts
167
- var BaseBlendMode = {
168
- overwrite: 0,
169
- sourceOver: 1,
170
- darken: 2,
171
- multiply: 3,
172
- colorBurn: 4,
173
- linearBurn: 5,
174
- darkerColor: 6,
175
- lighten: 7,
176
- screen: 8,
177
- colorDodge: 9,
178
- linearDodge: 10,
179
- lighterColor: 11,
180
- overlay: 12,
181
- softLight: 13,
182
- hardLight: 14,
183
- vividLight: 15,
184
- linearLight: 16,
185
- pinLight: 17,
186
- hardMix: 18,
187
- difference: 19,
188
- exclusion: 20,
189
- subtract: 21,
190
- divide: 22
194
+ // src/_types.ts
195
+ var MaskType = /* @__PURE__ */ ((MaskType2) => {
196
+ MaskType2[MaskType2["ALPHA"] = 0] = "ALPHA";
197
+ MaskType2[MaskType2["BINARY"] = 1] = "BINARY";
198
+ return MaskType2;
199
+ })(MaskType || {});
200
+
201
+ // src/color.ts
202
+ function packColor(r, g, b, a) {
203
+ return (a << 24 | b << 16 | g << 8 | r) >>> 0;
204
+ }
205
+ function packRGBA({
206
+ r,
207
+ g,
208
+ b,
209
+ a
210
+ }) {
211
+ return (a << 24 | b << 16 | g << 8 | r) >>> 0;
212
+ }
213
+ var unpackRed = (packed) => packed >>> 0 & 255;
214
+ var unpackGreen = (packed) => packed >>> 8 & 255;
215
+ var unpackBlue = (packed) => packed >>> 16 & 255;
216
+ var unpackAlpha = (packed) => packed >>> 24 & 255;
217
+ function unpackColor(packed) {
218
+ return {
219
+ r: packed >>> 0 & 255,
220
+ g: packed >>> 8 & 255,
221
+ b: packed >>> 16 & 255,
222
+ a: packed >>> 24 & 255
223
+ };
224
+ }
225
+ var SCRATCH_RGBA = {
226
+ r: 0,
227
+ g: 0,
228
+ b: 0,
229
+ a: 0
191
230
  };
192
- var overwriteBase = (src, _dst) => src;
193
- overwriteBase.isOverwrite = true;
231
+ function unpackColorTo(packed, scratch = SCRATCH_RGBA) {
232
+ scratch.r = packed >>> 0 & 255;
233
+ scratch.g = packed >>> 8 & 255;
234
+ scratch.b = packed >>> 16 & 255;
235
+ scratch.a = packed >>> 24 & 255;
236
+ return scratch;
237
+ }
238
+ function colorDistance(a, b) {
239
+ const dr = (a & 255) - (b & 255);
240
+ const dg = (a >>> 8 & 255) - (b >>> 8 & 255);
241
+ const db = (a >>> 16 & 255) - (b >>> 16 & 255);
242
+ const da = (a >>> 24 & 255) - (b >>> 24 & 255);
243
+ return dr * dr + dg * dg + db * db + da * da;
244
+ }
245
+ function lerpColor32(a, b, t) {
246
+ const r = (a & 255) + t * ((b & 255) - (a & 255));
247
+ const g = (a >>> 8 & 255) + t * ((b >>> 8 & 255) - (a >>> 8 & 255));
248
+ const b_ = (a >>> 16 & 255) + t * ((b >>> 16 & 255) - (a >>> 16 & 255));
249
+ const a_ = (a >>> 24 & 255) + t * ((b >>> 24 & 255) - (a >>> 24 & 255));
250
+ return (a_ << 24 | b_ << 16 | g << 8 | r) >>> 0;
251
+ }
252
+ function lerpColor32Fast(src, dst, w) {
253
+ const invA = 255 - w;
254
+ const rb = (src & 16711935) * w + (dst & 16711935) * invA >>> 8 & 16711935;
255
+ const ga = (src >>> 8 & 16711935) * w + (dst >>> 8 & 16711935) * invA >>> 8 & 16711935;
256
+ return (rb | ga << 8) >>> 0;
257
+ }
258
+ function color32ToHex(color) {
259
+ const r = (color & 255).toString(16).padStart(2, "0");
260
+ const g = (color >>> 8 & 255).toString(16).padStart(2, "0");
261
+ const b = (color >>> 16 & 255).toString(16).padStart(2, "0");
262
+ const a = (color >>> 24 & 255).toString(16).padStart(2, "0");
263
+ return `#${r}${g}${b}${a}`;
264
+ }
265
+ function color32ToCssRGBA(color) {
266
+ const r = color & 255;
267
+ const g = color >>> 8 & 255;
268
+ const b = color >>> 16 & 255;
269
+ const a = color >>> 24 & 255;
270
+ const alpha = Number((a / 255).toFixed(3));
271
+ return `rgba(${r},${g},${b},${alpha})`;
272
+ }
194
273
 
195
- // src/BlendModes/BlendModeRegistry.ts
196
- function makeBlendModeRegistry(blendModes, initialEntries) {
197
- const blendToName = /* @__PURE__ */ new Map();
198
- const blendToIndex = /* @__PURE__ */ new Map();
199
- const indexToName = [];
200
- const indexToBlend = [];
201
- const nameToBlend = {};
202
- const nameToIndex = {};
203
- const add = (name, index, blendFn) => {
204
- if (!Number.isFinite(index)) {
205
- throw new Error(`Index "${index}" is not a number. Attempting to add name: "${name}", index: "${index}"`);
274
+ // src/Internal/resolveClipping.ts
275
+ var makeClippedRect = () => ({
276
+ x: 0,
277
+ y: 0,
278
+ w: 0,
279
+ h: 0,
280
+ inBounds: false
281
+ });
282
+ var makeClippedBlit = () => ({
283
+ x: 0,
284
+ y: 0,
285
+ sx: 0,
286
+ sy: 0,
287
+ w: 0,
288
+ h: 0,
289
+ inBounds: false
290
+ });
291
+ function resolveRectClipping(x, y, w, h, boundaryW, boundaryH, out) {
292
+ if (x < 0) {
293
+ w += x;
294
+ x = 0;
295
+ }
296
+ if (y < 0) {
297
+ h += y;
298
+ y = 0;
299
+ }
300
+ const actualW = Math.min(w, boundaryW - x);
301
+ const actualH = Math.min(h, boundaryH - y);
302
+ if (actualW <= 0 || actualH <= 0) {
303
+ out.inBounds = false;
304
+ return out;
305
+ }
306
+ out.x = x;
307
+ out.y = y;
308
+ out.w = actualW;
309
+ out.h = actualH;
310
+ out.inBounds = true;
311
+ return out;
312
+ }
313
+ function resolveBlitClipping(x, y, sx, sy, w, h, dstW, dstH, srcW, srcH, out) {
314
+ if (sx < 0) {
315
+ x -= sx;
316
+ w += sx;
317
+ sx = 0;
318
+ }
319
+ if (sy < 0) {
320
+ y -= sy;
321
+ h += sy;
322
+ sy = 0;
323
+ }
324
+ w = Math.min(w, srcW - sx);
325
+ h = Math.min(h, srcH - sy);
326
+ if (x < 0) {
327
+ sx -= x;
328
+ w += x;
329
+ x = 0;
330
+ }
331
+ if (y < 0) {
332
+ sy -= y;
333
+ h += y;
334
+ y = 0;
335
+ }
336
+ const actualW = Math.min(w, dstW - x);
337
+ const actualH = Math.min(h, dstH - y);
338
+ if (actualW <= 0 || actualH <= 0) {
339
+ out.inBounds = false;
340
+ return out;
341
+ }
342
+ out.x = x;
343
+ out.y = y;
344
+ out.sx = sx;
345
+ out.sy = sy;
346
+ out.w = actualW;
347
+ out.h = actualH;
348
+ out.inBounds = true;
349
+ return out;
350
+ }
351
+
352
+ // src/ImageData/extractImageDataBuffer.ts
353
+ var SCRATCH_BLIT = makeClippedBlit();
354
+ function extractImageDataBuffer(imageData, _x, _y, _w, _h) {
355
+ const {
356
+ x,
357
+ y,
358
+ w,
359
+ h
360
+ } = typeof _x === "object" ? _x : {
361
+ x: _x,
362
+ y: _y,
363
+ w: _w,
364
+ h: _h
365
+ };
366
+ const {
367
+ width: srcW,
368
+ height: srcH,
369
+ data: src
370
+ } = imageData;
371
+ if (w <= 0 || h <= 0) return new Uint8ClampedArray(0);
372
+ const out = new Uint8ClampedArray(w * h * 4);
373
+ const clip = resolveBlitClipping(0, 0, x, y, w, h, w, h, srcW, srcH, SCRATCH_BLIT);
374
+ if (!clip.inBounds) return out;
375
+ const {
376
+ x: dstX,
377
+ y: dstY,
378
+ sx: srcX,
379
+ sy: srcY,
380
+ w: copyW,
381
+ h: copyH
382
+ } = clip;
383
+ const rowLen = copyW * 4;
384
+ for (let row = 0; row < copyH; row++) {
385
+ const srcStart = ((srcY + row) * srcW + srcX) * 4;
386
+ const dstStart = ((dstY + row) * w + dstX) * 4;
387
+ out.set(src.subarray(srcStart, srcStart + rowLen), dstStart);
388
+ }
389
+ return out;
390
+ }
391
+
392
+ // src/Mask/extractMask.ts
393
+ function extractMask(mask, maskWidth, xOrRect, y, w, h) {
394
+ let finalX;
395
+ let finalY;
396
+ let finalW;
397
+ let finalH;
398
+ if (typeof xOrRect === "object") {
399
+ finalX = xOrRect.x;
400
+ finalY = xOrRect.y;
401
+ finalW = xOrRect.w;
402
+ finalH = xOrRect.h;
403
+ } else {
404
+ finalX = xOrRect;
405
+ finalY = y;
406
+ finalW = w;
407
+ finalH = h;
408
+ }
409
+ const out = new Uint8Array(finalW * finalH);
410
+ const srcH = mask.length / maskWidth;
411
+ for (let row = 0; row < finalH; row++) {
412
+ const currentSrcY = finalY + row;
413
+ if (currentSrcY < 0 || currentSrcY >= srcH) {
414
+ continue;
206
415
  }
207
- if (indexToBlend[index]) {
208
- throw new Error(`Blend Mode index: ${index} is already used. Attempting to add name: "${name}", index: "${index}"`);
416
+ const start = Math.max(0, finalX);
417
+ const end = Math.min(maskWidth, finalX + finalW);
418
+ if (start < end) {
419
+ const srcOffset = currentSrcY * maskWidth + start;
420
+ const dstOffset = row * finalW + (start - finalX);
421
+ const count = end - start;
422
+ out.set(mask.subarray(srcOffset, srcOffset + count), dstOffset);
209
423
  }
210
- indexToName[index] = name;
211
- indexToBlend[index] = blendFn;
212
- blendToIndex.set(blendFn, index);
213
- blendToName.set(blendFn, name);
214
- nameToBlend[name] = blendFn;
215
- nameToIndex[name] = index;
216
- };
217
- for (const [name, index] of Object.entries(blendModes)) {
218
- const blend = initialEntries[index];
219
- add(name, index, blend);
220
424
  }
221
- return {
222
- nameToBlend,
223
- nameToIndex,
224
- blendToIndex,
225
- blendToName,
226
- indexToBlend,
227
- indexToName,
228
- indexType: null,
229
- nameType: null
230
- };
231
- }
232
-
233
- // src/BlendModes/blend-modes-fast.ts
234
- var overwriteFast = overwriteBase;
235
- var sourceOverFast = (src, dst) => {
236
- const sa = src >>> 24 & 255;
237
- if (sa === 255) return src;
238
- if (sa === 0) return dst;
239
- const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
240
- const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
241
- const da = dst >>> 24 & 255;
242
- const invA = 255 - sa;
243
- const r = sr * sa + dr * invA >> 8;
244
- const g = sg * sa + dg * invA >> 8;
245
- const b = sb * sa + db * invA >> 8;
246
- const a = 255 * sa + da * invA >> 8;
247
- return (a << 24 | b << 16 | g << 8 | r) >>> 0;
248
- };
249
- var darkenFast = (src, dst) => {
250
- const sa = src >>> 24 & 255;
251
- if (sa === 0) return dst;
252
- const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
253
- const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
254
- const br = sr < dr ? sr : dr;
255
- const bg = sg < dg ? sg : dg;
256
- const bb = sb < db ? sb : db;
257
- if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
258
- const invA = 255 - sa;
259
- const r = br * sa + dr * invA >> 8;
260
- const g = bg * sa + dg * invA >> 8;
261
- const b = bb * sa + db * invA >> 8;
262
- const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
263
- return (a << 24 | b << 16 | g << 8 | r) >>> 0;
264
- };
265
- var multiplyFast = (src, dst) => {
266
- const sa = src >>> 24 & 255;
267
- if (sa === 0) return dst;
268
- const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
269
- const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
270
- const br = sr * dr >> 8;
271
- const bg = sg * dg >> 8;
272
- const bb = sb * db >> 8;
273
- if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
274
- const invA = 255 - sa;
275
- const da = dst >>> 24 & 255;
276
- const r = br * sa + dr * invA >> 8;
277
- const g = bg * sa + dg * invA >> 8;
278
- const b = bb * sa + db * invA >> 8;
279
- const a = 255 * sa + da * invA >> 8;
280
- return (a << 24 | b << 16 | g << 8 | r) >>> 0;
281
- };
282
- var colorBurnFast = (src, dst) => {
283
- const sa = src >>> 24 & 255;
284
- if (sa === 0) return dst;
285
- const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
286
- const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
287
- const br = dr === 255 ? 255 : sr === 0 ? 0 : Math.max(0, 255 - (255 - dr << 8) / sr | 0);
288
- const bg = dg === 255 ? 255 : sg === 0 ? 0 : Math.max(0, 255 - (255 - dg << 8) / sg | 0);
289
- const bb = db === 255 ? 255 : sb === 0 ? 0 : Math.max(0, 255 - (255 - db << 8) / sb | 0);
290
- if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
291
- const invA = 255 - sa;
292
- const da = dst >>> 24 & 255;
293
- const r = br * sa + dr * invA >> 8;
294
- const g = bg * sa + dg * invA >> 8;
295
- const b = bb * sa + db * invA >> 8;
296
- const a = 255 * sa + da * invA >> 8;
297
- return (a << 24 | b << 16 | g << 8 | r) >>> 0;
298
- };
299
- var linearBurnFast = (src, dst) => {
300
- const sa = src >>> 24 & 255;
301
- if (sa === 0) return dst;
302
- const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
303
- const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
304
- const brU = dr + sr - 255;
305
- const bgU = dg + sg - 255;
306
- const bbU = db + sb - 255;
307
- const br = brU < 0 ? 0 : brU;
308
- const bg = bgU < 0 ? 0 : bgU;
309
- const bb = bbU < 0 ? 0 : bbU;
310
- if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
311
- const invA = 255 - sa;
312
- const r = br * sa + dr * invA >> 8;
313
- const g = bg * sa + dg * invA >> 8;
314
- const b = bb * sa + db * invA >> 8;
315
- const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
316
- return (a << 24 | b << 16 | g << 8 | r) >>> 0;
317
- };
318
- var darkerFast = (src, dst) => {
319
- const sa = src >>> 24 & 255;
320
- if (sa === 0) return dst;
321
- const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
322
- const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
323
- const lumSrc = sr * 77 + sg * 151 + sb * 28;
324
- const lumDst = dr * 77 + dg * 151 + db * 28;
325
- let br, bg, bb;
326
- if (lumSrc < lumDst) {
327
- br = sr;
328
- bg = sg;
329
- bb = sb;
330
- } else {
331
- br = dr;
332
- bg = dg;
333
- bb = db;
334
- }
335
- if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
336
- const invA = 255 - sa;
337
- const r = br * sa + dr * invA >> 8;
338
- const g = bg * sa + dg * invA >> 8;
339
- const b = bb * sa + db * invA >> 8;
340
- const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
341
- return (a << 24 | b << 16 | g << 8 | r) >>> 0;
342
- };
343
- var lightenFast = (src, dst) => {
344
- const sa = src >>> 24 & 255;
345
- if (sa === 0) return dst;
346
- const br = Math.max(src & 255, dst & 255);
347
- const bg = Math.max(src >> 8 & 255, dst >> 8 & 255);
348
- const bb = Math.max(src >> 16 & 255, dst >> 16 & 255);
349
- if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
350
- const dr = dst & 255;
351
- const dg = dst >> 8 & 255;
352
- const db = dst >> 16 & 255;
353
- const invA = 255 - sa;
354
- const r = br * sa + dr * invA >> 8;
355
- const g = bg * sa + dg * invA >> 8;
356
- const b = bb * sa + db * invA >> 8;
357
- const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
358
- return (a << 24 | b << 16 | g << 8 | r) >>> 0;
359
- };
360
- var screenFast = (src, dst) => {
361
- const sa = src >>> 24 & 255;
362
- if (sa === 0) return dst;
363
- const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
364
- const br = 255 - ((255 - (src & 255)) * (255 - dr) >> 8);
365
- const bg = 255 - ((255 - (src >>> 8 & 255)) * (255 - dg) >> 8);
366
- const bb = 255 - ((255 - (src >>> 16 & 255)) * (255 - db) >> 8);
367
- if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
368
- const invA = 255 - sa;
369
- const r = br * sa + dr * invA >> 8;
370
- const g = bg * sa + dg * invA >> 8;
371
- const b = bb * sa + db * invA >> 8;
372
- const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
373
- return (a << 24 | b << 16 | g << 8 | r) >>> 0;
374
- };
375
- var colorDodgeFast = (src, dst) => {
376
- const sa = src >>> 24 & 255;
377
- if (sa === 0) return dst;
378
- const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
379
- const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
380
- const br = sr === 255 ? 255 : Math.min(255, (dr << 8) / (255 - sr) | 0);
381
- const bg = sg === 255 ? 255 : Math.min(255, (dg << 8) / (255 - sg) | 0);
382
- const bb = sb === 255 ? 255 : Math.min(255, (db << 8) / (255 - sb) | 0);
383
- if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
384
- const invA = 255 - sa;
385
- const r = br * sa + dr * invA >> 8;
386
- const g = bg * sa + dg * invA >> 8;
387
- const b = bb * sa + db * invA >> 8;
388
- const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
389
- return (a << 24 | b << 16 | g << 8 | r) >>> 0;
390
- };
391
- var linearDodgeFast = (src, dst) => {
392
- const sa = src >>> 24 & 255;
393
- if (sa === 0) return dst;
394
- const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
395
- const brU = (src & 255) + dr;
396
- const bgU = (src >>> 8 & 255) + dg;
397
- const bbU = (src >>> 16 & 255) + db;
398
- const br = brU > 255 ? 255 : brU;
399
- const bg = bgU > 255 ? 255 : bgU;
400
- const bb = bbU > 255 ? 255 : bbU;
401
- if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
402
- const invA = 255 - sa;
403
- const r = br * sa + dr * invA >> 8;
404
- const g = bg * sa + dg * invA >> 8;
405
- const b = bb * sa + db * invA >> 8;
406
- const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
407
- return (a << 24 | b << 16 | g << 8 | r) >>> 0;
408
- };
409
- var lighterFast = (src, dst) => {
410
- const sa = src >>> 24 & 255;
411
- if (sa === 0) return dst;
412
- const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
413
- const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
414
- const lumSrc = sr * 77 + sg * 151 + sb * 28;
415
- const lumDst = dr * 77 + dg * 151 + db * 28;
416
- let br, bg, bb;
417
- if (lumSrc > lumDst) {
418
- br = sr;
419
- bg = sg;
420
- bb = sb;
421
- } else {
422
- br = dr;
423
- bg = dg;
424
- bb = db;
425
- }
426
- if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
427
- const invA = 255 - sa;
428
- const r = br * sa + dr * invA >> 8;
429
- const g = bg * sa + dg * invA >> 8;
430
- const b = bb * sa + db * invA >> 8;
431
- const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
432
- return (a << 24 | b << 16 | g << 8 | r) >>> 0;
433
- };
434
- var overlayFast = (src, dst) => {
435
- const sa = src >>> 24 & 255;
436
- if (sa === 0) return dst;
437
- const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
438
- const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
439
- const br = dr < 128 ? 2 * sr * dr >> 8 : 255 - (2 * (255 - sr) * (255 - dr) >> 8);
440
- const bg = dg < 128 ? 2 * sg * dg >> 8 : 255 - (2 * (255 - sg) * (255 - dg) >> 8);
441
- const bb = db < 128 ? 2 * sb * db >> 8 : 255 - (2 * (255 - sb) * (255 - db) >> 8);
442
- if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
443
- const invA = 255 - sa;
444
- const r = br * sa + dr * invA >> 8;
445
- const g = bg * sa + dg * invA >> 8;
446
- const b = bb * sa + db * invA >> 8;
447
- const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
448
- return (a << 24 | b << 16 | g << 8 | r) >>> 0;
449
- };
450
- var softLightFast = (src, dst) => {
451
- const sa = src >>> 24 & 255;
452
- if (sa === 0) return dst;
453
- const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
454
- const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
455
- const br = (255 - dr) * (sr * dr >> 8) + dr * (255 - ((255 - sr) * (255 - dr) >> 8)) >> 8;
456
- const bg = (255 - dg) * (sg * dg >> 8) + dg * (255 - ((255 - sg) * (255 - dg) >> 8)) >> 8;
457
- const bb = (255 - db) * (sb * db >> 8) + db * (255 - ((255 - sb) * (255 - db) >> 8)) >> 8;
458
- if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
459
- const invA = 255 - sa;
460
- const r = br * sa + dr * invA >> 8;
461
- const g = bg * sa + dg * invA >> 8;
462
- const b = bb * sa + db * invA >> 8;
463
- const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
464
- return (a << 24 | b << 16 | g << 8 | r) >>> 0;
465
- };
466
- var hardLightFast = (src, dst) => {
467
- const sa = src >>> 24 & 255;
468
- if (sa === 0) return dst;
469
- const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
470
- const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
471
- const br = sr < 128 ? 2 * sr * dr >> 8 : 255 - (2 * (255 - sr) * (255 - dr) >> 8);
472
- const bg = sg < 128 ? 2 * sg * dg >> 8 : 255 - (2 * (255 - sg) * (255 - dg) >> 8);
473
- const bb = sb < 128 ? 2 * sb * db >> 8 : 255 - (2 * (255 - sb) * (255 - db) >> 8);
474
- if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
475
- const invA = 255 - sa;
476
- const r = br * sa + dr * invA >> 8;
477
- const g = bg * sa + dg * invA >> 8;
478
- const b = bb * sa + db * invA >> 8;
479
- const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
480
- return (a << 24 | b << 16 | g << 8 | r) >>> 0;
481
- };
482
- var vividLightFast = (src, dst) => {
483
- const sa = src >>> 24 & 255;
484
- if (sa === 0) return dst;
485
- const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
486
- const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
487
- const br = sr < 128 ? sr === 0 ? 0 : Math.max(0, 255 - (255 - dr << 8) / (2 * sr) | 0) : sr === 255 ? 255 : Math.min(255, (dr << 8) / (2 * (255 - sr)) | 0);
488
- const bg = sg < 128 ? sg === 0 ? 0 : Math.max(0, 255 - (255 - dg << 8) / (2 * sg) | 0) : sg === 255 ? 255 : Math.min(255, (dg << 8) / (2 * (255 - sg)) | 0);
489
- const bb = sb < 128 ? sb === 0 ? 0 : Math.max(0, 255 - (255 - db << 8) / (2 * sb) | 0) : sb === 255 ? 255 : Math.min(255, (db << 8) / (2 * (255 - sb)) | 0);
490
- if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
491
- const invA = 255 - sa;
492
- const r = br * sa + dr * invA >> 8;
493
- const g = bg * sa + dg * invA >> 8;
494
- const b = bb * sa + db * invA >> 8;
495
- const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
496
- return (a << 24 | b << 16 | g << 8 | r) >>> 0;
497
- };
498
- var linearLightFast = (src, dst) => {
499
- const sa = src >>> 24 & 255;
500
- if (sa === 0) return dst;
501
- const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
502
- const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
503
- const brU = dr + 2 * sr - 255;
504
- const bgU = dg + 2 * sg - 255;
505
- const bbU = db + 2 * sb - 255;
506
- const br = brU < 0 ? 0 : brU > 255 ? 255 : brU;
507
- const bg = bgU < 0 ? 0 : bgU > 255 ? 255 : bgU;
508
- const bb = bbU < 0 ? 0 : bbU > 255 ? 255 : bbU;
509
- if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
510
- const invA = 255 - sa;
511
- const r = br * sa + dr * invA >> 8;
512
- const g = bg * sa + dg * invA >> 8;
513
- const b = bb * sa + db * invA >> 8;
514
- const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
515
- return (a << 24 | b << 16 | g << 8 | r) >>> 0;
516
- };
517
- var pinLightFast = (src, dst) => {
518
- const sa = src >>> 24 & 255;
519
- if (sa === 0) return dst;
520
- const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
521
- const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
522
- const br = sr < 128 ? dr < 2 * sr ? dr : 2 * sr : dr > 2 * sr - 256 ? dr : 2 * sr - 256;
523
- const bg = sg < 128 ? dg < 2 * sg ? dg : 2 * sg : dg > 2 * sg - 256 ? dg : 2 * sg - 256;
524
- const bb = sb < 128 ? db < 2 * sb ? db : 2 * sb : db > 2 * sb - 256 ? db : 2 * sb - 256;
525
- if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
526
- const invA = 255 - sa;
527
- const r = br * sa + dr * invA >> 8;
528
- const g = bg * sa + dg * invA >> 8;
529
- const b = bb * sa + db * invA >> 8;
530
- const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
531
- return (a << 24 | b << 16 | g << 8 | r) >>> 0;
532
- };
533
- var hardMixFast = (src, dst) => {
534
- const sa = src >>> 24 & 255;
535
- if (sa === 0) return dst;
536
- const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
537
- const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
538
- const br = (sr < 128 ? sr === 0 ? 0 : Math.max(0, 255 - (255 - dr << 8) / (2 * sr) | 0) : sr === 255 ? 255 : Math.min(255, (dr << 8) / (2 * (255 - sr)) | 0)) < 128 ? 0 : 255;
539
- const bg = (sg < 128 ? sg === 0 ? 0 : Math.max(0, 255 - (255 - dg << 8) / (2 * sg) | 0) : sg === 255 ? 255 : Math.min(255, (dg << 8) / (2 * (255 - sg)) | 0)) < 128 ? 0 : 255;
540
- const bb = (sb < 128 ? sb === 0 ? 0 : Math.max(0, 255 - (255 - db << 8) / (2 * sb) | 0) : sb === 255 ? 255 : Math.min(255, (db << 8) / (2 * (255 - sb)) | 0)) < 128 ? 0 : 255;
541
- if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
542
- const invA = 255 - sa;
543
- const r = br * sa + dr * invA >> 8;
544
- const g = bg * sa + dg * invA >> 8;
545
- const b = bb * sa + db * invA >> 8;
546
- const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
547
- return (a << 24 | b << 16 | g << 8 | r) >>> 0;
548
- };
549
- var differenceFast = (src, dst) => {
550
- const sa = src >>> 24 & 255;
551
- if (sa === 0) return dst;
552
- const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
553
- const brD = (src & 255) - dr;
554
- const bgD = (src >>> 8 & 255) - dg;
555
- const bbD = (src >>> 16 & 255) - db;
556
- const br = brD < 0 ? -brD : brD;
557
- const bg = bgD < 0 ? -bgD : bgD;
558
- const bb = bbD < 0 ? -bbD : bbD;
559
- if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
560
- const invA = 255 - sa;
561
- const r = br * sa + dr * invA >> 8;
562
- const g = bg * sa + dg * invA >> 8;
563
- const b = bb * sa + db * invA >> 8;
564
- const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
565
- return (a << 24 | b << 16 | g << 8 | r) >>> 0;
566
- };
567
- var exclusionFast = (src, dst) => {
568
- const sa = src >>> 24 & 255;
569
- if (sa === 0) return dst;
570
- const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
571
- const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
572
- const br = dr + sr - (dr * sr >> 7);
573
- const bg = dg + sg - (dg * sg >> 7);
574
- const bb = db + sb - (db * sb >> 7);
575
- if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
576
- const invA = 255 - sa;
577
- const r = br * sa + dr * invA >> 8;
578
- const g = bg * sa + dg * invA >> 8;
579
- const b = bb * sa + db * invA >> 8;
580
- const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
581
- return (a << 24 | b << 16 | g << 8 | r) >>> 0;
582
- };
583
- var subtractFast = (src, dst) => {
584
- const sa = src >>> 24 & 255;
585
- if (sa === 0) return dst;
586
- const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
587
- const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
588
- const brU = dr - sr;
589
- const bgU = dg - sg;
590
- const bbU = db - sb;
591
- const br = brU < 0 ? 0 : brU;
592
- const bg = bgU < 0 ? 0 : bgU;
593
- const bb = bbU < 0 ? 0 : bbU;
594
- if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
595
- const invA = 255 - sa;
596
- const r = br * sa + dr * invA >> 8;
597
- const g = bg * sa + dg * invA >> 8;
598
- const b = bb * sa + db * invA >> 8;
599
- const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
600
- return (a << 24 | b << 16 | g << 8 | r) >>> 0;
601
- };
602
- var divideFast = (src, dst) => {
603
- const sa = src >>> 24 & 255;
604
- if (sa === 0) return dst;
605
- const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
606
- const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
607
- const br = sr === 0 ? 255 : Math.min(255, (dr << 8) / sr | 0);
608
- const bg = sg === 0 ? 255 : Math.min(255, (dg << 8) / sg | 0);
609
- const bb = sb === 0 ? 255 : Math.min(255, (db << 8) / sb | 0);
610
- if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
611
- const invA = 255 - sa;
612
- const r = br * sa + dr * invA >> 8;
613
- const g = bg * sa + dg * invA >> 8;
614
- const b = bb * sa + db * invA >> 8;
615
- const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
616
- return (a << 24 | b << 16 | g << 8 | r) >>> 0;
617
- };
618
- var BASE_FAST_BLEND_MODE_FUNCTIONS = {
619
- [BaseBlendMode.overwrite]: overwriteFast,
620
- [BaseBlendMode.sourceOver]: sourceOverFast,
621
- [BaseBlendMode.darken]: darkenFast,
622
- [BaseBlendMode.multiply]: multiplyFast,
623
- [BaseBlendMode.colorBurn]: colorBurnFast,
624
- [BaseBlendMode.linearBurn]: linearBurnFast,
625
- [BaseBlendMode.darkerColor]: darkerFast,
626
- [BaseBlendMode.lighten]: lightenFast,
627
- [BaseBlendMode.screen]: screenFast,
628
- [BaseBlendMode.colorDodge]: colorDodgeFast,
629
- [BaseBlendMode.linearDodge]: linearDodgeFast,
630
- [BaseBlendMode.lighterColor]: lighterFast,
631
- [BaseBlendMode.overlay]: overlayFast,
632
- [BaseBlendMode.softLight]: softLightFast,
633
- [BaseBlendMode.hardLight]: hardLightFast,
634
- [BaseBlendMode.vividLight]: vividLightFast,
635
- [BaseBlendMode.linearLight]: linearLightFast,
636
- [BaseBlendMode.pinLight]: pinLightFast,
637
- [BaseBlendMode.hardMix]: hardMixFast,
638
- [BaseBlendMode.difference]: differenceFast,
639
- [BaseBlendMode.exclusion]: exclusionFast,
640
- [BaseBlendMode.subtract]: subtractFast,
641
- [BaseBlendMode.divide]: divideFast
642
- };
643
- function makeFastBlendModeRegistry() {
644
- return makeBlendModeRegistry(BaseBlendMode, BASE_FAST_BLEND_MODE_FUNCTIONS);
645
- }
646
-
647
- // src/_types.ts
648
- var MaskType = /* @__PURE__ */ ((MaskType2) => {
649
- MaskType2[MaskType2["ALPHA"] = 0] = "ALPHA";
650
- MaskType2[MaskType2["BINARY"] = 1] = "BINARY";
651
- return MaskType2;
652
- })(MaskType || {});
653
-
654
- // src/color.ts
655
- function packColor(r, g, b, a) {
656
- return (a << 24 | b << 16 | g << 8 | r) >>> 0;
657
- }
658
- function packRGBA({ r, g, b, a }) {
659
- return (a << 24 | b << 16 | g << 8 | r) >>> 0;
660
- }
661
- var unpackRed = (packed) => packed >>> 0 & 255;
662
- var unpackGreen = (packed) => packed >>> 8 & 255;
663
- var unpackBlue = (packed) => packed >>> 16 & 255;
664
- var unpackAlpha = (packed) => packed >>> 24 & 255;
665
- function unpackColor(packed) {
666
- return {
667
- r: packed >>> 0 & 255,
668
- g: packed >>> 8 & 255,
669
- b: packed >>> 16 & 255,
670
- a: packed >>> 24 & 255
671
- };
672
- }
673
- var SCRATCH_RGBA = { r: 0, g: 0, b: 0, a: 0 };
674
- function unpackColorTo(packed, scratch = SCRATCH_RGBA) {
675
- scratch.r = packed >>> 0 & 255;
676
- scratch.g = packed >>> 8 & 255;
677
- scratch.b = packed >>> 16 & 255;
678
- scratch.a = packed >>> 24 & 255;
679
- return scratch;
680
- }
681
- function colorDistance(a, b) {
682
- const dr = (a & 255) - (b & 255);
683
- const dg = (a >>> 8 & 255) - (b >>> 8 & 255);
684
- const db = (a >>> 16 & 255) - (b >>> 16 & 255);
685
- const da = (a >>> 24 & 255) - (b >>> 24 & 255);
686
- return dr * dr + dg * dg + db * db + da * da;
687
- }
688
- function lerpColor32(a, b, t) {
689
- const r = (a & 255) + t * ((b & 255) - (a & 255));
690
- const g = (a >>> 8 & 255) + t * ((b >>> 8 & 255) - (a >>> 8 & 255));
691
- const b_ = (a >>> 16 & 255) + t * ((b >>> 16 & 255) - (a >>> 16 & 255));
692
- const a_ = (a >>> 24 & 255) + t * ((b >>> 24 & 255) - (a >>> 24 & 255));
693
- return (a_ << 24 | b_ << 16 | g << 8 | r) >>> 0;
694
- }
695
- function lerpColor32Fast(src, dst, w) {
696
- const invA = 255 - w;
697
- const rb = (src & 16711935) * w + (dst & 16711935) * invA >>> 8 & 16711935;
698
- const ga = (src >>> 8 & 16711935) * w + (dst >>> 8 & 16711935) * invA >>> 8 & 16711935;
699
- return (rb | ga << 8) >>> 0;
700
- }
701
- function color32ToHex(color) {
702
- const r = (color & 255).toString(16).padStart(2, "0");
703
- const g = (color >>> 8 & 255).toString(16).padStart(2, "0");
704
- const b = (color >>> 16 & 255).toString(16).padStart(2, "0");
705
- const a = (color >>> 24 & 255).toString(16).padStart(2, "0");
706
- return `#${r}${g}${b}${a}`;
707
- }
708
- function color32ToCssRGBA(color) {
709
- const r = color & 255;
710
- const g = color >>> 8 & 255;
711
- const b = color >>> 16 & 255;
712
- const a = color >>> 24 & 255;
713
- const alpha = Number((a / 255).toFixed(3));
714
- return `rgba(${r},${g},${b},${alpha})`;
715
- }
716
-
717
- // src/ImageData/extractImageDataBuffer.ts
718
- function extractImageDataBuffer(imageData, _x, _y, _w, _h) {
719
- const { x, y, w, h } = typeof _x === "object" ? _x : { x: _x, y: _y, w: _w, h: _h };
720
- const { width: srcW, height: srcH, data: src } = imageData;
721
- if (w <= 0 || h <= 0) return new Uint8ClampedArray(0);
722
- const out = new Uint8ClampedArray(w * h * 4);
723
- const x0 = Math.max(0, x);
724
- const y0 = Math.max(0, y);
725
- const x1 = Math.min(srcW, x + w);
726
- const y1 = Math.min(srcH, y + h);
727
- if (x1 <= x0 || y1 <= y0) return out;
728
- for (let row = 0; row < y1 - y0; row++) {
729
- const srcRow = y0 + row;
730
- const srcStart = (srcRow * srcW + x0) * 4;
731
- const rowLen = (x1 - x0) * 4;
732
- const dstRow = y0 - y + row;
733
- const dstCol = x0 - x;
734
- const dstStart = (dstRow * w + dstCol) * 4;
735
- out.set(src.subarray(srcStart, srcStart + rowLen), dstStart);
736
- }
737
- return out;
738
- }
739
-
740
- // src/Mask/extractMask.ts
741
- function extractMask(mask, maskWidth, xOrRect, y, w, h) {
742
- let finalX;
743
- let finalY;
744
- let finalW;
745
- let finalH;
746
- if (typeof xOrRect === "object") {
747
- finalX = xOrRect.x;
748
- finalY = xOrRect.y;
749
- finalW = xOrRect.w;
750
- finalH = xOrRect.h;
751
- } else {
752
- finalX = xOrRect;
753
- finalY = y;
754
- finalW = w;
755
- finalH = h;
756
- }
757
- const out = new Uint8Array(finalW * finalH);
758
- const srcH = mask.length / maskWidth;
759
- for (let row = 0; row < finalH; row++) {
760
- const currentSrcY = finalY + row;
761
- if (currentSrcY < 0 || currentSrcY >= srcH) {
762
- continue;
763
- }
764
- const start = Math.max(0, finalX);
765
- const end = Math.min(maskWidth, finalX + finalW);
766
- if (start < end) {
767
- const srcOffset = currentSrcY * maskWidth + start;
768
- const dstOffset = row * finalW + (start - finalX);
769
- const count = end - start;
770
- out.set(
771
- mask.subarray(srcOffset, srcOffset + count),
772
- dstOffset
773
- );
774
- }
775
- }
776
- return out;
425
+ return out;
777
426
  }
778
427
 
779
428
  // src/Rect/trimRectBounds.ts
@@ -783,14 +432,8 @@ function trimRectBounds(target, bounds) {
783
432
  const originalW = target.w;
784
433
  const intersectedX = Math.max(target.x, bounds.x);
785
434
  const intersectedY = Math.max(target.y, bounds.y);
786
- const intersectedMaxX = Math.min(
787
- target.x + target.w,
788
- bounds.x + bounds.w
789
- );
790
- const intersectedMaxY = Math.min(
791
- target.y + target.h,
792
- bounds.y + bounds.h
793
- );
435
+ const intersectedMaxX = Math.min(target.x + target.w, bounds.x + bounds.w);
436
+ const intersectedMaxY = Math.min(target.y + target.h, bounds.y + bounds.h);
794
437
  if (intersectedMaxX <= intersectedX || intersectedMaxY <= intersectedY) {
795
438
  target.w = 0;
796
439
  target.h = 0;
@@ -808,14 +451,7 @@ function trimRectBounds(target, bounds) {
808
451
  target.w = intersectedW;
809
452
  target.h = intersectedH;
810
453
  if ("mask" in target && target.mask) {
811
- const currentMask = extractMask(
812
- target.mask,
813
- originalW,
814
- offsetX,
815
- offsetY,
816
- intersectedW,
817
- intersectedH
818
- );
454
+ const currentMask = extractMask(target.mask, originalW, offsetX, offsetY, intersectedW, intersectedH);
819
455
  let minX = intersectedW;
820
456
  let maxX = -1;
821
457
  let minY = intersectedH;
@@ -839,14 +475,7 @@ function trimRectBounds(target, bounds) {
839
475
  const finalW = maxX - minX + 1;
840
476
  const finalH = maxY - minY + 1;
841
477
  if (finalW !== intersectedW || finalH !== intersectedH) {
842
- target.mask = extractMask(
843
- currentMask,
844
- intersectedW,
845
- minX,
846
- minY,
847
- finalW,
848
- finalH
849
- );
478
+ target.mask = extractMask(currentMask, intersectedW, minX, minY, finalW, finalH);
850
479
  target.x += minX;
851
480
  target.y += minY;
852
481
  target.w = finalW;
@@ -869,11 +498,7 @@ function floodFillSelection(img, startX, startY, {
869
498
  data32 = img.data32;
870
499
  imageData = img.imageData;
871
500
  } else {
872
- data32 = new Uint32Array(
873
- img.data.buffer,
874
- img.data.byteOffset,
875
- img.data.byteLength >> 2
876
- );
501
+ data32 = new Uint32Array(img.data.buffer, img.data.byteOffset, img.data.byteLength >> 2);
877
502
  imageData = img;
878
503
  }
879
504
  const {
@@ -948,59 +573,537 @@ function floodFillSelection(img, startX, startY, {
948
573
  }
949
574
  }
950
575
  } else {
951
- for (let y = yMin; y <= yMax; y++) {
952
- for (let x = xMin; x <= xMax; x++) {
953
- const color = data32[y * width + x];
954
- if (colorDistance(color, baseColor) <= tolerance) {
955
- matchX[matchCount] = x;
956
- matchY[matchCount] = y;
957
- matchCount++;
958
- if (x < minX) minX = x;
959
- if (x > maxX) maxX = x;
960
- if (y < minY) minY = y;
961
- if (y > maxY) maxY = y;
962
- }
963
- }
964
- }
965
- }
966
- if (matchCount === 0) {
967
- return null;
576
+ for (let y = yMin; y <= yMax; y++) {
577
+ for (let x = xMin; x <= xMax; x++) {
578
+ const color = data32[y * width + x];
579
+ if (colorDistance(color, baseColor) <= tolerance) {
580
+ matchX[matchCount] = x;
581
+ matchY[matchCount] = y;
582
+ matchCount++;
583
+ if (x < minX) minX = x;
584
+ if (x > maxX) maxX = x;
585
+ if (y < minY) minY = y;
586
+ if (y > maxY) maxY = y;
587
+ }
588
+ }
589
+ }
590
+ }
591
+ if (matchCount === 0) {
592
+ return null;
593
+ }
594
+ const selectionRect = {
595
+ x: minX,
596
+ y: minY,
597
+ w: maxX - minX + 1,
598
+ h: maxY - minY + 1,
599
+ mask: new Uint8Array((maxX - minX + 1) * (maxY - minY + 1)),
600
+ maskType: 1 /* BINARY */
601
+ };
602
+ const sw = selectionRect.w;
603
+ const sh = selectionRect.h;
604
+ const finalMask = selectionRect.mask;
605
+ for (let i = 0; i < matchCount; i++) {
606
+ const mx = matchX[i] - selectionRect.x;
607
+ const my = matchY[i] - selectionRect.y;
608
+ if (mx >= 0 && mx < sw && my >= 0 && my < sh) {
609
+ finalMask[my * sw + mx] = 1;
610
+ }
611
+ }
612
+ trimRectBounds(selectionRect, {
613
+ x: 0,
614
+ y: 0,
615
+ w: width,
616
+ h: height
617
+ });
618
+ const extracted = extractImageDataBuffer(imageData, selectionRect.x, selectionRect.y, selectionRect.w, selectionRect.h);
619
+ return {
620
+ startX,
621
+ startY,
622
+ selectionRect,
623
+ pixels: extracted
624
+ };
625
+ }
626
+
627
+ // src/BlendModes/blend-modes.ts
628
+ var BaseBlendMode = {
629
+ overwrite: 0,
630
+ sourceOver: 1,
631
+ darken: 2,
632
+ multiply: 3,
633
+ colorBurn: 4,
634
+ linearBurn: 5,
635
+ darkerColor: 6,
636
+ lighten: 7,
637
+ screen: 8,
638
+ colorDodge: 9,
639
+ linearDodge: 10,
640
+ lighterColor: 11,
641
+ overlay: 12,
642
+ softLight: 13,
643
+ hardLight: 14,
644
+ vividLight: 15,
645
+ linearLight: 16,
646
+ pinLight: 17,
647
+ hardMix: 18,
648
+ difference: 19,
649
+ exclusion: 20,
650
+ subtract: 21,
651
+ divide: 22
652
+ };
653
+ var overwriteBase = (src, _dst) => src;
654
+ overwriteBase.isOverwrite = true;
655
+
656
+ // src/BlendModes/BlendModeRegistry.ts
657
+ function makeBlendModeRegistry(blendModes, initialEntries, registryName = "anonymous") {
658
+ const blendToName = /* @__PURE__ */ new Map();
659
+ const blendToIndex = /* @__PURE__ */ new Map();
660
+ const indexToName = [];
661
+ const indexToBlend = [];
662
+ const nameToBlend = {};
663
+ const nameToIndex = {};
664
+ const add = (name, index, blendFn) => {
665
+ if (!Number.isFinite(index)) {
666
+ throw new Error(`Index "${index}" is not a number. Attempting to add name: "${name}", index: "${index}"`);
667
+ }
668
+ if (indexToBlend[index]) {
669
+ throw new Error(`Blend Mode index: ${index} is already used. Attempting to add name: "${name}", index: "${index}"`);
670
+ }
671
+ indexToName[index] = name;
672
+ indexToBlend[index] = blendFn;
673
+ blendToIndex.set(blendFn, index);
674
+ blendToName.set(blendFn, name);
675
+ nameToBlend[name] = blendFn;
676
+ nameToIndex[name] = index;
677
+ };
678
+ for (const [name, index] of Object.entries(blendModes)) {
679
+ const blend = initialEntries[index];
680
+ add(name, index, blend);
681
+ }
682
+ return {
683
+ registryName,
684
+ nameToBlend,
685
+ nameToIndex,
686
+ blendToIndex,
687
+ blendToName,
688
+ indexToBlend,
689
+ indexToName,
690
+ indexType: null,
691
+ nameType: null
692
+ };
693
+ }
694
+
695
+ // src/BlendModes/blend-modes-fast.ts
696
+ var overwriteFast = overwriteBase;
697
+ var sourceOverFast = (src, dst) => {
698
+ const sa = src >>> 24 & 255;
699
+ if (sa === 255) return src;
700
+ if (sa === 0) return dst;
701
+ const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
702
+ const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
703
+ const da = dst >>> 24 & 255;
704
+ const invA = 255 - sa;
705
+ const r = sr * sa + dr * invA >> 8;
706
+ const g = sg * sa + dg * invA >> 8;
707
+ const b = sb * sa + db * invA >> 8;
708
+ const a = 255 * sa + da * invA >> 8;
709
+ return (a << 24 | b << 16 | g << 8 | r) >>> 0;
710
+ };
711
+ var darkenFast = (src, dst) => {
712
+ const sa = src >>> 24 & 255;
713
+ if (sa === 0) return dst;
714
+ const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
715
+ const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
716
+ const br = sr < dr ? sr : dr;
717
+ const bg = sg < dg ? sg : dg;
718
+ const bb = sb < db ? sb : db;
719
+ if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
720
+ const invA = 255 - sa;
721
+ const r = br * sa + dr * invA >> 8;
722
+ const g = bg * sa + dg * invA >> 8;
723
+ const b = bb * sa + db * invA >> 8;
724
+ const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
725
+ return (a << 24 | b << 16 | g << 8 | r) >>> 0;
726
+ };
727
+ var multiplyFast = (src, dst) => {
728
+ const sa = src >>> 24 & 255;
729
+ if (sa === 0) return dst;
730
+ const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
731
+ const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
732
+ const br = sr * dr >> 8;
733
+ const bg = sg * dg >> 8;
734
+ const bb = sb * db >> 8;
735
+ if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
736
+ const invA = 255 - sa;
737
+ const da = dst >>> 24 & 255;
738
+ const r = br * sa + dr * invA >> 8;
739
+ const g = bg * sa + dg * invA >> 8;
740
+ const b = bb * sa + db * invA >> 8;
741
+ const a = 255 * sa + da * invA >> 8;
742
+ return (a << 24 | b << 16 | g << 8 | r) >>> 0;
743
+ };
744
+ var colorBurnFast = (src, dst) => {
745
+ const sa = src >>> 24 & 255;
746
+ if (sa === 0) return dst;
747
+ const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
748
+ const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
749
+ const br = dr === 255 ? 255 : sr === 0 ? 0 : Math.max(0, 255 - (255 - dr << 8) / sr | 0);
750
+ const bg = dg === 255 ? 255 : sg === 0 ? 0 : Math.max(0, 255 - (255 - dg << 8) / sg | 0);
751
+ const bb = db === 255 ? 255 : sb === 0 ? 0 : Math.max(0, 255 - (255 - db << 8) / sb | 0);
752
+ if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
753
+ const invA = 255 - sa;
754
+ const da = dst >>> 24 & 255;
755
+ const r = br * sa + dr * invA >> 8;
756
+ const g = bg * sa + dg * invA >> 8;
757
+ const b = bb * sa + db * invA >> 8;
758
+ const a = 255 * sa + da * invA >> 8;
759
+ return (a << 24 | b << 16 | g << 8 | r) >>> 0;
760
+ };
761
+ var linearBurnFast = (src, dst) => {
762
+ const sa = src >>> 24 & 255;
763
+ if (sa === 0) return dst;
764
+ const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
765
+ const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
766
+ const brU = dr + sr - 255;
767
+ const bgU = dg + sg - 255;
768
+ const bbU = db + sb - 255;
769
+ const br = brU < 0 ? 0 : brU;
770
+ const bg = bgU < 0 ? 0 : bgU;
771
+ const bb = bbU < 0 ? 0 : bbU;
772
+ if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
773
+ const invA = 255 - sa;
774
+ const r = br * sa + dr * invA >> 8;
775
+ const g = bg * sa + dg * invA >> 8;
776
+ const b = bb * sa + db * invA >> 8;
777
+ const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
778
+ return (a << 24 | b << 16 | g << 8 | r) >>> 0;
779
+ };
780
+ var darkerFast = (src, dst) => {
781
+ const sa = src >>> 24 & 255;
782
+ if (sa === 0) return dst;
783
+ const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
784
+ const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
785
+ const lumSrc = sr * 77 + sg * 151 + sb * 28;
786
+ const lumDst = dr * 77 + dg * 151 + db * 28;
787
+ let br, bg, bb;
788
+ if (lumSrc < lumDst) {
789
+ br = sr;
790
+ bg = sg;
791
+ bb = sb;
792
+ } else {
793
+ br = dr;
794
+ bg = dg;
795
+ bb = db;
968
796
  }
969
- const selectionRect = {
970
- x: minX,
971
- y: minY,
972
- w: maxX - minX + 1,
973
- h: maxY - minY + 1,
974
- mask: new Uint8Array((maxX - minX + 1) * (maxY - minY + 1)),
975
- maskType: 1 /* BINARY */
976
- };
977
- const sw = selectionRect.w;
978
- const sh = selectionRect.h;
979
- const finalMask = selectionRect.mask;
980
- for (let i = 0; i < matchCount; i++) {
981
- const mx = matchX[i] - selectionRect.x;
982
- const my = matchY[i] - selectionRect.y;
983
- if (mx >= 0 && mx < sw && my >= 0 && my < sh) {
984
- finalMask[my * sw + mx] = 1;
985
- }
797
+ if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
798
+ const invA = 255 - sa;
799
+ const r = br * sa + dr * invA >> 8;
800
+ const g = bg * sa + dg * invA >> 8;
801
+ const b = bb * sa + db * invA >> 8;
802
+ const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
803
+ return (a << 24 | b << 16 | g << 8 | r) >>> 0;
804
+ };
805
+ var lightenFast = (src, dst) => {
806
+ const sa = src >>> 24 & 255;
807
+ if (sa === 0) return dst;
808
+ const br = Math.max(src & 255, dst & 255);
809
+ const bg = Math.max(src >> 8 & 255, dst >> 8 & 255);
810
+ const bb = Math.max(src >> 16 & 255, dst >> 16 & 255);
811
+ if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
812
+ const dr = dst & 255;
813
+ const dg = dst >> 8 & 255;
814
+ const db = dst >> 16 & 255;
815
+ const invA = 255 - sa;
816
+ const r = br * sa + dr * invA >> 8;
817
+ const g = bg * sa + dg * invA >> 8;
818
+ const b = bb * sa + db * invA >> 8;
819
+ const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
820
+ return (a << 24 | b << 16 | g << 8 | r) >>> 0;
821
+ };
822
+ var screenFast = (src, dst) => {
823
+ const sa = src >>> 24 & 255;
824
+ if (sa === 0) return dst;
825
+ const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
826
+ const br = 255 - ((255 - (src & 255)) * (255 - dr) >> 8);
827
+ const bg = 255 - ((255 - (src >>> 8 & 255)) * (255 - dg) >> 8);
828
+ const bb = 255 - ((255 - (src >>> 16 & 255)) * (255 - db) >> 8);
829
+ if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
830
+ const invA = 255 - sa;
831
+ const r = br * sa + dr * invA >> 8;
832
+ const g = bg * sa + dg * invA >> 8;
833
+ const b = bb * sa + db * invA >> 8;
834
+ const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
835
+ return (a << 24 | b << 16 | g << 8 | r) >>> 0;
836
+ };
837
+ var colorDodgeFast = (src, dst) => {
838
+ const sa = src >>> 24 & 255;
839
+ if (sa === 0) return dst;
840
+ const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
841
+ const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
842
+ const br = sr === 255 ? 255 : Math.min(255, (dr << 8) / (255 - sr) | 0);
843
+ const bg = sg === 255 ? 255 : Math.min(255, (dg << 8) / (255 - sg) | 0);
844
+ const bb = sb === 255 ? 255 : Math.min(255, (db << 8) / (255 - sb) | 0);
845
+ if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
846
+ const invA = 255 - sa;
847
+ const r = br * sa + dr * invA >> 8;
848
+ const g = bg * sa + dg * invA >> 8;
849
+ const b = bb * sa + db * invA >> 8;
850
+ const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
851
+ return (a << 24 | b << 16 | g << 8 | r) >>> 0;
852
+ };
853
+ var linearDodgeFast = (src, dst) => {
854
+ const sa = src >>> 24 & 255;
855
+ if (sa === 0) return dst;
856
+ const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
857
+ const brU = (src & 255) + dr;
858
+ const bgU = (src >>> 8 & 255) + dg;
859
+ const bbU = (src >>> 16 & 255) + db;
860
+ const br = brU > 255 ? 255 : brU;
861
+ const bg = bgU > 255 ? 255 : bgU;
862
+ const bb = bbU > 255 ? 255 : bbU;
863
+ if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
864
+ const invA = 255 - sa;
865
+ const r = br * sa + dr * invA >> 8;
866
+ const g = bg * sa + dg * invA >> 8;
867
+ const b = bb * sa + db * invA >> 8;
868
+ const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
869
+ return (a << 24 | b << 16 | g << 8 | r) >>> 0;
870
+ };
871
+ var lighterFast = (src, dst) => {
872
+ const sa = src >>> 24 & 255;
873
+ if (sa === 0) return dst;
874
+ const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
875
+ const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
876
+ const lumSrc = sr * 77 + sg * 151 + sb * 28;
877
+ const lumDst = dr * 77 + dg * 151 + db * 28;
878
+ let br, bg, bb;
879
+ if (lumSrc > lumDst) {
880
+ br = sr;
881
+ bg = sg;
882
+ bb = sb;
883
+ } else {
884
+ br = dr;
885
+ bg = dg;
886
+ bb = db;
986
887
  }
987
- trimRectBounds(
988
- selectionRect,
989
- { x: 0, y: 0, w: width, h: height }
990
- );
991
- const extracted = extractImageDataBuffer(
992
- imageData,
993
- selectionRect.x,
994
- selectionRect.y,
995
- selectionRect.w,
996
- selectionRect.h
997
- );
998
- return {
999
- startX,
1000
- startY,
1001
- selectionRect,
1002
- pixels: extracted
1003
- };
888
+ if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
889
+ const invA = 255 - sa;
890
+ const r = br * sa + dr * invA >> 8;
891
+ const g = bg * sa + dg * invA >> 8;
892
+ const b = bb * sa + db * invA >> 8;
893
+ const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
894
+ return (a << 24 | b << 16 | g << 8 | r) >>> 0;
895
+ };
896
+ var overlayFast = (src, dst) => {
897
+ const sa = src >>> 24 & 255;
898
+ if (sa === 0) return dst;
899
+ const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
900
+ const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
901
+ const br = dr < 128 ? 2 * sr * dr >> 8 : 255 - (2 * (255 - sr) * (255 - dr) >> 8);
902
+ const bg = dg < 128 ? 2 * sg * dg >> 8 : 255 - (2 * (255 - sg) * (255 - dg) >> 8);
903
+ const bb = db < 128 ? 2 * sb * db >> 8 : 255 - (2 * (255 - sb) * (255 - db) >> 8);
904
+ if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
905
+ const invA = 255 - sa;
906
+ const r = br * sa + dr * invA >> 8;
907
+ const g = bg * sa + dg * invA >> 8;
908
+ const b = bb * sa + db * invA >> 8;
909
+ const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
910
+ return (a << 24 | b << 16 | g << 8 | r) >>> 0;
911
+ };
912
+ var softLightFast = (src, dst) => {
913
+ const sa = src >>> 24 & 255;
914
+ if (sa === 0) return dst;
915
+ const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
916
+ const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
917
+ const br = (255 - dr) * (sr * dr >> 8) + dr * (255 - ((255 - sr) * (255 - dr) >> 8)) >> 8;
918
+ const bg = (255 - dg) * (sg * dg >> 8) + dg * (255 - ((255 - sg) * (255 - dg) >> 8)) >> 8;
919
+ const bb = (255 - db) * (sb * db >> 8) + db * (255 - ((255 - sb) * (255 - db) >> 8)) >> 8;
920
+ if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
921
+ const invA = 255 - sa;
922
+ const r = br * sa + dr * invA >> 8;
923
+ const g = bg * sa + dg * invA >> 8;
924
+ const b = bb * sa + db * invA >> 8;
925
+ const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
926
+ return (a << 24 | b << 16 | g << 8 | r) >>> 0;
927
+ };
928
+ var hardLightFast = (src, dst) => {
929
+ const sa = src >>> 24 & 255;
930
+ if (sa === 0) return dst;
931
+ const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
932
+ const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
933
+ const br = sr < 128 ? 2 * sr * dr >> 8 : 255 - (2 * (255 - sr) * (255 - dr) >> 8);
934
+ const bg = sg < 128 ? 2 * sg * dg >> 8 : 255 - (2 * (255 - sg) * (255 - dg) >> 8);
935
+ const bb = sb < 128 ? 2 * sb * db >> 8 : 255 - (2 * (255 - sb) * (255 - db) >> 8);
936
+ if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
937
+ const invA = 255 - sa;
938
+ const r = br * sa + dr * invA >> 8;
939
+ const g = bg * sa + dg * invA >> 8;
940
+ const b = bb * sa + db * invA >> 8;
941
+ const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
942
+ return (a << 24 | b << 16 | g << 8 | r) >>> 0;
943
+ };
944
+ var vividLightFast = (src, dst) => {
945
+ const sa = src >>> 24 & 255;
946
+ if (sa === 0) return dst;
947
+ const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
948
+ const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
949
+ const br = sr < 128 ? sr === 0 ? 0 : Math.max(0, 255 - (255 - dr << 8) / (2 * sr) | 0) : sr === 255 ? 255 : Math.min(255, (dr << 8) / (2 * (255 - sr)) | 0);
950
+ const bg = sg < 128 ? sg === 0 ? 0 : Math.max(0, 255 - (255 - dg << 8) / (2 * sg) | 0) : sg === 255 ? 255 : Math.min(255, (dg << 8) / (2 * (255 - sg)) | 0);
951
+ const bb = sb < 128 ? sb === 0 ? 0 : Math.max(0, 255 - (255 - db << 8) / (2 * sb) | 0) : sb === 255 ? 255 : Math.min(255, (db << 8) / (2 * (255 - sb)) | 0);
952
+ if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
953
+ const invA = 255 - sa;
954
+ const r = br * sa + dr * invA >> 8;
955
+ const g = bg * sa + dg * invA >> 8;
956
+ const b = bb * sa + db * invA >> 8;
957
+ const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
958
+ return (a << 24 | b << 16 | g << 8 | r) >>> 0;
959
+ };
960
+ var linearLightFast = (src, dst) => {
961
+ const sa = src >>> 24 & 255;
962
+ if (sa === 0) return dst;
963
+ const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
964
+ const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
965
+ const brU = dr + 2 * sr - 255;
966
+ const bgU = dg + 2 * sg - 255;
967
+ const bbU = db + 2 * sb - 255;
968
+ const br = brU < 0 ? 0 : brU > 255 ? 255 : brU;
969
+ const bg = bgU < 0 ? 0 : bgU > 255 ? 255 : bgU;
970
+ const bb = bbU < 0 ? 0 : bbU > 255 ? 255 : bbU;
971
+ if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
972
+ const invA = 255 - sa;
973
+ const r = br * sa + dr * invA >> 8;
974
+ const g = bg * sa + dg * invA >> 8;
975
+ const b = bb * sa + db * invA >> 8;
976
+ const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
977
+ return (a << 24 | b << 16 | g << 8 | r) >>> 0;
978
+ };
979
+ var pinLightFast = (src, dst) => {
980
+ const sa = src >>> 24 & 255;
981
+ if (sa === 0) return dst;
982
+ const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
983
+ const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
984
+ const br = sr < 128 ? dr < 2 * sr ? dr : 2 * sr : dr > 2 * sr - 256 ? dr : 2 * sr - 256;
985
+ const bg = sg < 128 ? dg < 2 * sg ? dg : 2 * sg : dg > 2 * sg - 256 ? dg : 2 * sg - 256;
986
+ const bb = sb < 128 ? db < 2 * sb ? db : 2 * sb : db > 2 * sb - 256 ? db : 2 * sb - 256;
987
+ if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
988
+ const invA = 255 - sa;
989
+ const r = br * sa + dr * invA >> 8;
990
+ const g = bg * sa + dg * invA >> 8;
991
+ const b = bb * sa + db * invA >> 8;
992
+ const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
993
+ return (a << 24 | b << 16 | g << 8 | r) >>> 0;
994
+ };
995
+ var hardMixFast = (src, dst) => {
996
+ const sa = src >>> 24 & 255;
997
+ if (sa === 0) return dst;
998
+ const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
999
+ const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
1000
+ const br = (sr < 128 ? sr === 0 ? 0 : Math.max(0, 255 - (255 - dr << 8) / (2 * sr) | 0) : sr === 255 ? 255 : Math.min(255, (dr << 8) / (2 * (255 - sr)) | 0)) < 128 ? 0 : 255;
1001
+ const bg = (sg < 128 ? sg === 0 ? 0 : Math.max(0, 255 - (255 - dg << 8) / (2 * sg) | 0) : sg === 255 ? 255 : Math.min(255, (dg << 8) / (2 * (255 - sg)) | 0)) < 128 ? 0 : 255;
1002
+ const bb = (sb < 128 ? sb === 0 ? 0 : Math.max(0, 255 - (255 - db << 8) / (2 * sb) | 0) : sb === 255 ? 255 : Math.min(255, (db << 8) / (2 * (255 - sb)) | 0)) < 128 ? 0 : 255;
1003
+ if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
1004
+ const invA = 255 - sa;
1005
+ const r = br * sa + dr * invA >> 8;
1006
+ const g = bg * sa + dg * invA >> 8;
1007
+ const b = bb * sa + db * invA >> 8;
1008
+ const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
1009
+ return (a << 24 | b << 16 | g << 8 | r) >>> 0;
1010
+ };
1011
+ var differenceFast = (src, dst) => {
1012
+ const sa = src >>> 24 & 255;
1013
+ if (sa === 0) return dst;
1014
+ const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
1015
+ const brD = (src & 255) - dr;
1016
+ const bgD = (src >>> 8 & 255) - dg;
1017
+ const bbD = (src >>> 16 & 255) - db;
1018
+ const br = brD < 0 ? -brD : brD;
1019
+ const bg = bgD < 0 ? -bgD : bgD;
1020
+ const bb = bbD < 0 ? -bbD : bbD;
1021
+ if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
1022
+ const invA = 255 - sa;
1023
+ const r = br * sa + dr * invA >> 8;
1024
+ const g = bg * sa + dg * invA >> 8;
1025
+ const b = bb * sa + db * invA >> 8;
1026
+ const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
1027
+ return (a << 24 | b << 16 | g << 8 | r) >>> 0;
1028
+ };
1029
+ var exclusionFast = (src, dst) => {
1030
+ const sa = src >>> 24 & 255;
1031
+ if (sa === 0) return dst;
1032
+ const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
1033
+ const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
1034
+ const br = dr + sr - (dr * sr >> 7);
1035
+ const bg = dg + sg - (dg * sg >> 7);
1036
+ const bb = db + sb - (db * sb >> 7);
1037
+ if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
1038
+ const invA = 255 - sa;
1039
+ const r = br * sa + dr * invA >> 8;
1040
+ const g = bg * sa + dg * invA >> 8;
1041
+ const b = bb * sa + db * invA >> 8;
1042
+ const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
1043
+ return (a << 24 | b << 16 | g << 8 | r) >>> 0;
1044
+ };
1045
+ var subtractFast = (src, dst) => {
1046
+ const sa = src >>> 24 & 255;
1047
+ if (sa === 0) return dst;
1048
+ const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
1049
+ const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
1050
+ const brU = dr - sr;
1051
+ const bgU = dg - sg;
1052
+ const bbU = db - sb;
1053
+ const br = brU < 0 ? 0 : brU;
1054
+ const bg = bgU < 0 ? 0 : bgU;
1055
+ const bb = bbU < 0 ? 0 : bbU;
1056
+ if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
1057
+ const invA = 255 - sa;
1058
+ const r = br * sa + dr * invA >> 8;
1059
+ const g = bg * sa + dg * invA >> 8;
1060
+ const b = bb * sa + db * invA >> 8;
1061
+ const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
1062
+ return (a << 24 | b << 16 | g << 8 | r) >>> 0;
1063
+ };
1064
+ var divideFast = (src, dst) => {
1065
+ const sa = src >>> 24 & 255;
1066
+ if (sa === 0) return dst;
1067
+ const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
1068
+ const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
1069
+ const br = sr === 0 ? 255 : Math.min(255, (dr << 8) / sr | 0);
1070
+ const bg = sg === 0 ? 255 : Math.min(255, (dg << 8) / sg | 0);
1071
+ const bb = sb === 0 ? 255 : Math.min(255, (db << 8) / sb | 0);
1072
+ if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
1073
+ const invA = 255 - sa;
1074
+ const r = br * sa + dr * invA >> 8;
1075
+ const g = bg * sa + dg * invA >> 8;
1076
+ const b = bb * sa + db * invA >> 8;
1077
+ const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
1078
+ return (a << 24 | b << 16 | g << 8 | r) >>> 0;
1079
+ };
1080
+ var BASE_FAST_BLEND_MODE_FUNCTIONS = {
1081
+ [BaseBlendMode.overwrite]: overwriteFast,
1082
+ [BaseBlendMode.sourceOver]: sourceOverFast,
1083
+ [BaseBlendMode.darken]: darkenFast,
1084
+ [BaseBlendMode.multiply]: multiplyFast,
1085
+ [BaseBlendMode.colorBurn]: colorBurnFast,
1086
+ [BaseBlendMode.linearBurn]: linearBurnFast,
1087
+ [BaseBlendMode.darkerColor]: darkerFast,
1088
+ [BaseBlendMode.lighten]: lightenFast,
1089
+ [BaseBlendMode.screen]: screenFast,
1090
+ [BaseBlendMode.colorDodge]: colorDodgeFast,
1091
+ [BaseBlendMode.linearDodge]: linearDodgeFast,
1092
+ [BaseBlendMode.lighterColor]: lighterFast,
1093
+ [BaseBlendMode.overlay]: overlayFast,
1094
+ [BaseBlendMode.softLight]: softLightFast,
1095
+ [BaseBlendMode.hardLight]: hardLightFast,
1096
+ [BaseBlendMode.vividLight]: vividLightFast,
1097
+ [BaseBlendMode.linearLight]: linearLightFast,
1098
+ [BaseBlendMode.pinLight]: pinLightFast,
1099
+ [BaseBlendMode.hardMix]: hardMixFast,
1100
+ [BaseBlendMode.difference]: differenceFast,
1101
+ [BaseBlendMode.exclusion]: exclusionFast,
1102
+ [BaseBlendMode.subtract]: subtractFast,
1103
+ [BaseBlendMode.divide]: divideFast
1104
+ };
1105
+ function makeFastBlendModeRegistry(name = "fast") {
1106
+ return makeBlendModeRegistry(BaseBlendMode, BASE_FAST_BLEND_MODE_FUNCTIONS, name);
1004
1107
  }
1005
1108
 
1006
1109
  // src/BlendModes/blend-modes-perfect.ts
@@ -1009,10 +1112,11 @@ var sourceOverPerfect = (src, dst) => {
1009
1112
  const sa = src >>> 24 & 255;
1010
1113
  if (sa === 255) return src;
1011
1114
  if (sa === 0) return dst;
1012
- const invA = 255 - sa;
1115
+ const da = dst >>> 24 & 255;
1116
+ if (da === 0) return src;
1013
1117
  const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
1014
1118
  const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
1015
- const da = dst >>> 24 & 255;
1119
+ const invA = 255 - sa;
1016
1120
  const tR = sr * sa + dr * invA;
1017
1121
  const r = tR + 1 + (tR >> 8) >> 8;
1018
1122
  const tG = sg * sa + dg * invA;
@@ -1554,9 +1658,53 @@ var BASE_PERFECT_BLEND_MODE_FUNCTIONS = {
1554
1658
  [BaseBlendMode.subtract]: subtractPerfect,
1555
1659
  [BaseBlendMode.divide]: dividePerfect
1556
1660
  };
1557
- function makePerfectBlendModeRegistry() {
1558
- return makeBlendModeRegistry(BaseBlendMode, BASE_PERFECT_BLEND_MODE_FUNCTIONS);
1661
+ function makePerfectBlendModeRegistry(name = "perfect") {
1662
+ return makeBlendModeRegistry(BaseBlendMode, BASE_PERFECT_BLEND_MODE_FUNCTIONS, name);
1663
+ }
1664
+
1665
+ // src/BlendModes/toBlendModeIndexAndName.ts
1666
+ function toBlendModeIndexAndName(input) {
1667
+ if (typeof input === "number") {
1668
+ const name = getKeyByValue(BaseBlendMode, input);
1669
+ if (name === void 0) throw new Error(`Invalid index: ${input}`);
1670
+ return {
1671
+ blendIndex: input,
1672
+ blendName: name
1673
+ };
1674
+ }
1675
+ const trimmed = input.trim();
1676
+ const num = Number(trimmed);
1677
+ const isNumeric = trimmed !== "" && !Number.isNaN(num);
1678
+ if (isNumeric && Number.isInteger(num)) {
1679
+ console.log({
1680
+ trimmed,
1681
+ num,
1682
+ isNumeric,
1683
+ isInt: Number.isInteger(num)
1684
+ });
1685
+ const name = getKeyByValue(BaseBlendMode, num);
1686
+ console.log({
1687
+ name
1688
+ });
1689
+ if (name === void 0) throw new Error(`Invalid index: ${num}`);
1690
+ return {
1691
+ blendIndex: num,
1692
+ blendName: name
1693
+ };
1694
+ }
1695
+ if (trimmed in BaseBlendMode) {
1696
+ return {
1697
+ blendIndex: BaseBlendMode[trimmed],
1698
+ blendName: trimmed
1699
+ };
1700
+ }
1701
+ throw new Error(`Invalid blend mode: ${JSON.stringify(input)}`);
1559
1702
  }
1703
+ var getKeyByValue = (obj, value) => {
1704
+ for (const key in obj) {
1705
+ if (obj[key] === value) return key;
1706
+ }
1707
+ };
1560
1708
 
1561
1709
  // src/Canvas/_constants.ts
1562
1710
  var OFFSCREEN_CANVAS_CTX_FAILED = "Failed to create OffscreenCanvas context";
@@ -1595,7 +1743,10 @@ function makeReusableCanvas() {
1595
1743
  } else {
1596
1744
  ctx.clearRect(0, 0, width, height);
1597
1745
  }
1598
- return { canvas, ctx };
1746
+ return {
1747
+ canvas,
1748
+ ctx
1749
+ };
1599
1750
  }
1600
1751
  get2.reset = () => {
1601
1752
  canvas = null;
@@ -1609,21 +1760,13 @@ async function imgBlobToImageData(blob) {
1609
1760
  let bitmap = null;
1610
1761
  try {
1611
1762
  bitmap = await createImageBitmap(blob);
1612
- const canvas = new OffscreenCanvas(
1613
- bitmap.width,
1614
- bitmap.height
1615
- );
1763
+ const canvas = new OffscreenCanvas(bitmap.width, bitmap.height);
1616
1764
  const ctx = canvas.getContext("2d");
1617
1765
  if (!ctx) {
1618
1766
  throw new Error("Failed to get 2D context");
1619
1767
  }
1620
1768
  ctx.drawImage(bitmap, 0, 0);
1621
- return ctx.getImageData(
1622
- 0,
1623
- 0,
1624
- bitmap.width,
1625
- bitmap.height
1626
- );
1769
+ return ctx.getImageData(0, 0, bitmap.width, bitmap.height);
1627
1770
  } finally {
1628
1771
  bitmap?.close();
1629
1772
  }
@@ -1671,6 +1814,64 @@ async function writeImageDataToClipboard(imageData) {
1671
1814
  return writeImgBlobToClipboard(blob);
1672
1815
  }
1673
1816
 
1817
+ // src/History/HistoryManager.ts
1818
+ var HistoryManager = class {
1819
+ constructor(maxSteps = 50) {
1820
+ this.maxSteps = maxSteps;
1821
+ this.undoStack = [];
1822
+ this.redoStack = [];
1823
+ this.listeners = /* @__PURE__ */ new Set();
1824
+ }
1825
+ undoStack;
1826
+ redoStack;
1827
+ listeners;
1828
+ get canUndo() {
1829
+ return this.undoStack.length > 0;
1830
+ }
1831
+ get canRedo() {
1832
+ return this.redoStack.length > 0;
1833
+ }
1834
+ subscribe(fn) {
1835
+ this.listeners.add(fn);
1836
+ return () => this.listeners.delete(fn);
1837
+ }
1838
+ notify() {
1839
+ this.listeners.forEach((fn) => fn());
1840
+ }
1841
+ commit(action) {
1842
+ this.undoStack.push(action);
1843
+ this.clearRedoStack();
1844
+ if (this.undoStack.length > this.maxSteps) {
1845
+ this.undoStack.shift()?.dispose?.();
1846
+ }
1847
+ this.notify();
1848
+ }
1849
+ undo() {
1850
+ let action = this.undoStack.pop();
1851
+ if (!action) return;
1852
+ this.redoStack.push(action);
1853
+ action.undo();
1854
+ this.notify();
1855
+ }
1856
+ redo() {
1857
+ let action = this.redoStack.pop();
1858
+ if (!action) return;
1859
+ this.undoStack.push(action);
1860
+ action.redo();
1861
+ this.notify();
1862
+ }
1863
+ clearRedoStack() {
1864
+ let length = this.redoStack.length;
1865
+ for (let i = 0; i < length; i++) {
1866
+ let action = this.redoStack[i];
1867
+ if (action) {
1868
+ action.dispose?.();
1869
+ }
1870
+ }
1871
+ this.redoStack.length = 0;
1872
+ }
1873
+ };
1874
+
1674
1875
  // src/History/PixelPatchTiles.ts
1675
1876
  var PixelTile = class {
1676
1877
  constructor(id, tx, ty, tileArea) {
@@ -1724,12 +1925,7 @@ var PixelAccumulator = class {
1724
1925
  tile.ty = ty;
1725
1926
  return tile;
1726
1927
  }
1727
- return new PixelTile(
1728
- id,
1729
- tx,
1730
- ty,
1731
- this.config.tileArea
1732
- );
1928
+ return new PixelTile(id, tx, ty, this.config.tileArea);
1733
1929
  }
1734
1930
  recyclePatch(patch) {
1735
1931
  const before = patch.beforeTiles;
@@ -1760,11 +1956,7 @@ var PixelAccumulator = class {
1760
1956
  let id = ty * columns + tx;
1761
1957
  let tile = this.lookup[id];
1762
1958
  if (!tile) {
1763
- tile = this.getTile(
1764
- id,
1765
- tx,
1766
- ty
1767
- );
1959
+ tile = this.getTile(id, tx, ty);
1768
1960
  this.extractState(tile);
1769
1961
  this.lookup[id] = tile;
1770
1962
  this.beforeTiles.push(tile);
@@ -1790,11 +1982,7 @@ var PixelAccumulator = class {
1790
1982
  let id = ty * columns + tx;
1791
1983
  let tile = this.lookup[id];
1792
1984
  if (!tile) {
1793
- tile = this.getTile(
1794
- id,
1795
- tx,
1796
- ty
1797
- );
1985
+ tile = this.getTile(id, tx, ty);
1798
1986
  this.extractState(tile);
1799
1987
  this.lookup[id] = tile;
1800
1988
  this.beforeTiles.push(tile);
@@ -1833,78 +2021,16 @@ var PixelAccumulator = class {
1833
2021
  for (let i = 0; i < length; i++) {
1834
2022
  let beforeTile = this.beforeTiles[i];
1835
2023
  if (beforeTile) {
1836
- let afterTile = this.getTile(
1837
- beforeTile.id,
1838
- beforeTile.tx,
1839
- beforeTile.ty
1840
- );
1841
- this.extractState(afterTile);
1842
- afterTiles.push(afterTile);
1843
- }
1844
- }
1845
- return afterTiles;
1846
- }
1847
- reset() {
1848
- this.lookup = [];
1849
- this.beforeTiles = [];
1850
- }
1851
- };
1852
-
1853
- // src/History/HistoryManager.ts
1854
- var HistoryManager = class {
1855
- constructor(maxSteps = 50) {
1856
- this.maxSteps = maxSteps;
1857
- this.undoStack = [];
1858
- this.redoStack = [];
1859
- this.listeners = /* @__PURE__ */ new Set();
1860
- }
1861
- undoStack;
1862
- redoStack;
1863
- listeners;
1864
- get canUndo() {
1865
- return this.undoStack.length > 0;
1866
- }
1867
- get canRedo() {
1868
- return this.redoStack.length > 0;
1869
- }
1870
- subscribe(fn) {
1871
- this.listeners.add(fn);
1872
- return () => this.listeners.delete(fn);
1873
- }
1874
- notify() {
1875
- this.listeners.forEach((fn) => fn());
1876
- }
1877
- commit(action) {
1878
- this.undoStack.push(action);
1879
- this.clearRedoStack();
1880
- if (this.undoStack.length > this.maxSteps) {
1881
- this.undoStack.shift()?.dispose?.();
1882
- }
1883
- this.notify();
1884
- }
1885
- undo() {
1886
- let action = this.undoStack.pop();
1887
- if (!action) return;
1888
- this.redoStack.push(action);
1889
- action.undo();
1890
- this.notify();
1891
- }
1892
- redo() {
1893
- let action = this.redoStack.pop();
1894
- if (!action) return;
1895
- this.undoStack.push(action);
1896
- action.redo();
1897
- this.notify();
1898
- }
1899
- clearRedoStack() {
1900
- let length = this.redoStack.length;
1901
- for (let i = 0; i < length; i++) {
1902
- let action = this.redoStack[i];
1903
- if (action) {
1904
- action.dispose?.();
2024
+ let afterTile = this.getTile(beforeTile.id, beforeTile.tx, beforeTile.ty);
2025
+ this.extractState(afterTile);
2026
+ afterTiles.push(afterTile);
1905
2027
  }
1906
2028
  }
1907
- this.redoStack.length = 0;
2029
+ return afterTiles;
2030
+ }
2031
+ reset() {
2032
+ this.lookup = [];
2033
+ this.beforeTiles = [];
1908
2034
  }
1909
2035
  };
1910
2036
 
@@ -1925,20 +2051,20 @@ var PixelEngineConfig = class {
1925
2051
  }
1926
2052
  };
1927
2053
 
1928
- // src/PixelData/applyMaskToPixelData.ts
1929
- function applyMaskToPixelData(dst, mask, opts = {}) {
2054
+ // src/PixelData/applyAlphaMaskToPixelData.ts
2055
+ function applyAlphaMaskToPixelData(dst, mask, opts = {}) {
1930
2056
  const {
1931
2057
  x: targetX = 0,
1932
2058
  y: targetY = 0,
1933
2059
  w: width = dst.width,
1934
2060
  h: height = dst.height,
1935
2061
  alpha: globalAlpha = 255,
1936
- maskType = 0 /* ALPHA */,
1937
2062
  mw,
1938
2063
  mx = 0,
1939
2064
  my = 0,
1940
2065
  invertMask = false
1941
2066
  } = opts;
2067
+ if (globalAlpha === 0) return;
1942
2068
  let x = targetX;
1943
2069
  let y = targetY;
1944
2070
  let w = width;
@@ -1951,59 +2077,557 @@ function applyMaskToPixelData(dst, mask, opts = {}) {
1951
2077
  h += y;
1952
2078
  y = 0;
1953
2079
  }
1954
- const actualW = Math.min(w, dst.width - x);
1955
- const actualH = Math.min(h, dst.height - y);
1956
- if (actualW <= 0 || actualH <= 0 || globalAlpha === 0) {
2080
+ w = Math.min(w, dst.width - x);
2081
+ h = Math.min(h, dst.height - y);
2082
+ if (w <= 0) return;
2083
+ if (h <= 0) return;
2084
+ const mPitch = mw ?? width;
2085
+ if (mPitch <= 0) return;
2086
+ const maskHeight = mask.length / mPitch | 0;
2087
+ const startX = mx + (x - targetX);
2088
+ const startY = my + (y - targetY);
2089
+ const sX0 = Math.max(0, startX);
2090
+ const sY0 = Math.max(0, startY);
2091
+ const sX1 = Math.min(mPitch, startX + w);
2092
+ const sY1 = Math.min(maskHeight, startY + h);
2093
+ const finalW = sX1 - sX0;
2094
+ const finalH = sY1 - sY0;
2095
+ if (finalW <= 0) return;
2096
+ if (finalH <= 0) return;
2097
+ const xShift = sX0 - startX;
2098
+ const yShift = sY0 - startY;
2099
+ const dst32 = dst.data32;
2100
+ const dw = dst.width;
2101
+ const dStride = dw - finalW;
2102
+ const mStride = mPitch - finalW;
2103
+ let dIdx = (y + yShift) * dw + (x + xShift);
2104
+ let mIdx = sY0 * mPitch + sX0;
2105
+ for (let iy = 0; iy < h; iy++) {
2106
+ for (let ix = 0; ix < w; ix++) {
2107
+ const mVal = mask[mIdx];
2108
+ const effectiveM = invertMask ? 255 - mVal : mVal;
2109
+ let weight = 0;
2110
+ if (effectiveM === 0) {
2111
+ weight = 0;
2112
+ } else if (effectiveM === 255) {
2113
+ weight = globalAlpha;
2114
+ } else if (globalAlpha === 255) {
2115
+ weight = effectiveM;
2116
+ } else {
2117
+ weight = effectiveM * globalAlpha + 128 >> 8;
2118
+ }
2119
+ if (weight === 0) {
2120
+ dst32[dIdx] = (dst32[dIdx] & 16777215) >>> 0;
2121
+ } else if (weight !== 255) {
2122
+ const d = dst32[dIdx];
2123
+ const da = d >>> 24;
2124
+ if (da !== 0) {
2125
+ const finalAlpha = da === 255 ? weight : da * weight + 128 >> 8;
2126
+ dst32[dIdx] = (d & 16777215 | finalAlpha << 24) >>> 0;
2127
+ }
2128
+ }
2129
+ dIdx++;
2130
+ mIdx++;
2131
+ }
2132
+ dIdx += dStride;
2133
+ mIdx += mStride;
2134
+ }
2135
+ }
2136
+
2137
+ // src/History/PixelMutator/mutatorApplyAlphaMask.ts
2138
+ var defaults = {
2139
+ applyAlphaMaskToPixelData
2140
+ };
2141
+ var mutatorApplyAlphaMask = ((writer, deps = defaults) => {
2142
+ const {
2143
+ applyAlphaMaskToPixelData: applyAlphaMaskToPixelData2 = defaults.applyAlphaMaskToPixelData
2144
+ } = deps;
2145
+ return {
2146
+ applyAlphaMask: (mask, opts = {}) => {
2147
+ let target = writer.target;
2148
+ const {
2149
+ x = 0,
2150
+ y = 0,
2151
+ w = writer.target.width,
2152
+ h = writer.target.height
2153
+ } = opts;
2154
+ writer.accumulator.storeRegionBeforeState(x, y, w, h);
2155
+ applyAlphaMaskToPixelData2(target, mask, opts);
2156
+ }
2157
+ };
2158
+ });
2159
+
2160
+ // src/PixelData/applyBinaryMaskToPixelData.ts
2161
+ function applyBinaryMaskToPixelData(dst, mask, opts = {}) {
2162
+ const {
2163
+ x: targetX = 0,
2164
+ y: targetY = 0,
2165
+ w: width = dst.width,
2166
+ h: height = dst.height,
2167
+ alpha = 255,
2168
+ mw,
2169
+ mx = 0,
2170
+ my = 0,
2171
+ invertMask = false
2172
+ } = opts;
2173
+ if (alpha === 0) return;
2174
+ let x = targetX;
2175
+ let y = targetY;
2176
+ let w = width;
2177
+ let h = height;
2178
+ if (x < 0) {
2179
+ w += x;
2180
+ x = 0;
2181
+ }
2182
+ if (y < 0) {
2183
+ h += y;
2184
+ y = 0;
2185
+ }
2186
+ w = Math.min(w, dst.width - x);
2187
+ h = Math.min(h, dst.height - y);
2188
+ if (w <= 0) return;
2189
+ if (h <= 0) return;
2190
+ const mPitch = mw ?? width;
2191
+ if (mPitch <= 0) return;
2192
+ const maskHeight = mask.length / mPitch | 0;
2193
+ const startX = mx + (x - targetX);
2194
+ const startY = my + (y - targetY);
2195
+ const sX0 = Math.max(0, startX);
2196
+ const sY0 = Math.max(0, startY);
2197
+ const sX1 = Math.min(mPitch, startX + w);
2198
+ const sY1 = Math.min(maskHeight, startY + h);
2199
+ const finalW = sX1 - sX0;
2200
+ const finalH = sY1 - sY0;
2201
+ if (finalW <= 0) return;
2202
+ if (finalH <= 0) return;
2203
+ const xShift = sX0 - startX;
2204
+ const yShift = sY0 - startY;
2205
+ const dst32 = dst.data32;
2206
+ const dw = dst.width;
2207
+ const dStride = dw - finalW;
2208
+ const mStride = mPitch - finalW;
2209
+ let dIdx = (y + yShift) * dw + (x + xShift);
2210
+ let mIdx = sY0 * mPitch + sX0;
2211
+ for (let iy = 0; iy < h; iy++) {
2212
+ for (let ix = 0; ix < w; ix++) {
2213
+ const mVal = mask[mIdx];
2214
+ const isMaskedOut = invertMask ? mVal !== 0 : mVal === 0;
2215
+ if (isMaskedOut) {
2216
+ dst32[dIdx] = (dst32[dIdx] & 16777215) >>> 0;
2217
+ } else if (alpha !== 255) {
2218
+ const d = dst32[dIdx];
2219
+ const da = d >>> 24;
2220
+ if (da !== 0) {
2221
+ const finalAlpha = da === 255 ? alpha : da * alpha + 128 >> 8;
2222
+ dst32[dIdx] = (d & 16777215 | finalAlpha << 24) >>> 0;
2223
+ }
2224
+ }
2225
+ dIdx++;
2226
+ mIdx++;
2227
+ }
2228
+ dIdx += dStride;
2229
+ mIdx += mStride;
2230
+ }
2231
+ }
2232
+
2233
+ // src/History/PixelMutator/mutatorApplyBinaryMask.ts
2234
+ var defaults2 = {
2235
+ applyBinaryMaskToPixelData
2236
+ };
2237
+ var mutatorApplyBinaryMask = ((writer, deps = defaults2) => {
2238
+ const {
2239
+ applyBinaryMaskToPixelData: applyBinaryMaskToPixelData2 = defaults2.applyBinaryMaskToPixelData
2240
+ } = deps;
2241
+ return {
2242
+ applyBinaryMask: (mask, opts = {}) => {
2243
+ let target = writer.target;
2244
+ const {
2245
+ x = 0,
2246
+ y = 0,
2247
+ w = writer.target.width,
2248
+ h = writer.target.height
2249
+ } = opts;
2250
+ writer.accumulator.storeRegionBeforeState(x, y, w, h);
2251
+ applyBinaryMaskToPixelData2(target, mask, opts);
2252
+ }
2253
+ };
2254
+ });
2255
+
2256
+ // src/Rect/getCircleBrushOrPencilBounds.ts
2257
+ function getCircleBrushOrPencilBounds(centerX, centerY, brushSize, targetWidth, targetHeight, out) {
2258
+ const r = brushSize / 2;
2259
+ const minOffset = -Math.ceil(r - 0.5);
2260
+ const maxOffset = Math.floor(r - 0.5);
2261
+ const startX = Math.floor(centerX + minOffset);
2262
+ const startY = Math.floor(centerY + minOffset);
2263
+ const endX = Math.floor(centerX + maxOffset) + 1;
2264
+ const endY = Math.floor(centerY + maxOffset) + 1;
2265
+ const res = out ?? {
2266
+ x: 0,
2267
+ y: 0,
2268
+ w: 0,
2269
+ h: 0
2270
+ };
2271
+ const cStartX = Math.max(0, startX);
2272
+ const cStartY = Math.max(0, startY);
2273
+ const cEndX = Math.min(targetWidth, endX);
2274
+ const cEndY = Math.min(targetHeight, endY);
2275
+ const w = cEndX - cStartX;
2276
+ const h = cEndY - cStartY;
2277
+ res.x = cStartX;
2278
+ res.y = cStartY;
2279
+ res.w = w < 0 ? 0 : w;
2280
+ res.h = h < 0 ? 0 : h;
2281
+ return res;
2282
+ }
2283
+
2284
+ // src/PixelData/applyCircleBrushToPixelData.ts
2285
+ function applyCircleBrushToPixelData(target, color, centerX, centerY, brushSize, alpha = 255, fallOff, blendFn = sourceOverPerfect, bounds) {
2286
+ const targetWidth = target.width;
2287
+ const targetHeight = target.height;
2288
+ const b = bounds ?? getCircleBrushOrPencilBounds(centerX, centerY, brushSize, targetWidth, targetHeight);
2289
+ if (b.w <= 0 || b.h <= 0) return;
2290
+ const data32 = target.data32;
2291
+ const r = brushSize / 2;
2292
+ const rSqr = r * r;
2293
+ const invR = 1 / r;
2294
+ const centerOffset = brushSize % 2 === 0 ? 0.5 : 0;
2295
+ const endX = b.x + b.w;
2296
+ const endY = b.y + b.h;
2297
+ const fCenterX = Math.floor(centerX);
2298
+ const fCenterY = Math.floor(centerY);
2299
+ const baseSrcAlpha = color >>> 24;
2300
+ const colorRGB = color & 16777215;
2301
+ const isOpaque = alpha === 255;
2302
+ const isOverwrite = blendFn.isOverwrite;
2303
+ for (let cy = b.y; cy < endY; cy++) {
2304
+ const relY = cy - fCenterY + centerOffset;
2305
+ const dySqr = relY * relY;
2306
+ const rowOffset = cy * targetWidth;
2307
+ for (let cx = b.x; cx < endX; cx++) {
2308
+ const relX = cx - fCenterX + centerOffset;
2309
+ const dSqr = relX * relX + dySqr;
2310
+ if (dSqr <= rSqr) {
2311
+ const idx = rowOffset + cx;
2312
+ let weight = alpha;
2313
+ const strength = fallOff(1 - Math.sqrt(dSqr) * invR);
2314
+ const maskVal = strength * 255 | 0;
2315
+ if (maskVal === 0) continue;
2316
+ if (isOpaque) {
2317
+ weight = maskVal;
2318
+ } else if (maskVal !== 255) {
2319
+ weight = maskVal * alpha + 128 >> 8;
2320
+ }
2321
+ let finalCol = color;
2322
+ if (weight < 255) {
2323
+ const a = baseSrcAlpha * weight + 128 >> 8;
2324
+ if (a === 0 && !isOverwrite) continue;
2325
+ finalCol = (colorRGB | a << 24) >>> 0;
2326
+ }
2327
+ data32[idx] = blendFn(finalCol, data32[idx]);
2328
+ }
2329
+ }
2330
+ }
2331
+ }
2332
+
2333
+ // src/History/PixelMutator/mutatorApplyCircleBrush.ts
2334
+ var defaults3 = {
2335
+ applyCircleBrushToPixelData,
2336
+ getCircleBrushOrPencilBounds
2337
+ };
2338
+ var mutatorApplyCircleBrush = ((writer, deps = defaults3) => {
2339
+ const {
2340
+ applyCircleBrushToPixelData: applyCircleBrushToPixelData2 = defaults3.applyCircleBrushToPixelData,
2341
+ getCircleBrushOrPencilBounds: getCircleBrushOrPencilBounds2 = defaults3.getCircleBrushOrPencilBounds
2342
+ } = deps;
2343
+ const boundsOut = {
2344
+ x: 0,
2345
+ y: 0,
2346
+ w: 0,
2347
+ h: 0
2348
+ };
2349
+ return {
2350
+ applyCircleBrush(color, centerX, centerY, brushSize, alpha = 255, fallOff, blendFn) {
2351
+ const bounds = getCircleBrushOrPencilBounds2(centerX, centerY, brushSize, writer.target.width, writer.target.height, boundsOut);
2352
+ const {
2353
+ x,
2354
+ y,
2355
+ w,
2356
+ h
2357
+ } = bounds;
2358
+ writer.accumulator.storeRegionBeforeState(x, y, w, h);
2359
+ applyCircleBrushToPixelData2(writer.target, color, centerX, centerY, brushSize, alpha, fallOff, blendFn, bounds);
2360
+ }
2361
+ };
2362
+ });
2363
+
2364
+ // src/Algorithm/forEachLinePoint.ts
2365
+ function forEachLinePoint(x0, y0, x1, y1, callback) {
2366
+ const dx = x1 - x0;
2367
+ const dy = y1 - y0;
2368
+ const steps = Math.max(Math.abs(dx), Math.abs(dy));
2369
+ if (steps === 0) {
2370
+ callback(x0, y0);
1957
2371
  return;
1958
2372
  }
2373
+ const xInc = dx / steps;
2374
+ const yInc = dy / steps;
2375
+ let curX = x0;
2376
+ let curY = y0;
2377
+ for (let i = 0; i <= steps; i++) {
2378
+ callback(curX, curY);
2379
+ curX += xInc;
2380
+ curY += yInc;
2381
+ }
2382
+ }
2383
+
2384
+ // src/PixelData/blendColorPixelDataAlphaMask.ts
2385
+ function blendColorPixelDataAlphaMask(dst, color, mask, opts) {
2386
+ const {
2387
+ x: targetX = 0,
2388
+ y: targetY = 0,
2389
+ w: width = dst.width,
2390
+ h: height = dst.height,
2391
+ alpha: globalAlpha = 255,
2392
+ blendFn = sourceOverPerfect,
2393
+ mw = width,
2394
+ mx = 0,
2395
+ my = 0,
2396
+ invertMask = false
2397
+ } = opts;
2398
+ if (globalAlpha === 0 || !mask) return;
2399
+ const baseSrcAlpha = color >>> 24;
2400
+ const isOverwrite = blendFn.isOverwrite || false;
2401
+ if (baseSrcAlpha === 0 && !isOverwrite) return;
2402
+ let x = targetX;
2403
+ let y = targetY;
2404
+ let w = width;
2405
+ let h = height;
2406
+ if (x < 0) {
2407
+ w += x;
2408
+ x = 0;
2409
+ }
2410
+ if (y < 0) {
2411
+ h += y;
2412
+ y = 0;
2413
+ }
2414
+ const actualW = Math.min(w, dst.width - x);
2415
+ const actualH = Math.min(h, dst.height - y);
2416
+ if (actualW <= 0 || actualH <= 0) return;
2417
+ const dx = x - targetX | 0;
2418
+ const dy = y - targetY | 0;
1959
2419
  const dst32 = dst.data32;
1960
2420
  const dw = dst.width;
1961
- const mPitch = mw ?? width;
1962
- const isAlpha = maskType === 0 /* ALPHA */;
1963
- const dx = x - targetX;
1964
- const dy = y - targetY;
1965
- let dIdx = y * dw + x;
1966
- let mIdx = (my + dy) * mPitch + (mx + dx);
1967
- const dStride = dw - actualW;
1968
- const mStride = mPitch - actualW;
2421
+ const mPitch = mw;
2422
+ let dIdx = y * dw + x | 0;
2423
+ let mIdx = (my + dy) * mPitch + (mx + dx) | 0;
2424
+ const dStride = dw - actualW | 0;
2425
+ let mStride = mPitch - actualW | 0;
2426
+ const isOpaque = globalAlpha === 255;
2427
+ const colorRGB = color & 16777215;
1969
2428
  for (let iy = 0; iy < actualH; iy++) {
1970
2429
  for (let ix = 0; ix < actualW; ix++) {
1971
2430
  const mVal = mask[mIdx];
2431
+ const effM = invertMask ? 255 - mVal : mVal;
2432
+ if (effM === 0) {
2433
+ dIdx++;
2434
+ mIdx++;
2435
+ continue;
2436
+ }
1972
2437
  let weight = globalAlpha;
1973
- if (isAlpha) {
1974
- const effectiveM = invertMask ? 255 - mVal : mVal;
1975
- if (effectiveM === 0) {
1976
- dst32[dIdx] = (dst32[dIdx] & 16777215) >>> 0;
1977
- dIdx++;
1978
- mIdx++;
1979
- continue;
1980
- }
1981
- weight = globalAlpha === 255 ? effectiveM : effectiveM * globalAlpha + 128 >> 8;
1982
- } else {
1983
- const isHit = invertMask ? mVal === 0 : mVal === 1;
1984
- if (!isHit) {
1985
- dst32[dIdx] = (dst32[dIdx] & 16777215) >>> 0;
2438
+ if (isOpaque) {
2439
+ weight = effM;
2440
+ } else if (effM !== 255) {
2441
+ weight = effM * globalAlpha + 128 >> 8;
2442
+ }
2443
+ if (weight === 0) {
2444
+ dIdx++;
2445
+ mIdx++;
2446
+ continue;
2447
+ }
2448
+ let finalCol = color;
2449
+ if (weight < 255) {
2450
+ const a = baseSrcAlpha * weight + 128 >> 8;
2451
+ if (a === 0 && !isOverwrite) {
1986
2452
  dIdx++;
1987
2453
  mIdx++;
1988
2454
  continue;
1989
2455
  }
1990
- weight = globalAlpha;
2456
+ finalCol = (colorRGB | a << 24) >>> 0;
1991
2457
  }
1992
- if (weight === 0) {
1993
- dst32[dIdx] = (dst32[dIdx] & 16777215) >>> 0;
1994
- } else {
1995
- const d = dst32[dIdx];
1996
- const da = d >>> 24;
1997
- let finalAlpha = da;
1998
- if (da === 0) {
1999
- } else if (weight === 255) {
2000
- } else if (da === 255) {
2001
- finalAlpha = weight;
2002
- } else {
2003
- finalAlpha = da * weight + 128 >> 8;
2458
+ dst32[dIdx] = blendFn(finalCol, dst32[dIdx]);
2459
+ dIdx++;
2460
+ mIdx++;
2461
+ }
2462
+ dIdx += dStride;
2463
+ mIdx += mStride;
2464
+ }
2465
+ }
2466
+
2467
+ // src/Rect/getCircleBrushOrPencilStrokeBounds.ts
2468
+ function getCircleBrushOrPencilStrokeBounds(x0, y0, x1, y1, brushSize, result) {
2469
+ const r = Math.ceil(brushSize / 2);
2470
+ const minX = Math.min(x0, x1) - r;
2471
+ const minY = Math.min(y0, y1) - r;
2472
+ const maxX = Math.max(x0, x1) + r;
2473
+ const maxY = Math.max(x0, y1) + r;
2474
+ result.x = Math.floor(minX);
2475
+ result.y = Math.floor(minY);
2476
+ result.w = Math.ceil(maxX - minX);
2477
+ result.h = Math.ceil(maxY - minY);
2478
+ return result;
2479
+ }
2480
+
2481
+ // src/History/PixelMutator/mutatorApplyCircleBrushStroke.ts
2482
+ var defaults4 = {
2483
+ forEachLinePoint,
2484
+ blendColorPixelDataAlphaMask,
2485
+ getCircleBrushOrPencilBounds,
2486
+ getCircleBrushOrPencilStrokeBounds
2487
+ };
2488
+ var mutatorApplyCircleBrushStroke = ((writer, deps = defaults4) => {
2489
+ const {
2490
+ forEachLinePoint: forEachLinePoint2 = defaults4.forEachLinePoint,
2491
+ blendColorPixelDataAlphaMask: blendColorPixelDataAlphaMask2 = defaults4.blendColorPixelDataAlphaMask,
2492
+ getCircleBrushOrPencilBounds: getCircleBrushOrPencilBounds2 = defaults4.getCircleBrushOrPencilBounds,
2493
+ getCircleBrushOrPencilStrokeBounds: getCircleBrushOrPencilStrokeBounds2 = defaults4.getCircleBrushOrPencilStrokeBounds
2494
+ } = deps;
2495
+ const strokeBoundsOut = {
2496
+ x: 0,
2497
+ y: 0,
2498
+ w: 0,
2499
+ h: 0
2500
+ };
2501
+ const circleBrushBounds = {
2502
+ x: 0,
2503
+ y: 0,
2504
+ w: 0,
2505
+ h: 0
2506
+ };
2507
+ const blendColorPixelOptions = {
2508
+ alpha: 255,
2509
+ blendFn: sourceOverPerfect,
2510
+ x: 0,
2511
+ y: 0,
2512
+ w: 0,
2513
+ h: 0
2514
+ };
2515
+ return {
2516
+ applyCircleBrushStroke(color, x0, y0, x1, y1, brushSize, alpha = 255, fallOff, blendFn = sourceOverPerfect) {
2517
+ const {
2518
+ x: bx,
2519
+ y: by,
2520
+ w: bw,
2521
+ h: bh
2522
+ } = getCircleBrushOrPencilStrokeBounds2(x0, y0, x1, y1, brushSize, strokeBoundsOut);
2523
+ if (bw <= 0 || bh <= 0) return;
2524
+ const mask = new Uint8Array(bw * bh);
2525
+ const r = brushSize / 2;
2526
+ const rSqr = r * r;
2527
+ const invR = 1 / r;
2528
+ const centerOffset = brushSize % 2 === 0 ? 0.5 : 0;
2529
+ const targetWidth = writer.target.width;
2530
+ const targetHeight = writer.target.height;
2531
+ forEachLinePoint2(x0, y0, x1, y1, (px, py) => {
2532
+ const {
2533
+ x: cbx,
2534
+ y: cby,
2535
+ w: cbw,
2536
+ h: cbh
2537
+ } = getCircleBrushOrPencilBounds2(px, py, brushSize, targetWidth, targetHeight, circleBrushBounds);
2538
+ writer.accumulator.storeRegionBeforeState(cbx, cby, cbw, cbh);
2539
+ const startX = Math.max(bx, cbx);
2540
+ const startY = Math.max(by, cby);
2541
+ const endX = Math.min(bx + bw, cbx + cbw);
2542
+ const endY = Math.min(by + bh, cby + cbh);
2543
+ const fPx = Math.floor(px);
2544
+ const fPy = Math.floor(py);
2545
+ for (let my = startY; my < endY; my++) {
2546
+ const dy = my - fPy + centerOffset;
2547
+ const dySqr = dy * dy;
2548
+ const maskRowOffset = (my - by) * bw;
2549
+ for (let mx = startX; mx < endX; mx++) {
2550
+ const dx = mx - fPx + centerOffset;
2551
+ const dSqr = dx * dx + dySqr;
2552
+ if (dSqr <= rSqr) {
2553
+ const maskIdx = maskRowOffset + (mx - bx);
2554
+ const dist = Math.sqrt(dSqr) * invR;
2555
+ const intensity = fallOff(1 - dist) * 255 | 0;
2556
+ if (intensity > mask[maskIdx]) {
2557
+ mask[maskIdx] = intensity;
2558
+ }
2559
+ }
2560
+ }
2004
2561
  }
2005
- dst32[dIdx] = (d & 16777215 | finalAlpha << 24) >>> 0;
2562
+ });
2563
+ blendColorPixelOptions.blendFn = blendFn;
2564
+ blendColorPixelOptions.alpha = alpha;
2565
+ blendColorPixelOptions.x = bx;
2566
+ blendColorPixelOptions.y = by;
2567
+ blendColorPixelOptions.w = bw;
2568
+ blendColorPixelOptions.h = bh;
2569
+ blendColorPixelDataAlphaMask2(writer.target, color, mask, blendColorPixelOptions);
2570
+ }
2571
+ };
2572
+ });
2573
+
2574
+ // src/PixelData/blendColorPixelDataBinaryMask.ts
2575
+ function blendColorPixelDataBinaryMask(dst, color, mask, opts = {}) {
2576
+ const {
2577
+ x: targetX = 0,
2578
+ y: targetY = 0,
2579
+ w: width = dst.width,
2580
+ h: height = dst.height,
2581
+ alpha: globalAlpha = 255,
2582
+ blendFn = sourceOverPerfect,
2583
+ mw = width,
2584
+ mx = 0,
2585
+ my = 0,
2586
+ invertMask = false
2587
+ } = opts;
2588
+ if (globalAlpha === 0 || !mask) return;
2589
+ const baseSrcAlpha = color >>> 24;
2590
+ const isOverwrite = blendFn.isOverwrite || false;
2591
+ if (baseSrcAlpha === 0 && !isOverwrite) return;
2592
+ let x = targetX;
2593
+ let y = targetY;
2594
+ let w = width;
2595
+ let h = height;
2596
+ if (x < 0) {
2597
+ w += x;
2598
+ x = 0;
2599
+ }
2600
+ if (y < 0) {
2601
+ h += y;
2602
+ y = 0;
2603
+ }
2604
+ const actualW = Math.min(w, dst.width - x);
2605
+ const actualH = Math.min(h, dst.height - y);
2606
+ if (actualW <= 0 || actualH <= 0) return;
2607
+ let baseColorWithGlobalAlpha = color;
2608
+ if (globalAlpha < 255) {
2609
+ const a = baseSrcAlpha * globalAlpha + 128 >> 8;
2610
+ if (a === 0 && !isOverwrite) return;
2611
+ baseColorWithGlobalAlpha = (color & 16777215 | a << 24) >>> 0;
2612
+ }
2613
+ const dx = x - targetX | 0;
2614
+ const dy = y - targetY | 0;
2615
+ const dst32 = dst.data32;
2616
+ const dw = dst.width;
2617
+ const mPitch = mw;
2618
+ let dIdx = y * dw + x | 0;
2619
+ let mIdx = (my + dy) * mPitch + (mx + dx) | 0;
2620
+ const dStride = dw - actualW | 0;
2621
+ const mStride = mPitch - actualW | 0;
2622
+ const skipVal = invertMask ? 1 : 0;
2623
+ for (let iy = 0; iy < actualH; iy++) {
2624
+ for (let ix = 0; ix < actualW; ix++) {
2625
+ if (mask[mIdx] === skipVal) {
2626
+ dIdx++;
2627
+ mIdx++;
2628
+ continue;
2006
2629
  }
2630
+ dst32[dIdx] = blendFn(baseColorWithGlobalAlpha, dst32[dIdx]);
2007
2631
  dIdx++;
2008
2632
  mIdx++;
2009
2633
  }
@@ -2012,63 +2636,424 @@ function applyMaskToPixelData(dst, mask, opts = {}) {
2012
2636
  }
2013
2637
  }
2014
2638
 
2015
- // src/History/PixelWriter.ts
2016
- var PixelWriter = class {
2017
- target;
2018
- historyManager;
2019
- accumulator;
2020
- config;
2021
- mutator;
2022
- constructor(target, mutatorFactory, {
2023
- tileSize = 256,
2024
- maxHistorySteps = 50,
2025
- historyManager = new HistoryManager(maxHistorySteps)
2026
- } = {}) {
2027
- this.target = target;
2028
- this.config = new PixelEngineConfig(tileSize);
2029
- this.historyManager = historyManager;
2030
- this.accumulator = new PixelAccumulator(target, this.config);
2031
- this.mutator = mutatorFactory(this);
2032
- }
2033
- withHistory(cb) {
2034
- cb(this.mutator);
2035
- const beforeTiles = this.accumulator.beforeTiles;
2036
- if (beforeTiles.length === 0) return;
2037
- const afterTiles = this.accumulator.extractAfterTiles();
2038
- const patch = {
2039
- beforeTiles,
2040
- afterTiles
2041
- };
2042
- const target = this.target;
2043
- const tileSize = this.config.tileSize;
2044
- const accumulator = this.accumulator;
2045
- const action = {
2046
- undo: () => applyPatchTiles(target, patch.beforeTiles, tileSize),
2047
- redo: () => applyPatchTiles(target, patch.afterTiles, tileSize),
2048
- dispose: () => accumulator.recyclePatch(patch)
2049
- };
2050
- this.historyManager.commit(action);
2051
- this.accumulator.reset();
2052
- }
2639
+ // src/History/PixelMutator/mutatorApplyCirclePencilStroke.ts
2640
+ var defaults5 = {
2641
+ forEachLinePoint,
2642
+ blendColorPixelDataBinaryMask,
2643
+ getCircleBrushOrPencilBounds,
2644
+ getCircleBrushOrPencilStrokeBounds
2053
2645
  };
2646
+ var mutatorApplyCirclePencilStroke = ((writer, deps = defaults5) => {
2647
+ const {
2648
+ forEachLinePoint: forEachLinePoint2 = defaults5.forEachLinePoint,
2649
+ blendColorPixelDataBinaryMask: blendColorPixelDataBinaryMask2 = defaults5.blendColorPixelDataBinaryMask,
2650
+ getCircleBrushOrPencilStrokeBounds: getCircleBrushOrPencilStrokeBounds2 = defaults5.getCircleBrushOrPencilStrokeBounds,
2651
+ getCircleBrushOrPencilBounds: getCircleBrushOrPencilBounds2 = defaults5.getCircleBrushOrPencilBounds
2652
+ } = deps;
2653
+ const strokeBoundsOut = {
2654
+ x: 0,
2655
+ y: 0,
2656
+ w: 0,
2657
+ h: 0
2658
+ };
2659
+ const circlePencilBounds = {
2660
+ x: 0,
2661
+ y: 0,
2662
+ w: 0,
2663
+ h: 0
2664
+ };
2665
+ const blendColorPixelOptions = {
2666
+ alpha: 255,
2667
+ blendFn: sourceOverPerfect,
2668
+ x: 0,
2669
+ y: 0,
2670
+ w: 0,
2671
+ h: 0
2672
+ };
2673
+ return {
2674
+ applyCirclePencilStroke(color, x0, y0, x1, y1, brushSize, alpha = 255, blendFn = sourceOverPerfect) {
2675
+ const {
2676
+ x: bx,
2677
+ y: by,
2678
+ w: bw,
2679
+ h: bh
2680
+ } = getCircleBrushOrPencilStrokeBounds2(x0, y0, x1, y1, brushSize, strokeBoundsOut);
2681
+ if (bw <= 0 || bh <= 0) return;
2682
+ const mask = new Uint8Array(bw * bh);
2683
+ const r = brushSize / 2;
2684
+ const rSqr = r * r;
2685
+ const centerOffset = brushSize % 2 === 0 ? 0.5 : 0;
2686
+ const targetWidth = writer.target.width;
2687
+ const targetHeight = writer.target.height;
2688
+ forEachLinePoint2(x0, y0, x1, y1, (px, py) => {
2689
+ const {
2690
+ x: cbx,
2691
+ y: cby,
2692
+ w: cbw,
2693
+ h: cbh
2694
+ } = getCircleBrushOrPencilBounds2(px, py, brushSize, targetWidth, targetHeight, circlePencilBounds);
2695
+ writer.accumulator.storeRegionBeforeState(cbx, cby, cbw, cbh);
2696
+ const startX = Math.max(bx, cbx);
2697
+ const startY = Math.max(by, cby);
2698
+ const endX = Math.min(bx + bw, cbx + cbw);
2699
+ const endY = Math.min(by + bh, cby + cbh);
2700
+ const fPx = Math.floor(px);
2701
+ const fPy = Math.floor(py);
2702
+ for (let my = startY; my < endY; my++) {
2703
+ const dy = my - fPy + centerOffset;
2704
+ const dySqr = dy * dy;
2705
+ const maskRowOffset = (my - by) * bw;
2706
+ for (let mx = startX; mx < endX; mx++) {
2707
+ const dx = mx - fPx + centerOffset;
2708
+ const dSqr = dx * dx + dySqr;
2709
+ if (dSqr <= rSqr) {
2710
+ const maskIdx = maskRowOffset + (mx - bx);
2711
+ mask[maskIdx] = 1;
2712
+ }
2713
+ }
2714
+ }
2715
+ });
2716
+ blendColorPixelOptions.blendFn = blendFn;
2717
+ blendColorPixelOptions.alpha = alpha;
2718
+ blendColorPixelOptions.x = bx;
2719
+ blendColorPixelOptions.y = by;
2720
+ blendColorPixelOptions.w = bw;
2721
+ blendColorPixelOptions.h = bh;
2722
+ blendColorPixelDataBinaryMask2(writer.target, color, mask, blendColorPixelOptions);
2723
+ }
2724
+ };
2725
+ });
2054
2726
 
2055
- // src/History/PixelMutator/mutatorApplyMask.ts
2056
- function mutatorApplyMask(writer) {
2727
+ // src/Rect/getRectBrushOrPencilBounds.ts
2728
+ function getRectBrushOrPencilBounds(centerX, centerY, brushWidth, brushHeight, targetWidth, targetHeight, out) {
2729
+ const startX = Math.floor(centerX - brushWidth / 2);
2730
+ const startY = Math.floor(centerY - brushHeight / 2);
2731
+ const endX = startX + brushWidth;
2732
+ const endY = startY + brushHeight;
2733
+ const res = out ?? {
2734
+ x: 0,
2735
+ y: 0,
2736
+ w: 0,
2737
+ h: 0
2738
+ };
2739
+ const cStartX = Math.max(0, startX);
2740
+ const cStartY = Math.max(0, startY);
2741
+ const cEndX = Math.min(targetWidth, endX);
2742
+ const cEndY = Math.min(targetHeight, endY);
2743
+ const w = cEndX - cStartX;
2744
+ const h = cEndY - cStartY;
2745
+ res.x = cStartX;
2746
+ res.y = cStartY;
2747
+ res.w = w < 0 ? 0 : w;
2748
+ res.h = h < 0 ? 0 : h;
2749
+ return res;
2750
+ }
2751
+
2752
+ // src/PixelData/applyRectBrushToPixelData.ts
2753
+ function applyRectBrushToPixelData(target, color, centerX, centerY, brushWidth, brushHeight, alpha = 255, fallOff, blendFn = sourceOverPerfect, bounds) {
2754
+ const targetWidth = target.width;
2755
+ const targetHeight = target.height;
2756
+ const b = bounds ?? getRectBrushOrPencilBounds(centerX, centerY, brushWidth, brushHeight, targetWidth, targetHeight);
2757
+ if (b.w <= 0 || b.h <= 0) return;
2758
+ const data32 = target.data32;
2759
+ const baseColor = color & 16777215;
2760
+ const baseSrcAlpha = color >>> 24;
2761
+ const isOpaque = alpha === 255;
2762
+ const invHalfW = 1 / (brushWidth / 2);
2763
+ const invHalfH = 1 / (brushHeight / 2);
2764
+ const centerOffsetX = brushWidth % 2 === 0 ? 0.5 : 0;
2765
+ const centerOffsetY = brushHeight % 2 === 0 ? 0.5 : 0;
2766
+ const fCenterX = Math.floor(centerX);
2767
+ const fCenterY = Math.floor(centerY);
2768
+ const endX = b.x + b.w;
2769
+ const endY = b.y + b.h;
2770
+ const isOverwrite = blendFn.isOverwrite;
2771
+ for (let py = b.y; py < endY; py++) {
2772
+ const rowOffset = py * targetWidth;
2773
+ const dy = Math.abs(py - fCenterY + centerOffsetY) * invHalfH;
2774
+ for (let px = b.x; px < endX; px++) {
2775
+ const idx = rowOffset + px;
2776
+ const dx = Math.abs(px - fCenterX + centerOffsetX) * invHalfW;
2777
+ const dist = dx > dy ? dx : dy;
2778
+ const strength = fallOff(dist);
2779
+ const maskVal = strength * 255 | 0;
2780
+ if (maskVal <= 0) continue;
2781
+ let weight = alpha;
2782
+ if (isOpaque) {
2783
+ weight = maskVal;
2784
+ } else if (maskVal !== 255) {
2785
+ weight = maskVal * alpha + 128 >> 8;
2786
+ }
2787
+ let finalCol = color;
2788
+ if (weight < 255) {
2789
+ const a = baseSrcAlpha * weight + 128 >> 8;
2790
+ if (a === 0 && !isOverwrite) continue;
2791
+ finalCol = (a << 24 | baseColor) >>> 0;
2792
+ }
2793
+ data32[idx] = blendFn(finalCol, data32[idx]);
2794
+ }
2795
+ }
2796
+ }
2797
+
2798
+ // src/History/PixelMutator/mutatorApplyRectBrush.ts
2799
+ var defaults6 = {
2800
+ applyRectBrushToPixelData,
2801
+ getRectBrushOrPencilBounds
2802
+ };
2803
+ var mutatorApplyRectBrush = ((writer, deps = defaults6) => {
2804
+ const {
2805
+ applyRectBrushToPixelData: applyRectBrushToPixelData2 = defaults6.applyRectBrushToPixelData,
2806
+ getRectBrushOrPencilBounds: getRectBrushOrPencilBounds2 = defaults6.getRectBrushOrPencilBounds
2807
+ } = deps;
2808
+ const boundsOut = {
2809
+ x: 0,
2810
+ y: 0,
2811
+ w: 0,
2812
+ h: 0
2813
+ };
2057
2814
  return {
2058
- applyMask: (mask, opts = {}) => {
2059
- let target = writer.target;
2815
+ applyRectBrush(color, centerX, centerY, brushWidth, brushHeight, alpha = 255, fallOff, blendFn) {
2816
+ const bounds = getRectBrushOrPencilBounds2(centerX, centerY, brushWidth, brushHeight, writer.target.width, writer.target.height, boundsOut);
2060
2817
  const {
2061
- x = 0,
2062
- y = 0,
2063
- w = writer.target.width,
2064
- h = writer.target.height
2065
- } = opts;
2818
+ x,
2819
+ y,
2820
+ w,
2821
+ h
2822
+ } = bounds;
2066
2823
  writer.accumulator.storeRegionBeforeState(x, y, w, h);
2067
- applyMaskToPixelData(target, mask, opts);
2824
+ applyRectBrushToPixelData2(writer.target, color, centerX, centerY, brushWidth, brushHeight, alpha, fallOff, blendFn, bounds);
2068
2825
  }
2069
2826
  };
2827
+ });
2828
+
2829
+ // src/Rect/getRectBrushOrPencilStrokeBounds.ts
2830
+ function getRectBrushOrPencilStrokeBounds(x0, y0, x1, y1, brushWidth, brushHeight, result) {
2831
+ const halfW = brushWidth / 2;
2832
+ const halfH = brushHeight / 2;
2833
+ const minX = Math.min(x0, x1) - halfW;
2834
+ const minY = Math.min(y0, y1) - halfH;
2835
+ const maxX = Math.max(x0, x1) + halfW;
2836
+ const maxY = Math.max(y0, y1) + halfH;
2837
+ result.x = Math.floor(minX);
2838
+ result.y = Math.floor(minY);
2839
+ result.w = Math.ceil(maxX - minX);
2840
+ result.h = Math.ceil(maxY - minY);
2841
+ return result;
2070
2842
  }
2071
2843
 
2844
+ // src/History/PixelMutator/mutatorApplyRectBrushStroke.ts
2845
+ var defaults7 = {
2846
+ forEachLinePoint,
2847
+ blendColorPixelDataAlphaMask,
2848
+ getRectBrushOrPencilBounds,
2849
+ getRectBrushOrPencilStrokeBounds
2850
+ };
2851
+ var mutatorApplyRectBrushStroke = ((writer, deps = defaults7) => {
2852
+ const {
2853
+ forEachLinePoint: forEachLinePoint2 = defaults7.forEachLinePoint,
2854
+ blendColorPixelDataAlphaMask: blendColorPixelDataAlphaMask2 = defaults7.blendColorPixelDataAlphaMask,
2855
+ getRectBrushOrPencilBounds: getRectBrushOrPencilBounds2 = defaults7.getRectBrushOrPencilBounds,
2856
+ getRectBrushOrPencilStrokeBounds: getRectBrushOrPencilStrokeBounds2 = defaults7.getRectBrushOrPencilStrokeBounds
2857
+ } = deps;
2858
+ const strokeBoundsOut = {
2859
+ x: 0,
2860
+ y: 0,
2861
+ w: 0,
2862
+ h: 0
2863
+ };
2864
+ const rectBrushBounds = {
2865
+ x: 0,
2866
+ y: 0,
2867
+ w: 0,
2868
+ h: 0
2869
+ };
2870
+ const blendColorPixelOptions = {
2871
+ alpha: 255,
2872
+ blendFn: sourceOverPerfect,
2873
+ x: 0,
2874
+ y: 0,
2875
+ w: 0,
2876
+ h: 0
2877
+ };
2878
+ return {
2879
+ applyRectBrushStroke(color, x0, y0, x1, y1, brushWidth, brushHeight, alpha = 255, fallOff, blendFn = sourceOverPerfect) {
2880
+ const {
2881
+ x: bx,
2882
+ y: by,
2883
+ w: bw,
2884
+ h: bh
2885
+ } = getRectBrushOrPencilStrokeBounds2(x0, y0, x1, y1, brushWidth, brushHeight, strokeBoundsOut);
2886
+ if (bw <= 0 || bh <= 0) return;
2887
+ const mask = new Uint8Array(bw * bh);
2888
+ const halfW = brushWidth / 2;
2889
+ const halfH = brushHeight / 2;
2890
+ const invHalfW = 1 / halfW;
2891
+ const invHalfH = 1 / halfH;
2892
+ const centerOffsetX = brushWidth % 2 === 0 ? 0.5 : 0;
2893
+ const centerOffsetY = brushHeight % 2 === 0 ? 0.5 : 0;
2894
+ const targetWidth = writer.target.width;
2895
+ const targetHeight = writer.target.height;
2896
+ forEachLinePoint2(x0, y0, x1, y1, (px, py) => {
2897
+ const {
2898
+ x: rbx,
2899
+ y: rby,
2900
+ w: rbw,
2901
+ h: rbh
2902
+ } = getRectBrushOrPencilBounds2(px, py, brushWidth, brushHeight, targetWidth, targetHeight, rectBrushBounds);
2903
+ writer.accumulator.storeRegionBeforeState(rbx, rby, rbw, rbh);
2904
+ const startX = Math.max(bx, rbx);
2905
+ const startY = Math.max(by, rby);
2906
+ const endX = Math.min(bx + bw, rbx + rbw);
2907
+ const endY = Math.min(by + bh, rby + rbh);
2908
+ const fPx = Math.floor(px);
2909
+ const fPy = Math.floor(py);
2910
+ for (let my = startY; my < endY; my++) {
2911
+ const dy = Math.abs(my - fPy + centerOffsetY) * invHalfH;
2912
+ const maskRowOffset = (my - by) * bw;
2913
+ for (let mx = startX; mx < endX; mx++) {
2914
+ const dx = Math.abs(mx - fPx + centerOffsetX) * invHalfW;
2915
+ const maskIdx = maskRowOffset + (mx - bx);
2916
+ const dist = dx > dy ? dx : dy;
2917
+ const strength = fallOff(dist);
2918
+ if (strength > 0) {
2919
+ const intensity = strength * 255 | 0;
2920
+ if (intensity > mask[maskIdx]) {
2921
+ mask[maskIdx] = intensity;
2922
+ }
2923
+ }
2924
+ }
2925
+ }
2926
+ });
2927
+ blendColorPixelOptions.blendFn = blendFn;
2928
+ blendColorPixelOptions.alpha = alpha;
2929
+ blendColorPixelOptions.x = bx;
2930
+ blendColorPixelOptions.y = by;
2931
+ blendColorPixelOptions.w = bw;
2932
+ blendColorPixelOptions.h = bh;
2933
+ blendColorPixelDataAlphaMask2(writer.target, color, mask, blendColorPixelOptions);
2934
+ }
2935
+ };
2936
+ });
2937
+
2938
+ // src/History/PixelMutator/mutatorApplyRectPencil.ts
2939
+ var defaults8 = {
2940
+ applyRectBrushToPixelData,
2941
+ getRectBrushOrPencilBounds,
2942
+ fallOff: () => 1
2943
+ };
2944
+ var mutatorApplyRectPencil = ((writer, deps = defaults8) => {
2945
+ const {
2946
+ applyRectBrushToPixelData: applyRectBrushToPixelData2 = defaults8.applyRectBrushToPixelData,
2947
+ getRectBrushOrPencilBounds: getRectBrushOrPencilBounds2 = defaults8.getRectBrushOrPencilBounds,
2948
+ fallOff = defaults8.fallOff
2949
+ } = deps;
2950
+ const boundsOut = {
2951
+ x: 0,
2952
+ y: 0,
2953
+ w: 0,
2954
+ h: 0
2955
+ };
2956
+ return {
2957
+ applyRectPencil(color, centerX, centerY, brushWidth, brushHeight, alpha = 255, blendFn) {
2958
+ const bounds = getRectBrushOrPencilBounds2(centerX, centerY, brushWidth, brushHeight, writer.target.width, writer.target.height, boundsOut);
2959
+ const {
2960
+ x,
2961
+ y,
2962
+ w,
2963
+ h
2964
+ } = bounds;
2965
+ writer.accumulator.storeRegionBeforeState(x, y, w, h);
2966
+ applyRectBrushToPixelData2(writer.target, color, centerX, centerY, brushWidth, brushHeight, alpha, fallOff, blendFn, bounds);
2967
+ }
2968
+ };
2969
+ });
2970
+
2971
+ // src/History/PixelMutator/mutatorApplyRectPencilStroke.ts
2972
+ var defaults9 = {
2973
+ forEachLinePoint,
2974
+ getRectBrushOrPencilBounds,
2975
+ getRectBrushOrPencilStrokeBounds,
2976
+ blendColorPixelDataBinaryMask
2977
+ };
2978
+ var mutatorApplyRectPencilStroke = ((writer, deps = defaults9) => {
2979
+ const {
2980
+ forEachLinePoint: forEachLinePoint2 = defaults9.forEachLinePoint,
2981
+ blendColorPixelDataBinaryMask: blendColorPixelDataBinaryMask2 = defaults9.blendColorPixelDataBinaryMask,
2982
+ getRectBrushOrPencilBounds: getRectBrushOrPencilBounds2 = defaults9.getRectBrushOrPencilBounds,
2983
+ getRectBrushOrPencilStrokeBounds: getRectBrushOrPencilStrokeBounds2 = defaults9.getRectBrushOrPencilStrokeBounds
2984
+ } = deps;
2985
+ const strokeBoundsOut = {
2986
+ x: 0,
2987
+ y: 0,
2988
+ w: 0,
2989
+ h: 0
2990
+ };
2991
+ const rectPencilBounds = {
2992
+ x: 0,
2993
+ y: 0,
2994
+ w: 0,
2995
+ h: 0
2996
+ };
2997
+ const blendColorPixelOptions = {
2998
+ alpha: 255,
2999
+ blendFn: sourceOverPerfect,
3000
+ x: 0,
3001
+ y: 0,
3002
+ w: 0,
3003
+ h: 0
3004
+ };
3005
+ return {
3006
+ applyRectPencilStroke(color, x0, y0, x1, y1, brushWidth, brushHeight, alpha = 255, blendFn = sourceOverPerfect) {
3007
+ const {
3008
+ x: bx,
3009
+ y: by,
3010
+ w: bw,
3011
+ h: bh
3012
+ } = getRectBrushOrPencilStrokeBounds2(x0, y0, x1, y1, brushWidth, brushHeight, strokeBoundsOut);
3013
+ if (bw <= 0 || bh <= 0) return;
3014
+ const mask = new Uint8Array(bw * bh);
3015
+ const halfW = brushWidth / 2;
3016
+ const halfH = brushHeight / 2;
3017
+ const centerOffset = brushWidth % 2 === 0 ? 0.5 : 0;
3018
+ const targetWidth = writer.target.width;
3019
+ const targetHeight = writer.target.height;
3020
+ forEachLinePoint2(x0, y0, x1, y1, (px, py) => {
3021
+ const {
3022
+ x: rbx,
3023
+ y: rby,
3024
+ w: rbw,
3025
+ h: rbh
3026
+ } = getRectBrushOrPencilBounds2(px, py, brushWidth, brushHeight, targetWidth, targetHeight, rectPencilBounds);
3027
+ writer.accumulator.storeRegionBeforeState(rbx, rby, rbw, rbh);
3028
+ const startX = Math.max(bx, rbx);
3029
+ const startY = Math.max(by, rby);
3030
+ const endX = Math.min(bx + bw, rbx + rbw);
3031
+ const endY = Math.min(by + bh, rby + rbh);
3032
+ const fPx = Math.floor(px);
3033
+ const fPy = Math.floor(py);
3034
+ for (let my = startY; my < endY; my++) {
3035
+ const dy = Math.abs(my - fPy + centerOffset);
3036
+ const maskRowOffset = (my - by) * bw;
3037
+ for (let mx = startX; mx < endX; mx++) {
3038
+ const dx = Math.abs(mx - fPx + centerOffset);
3039
+ const maskIdx = maskRowOffset + (mx - bx);
3040
+ if (dx <= halfW && dy <= halfH) {
3041
+ mask[maskIdx] = 1;
3042
+ }
3043
+ }
3044
+ }
3045
+ });
3046
+ blendColorPixelOptions.blendFn = blendFn;
3047
+ blendColorPixelOptions.alpha = alpha;
3048
+ blendColorPixelOptions.x = bx;
3049
+ blendColorPixelOptions.y = by;
3050
+ blendColorPixelOptions.w = bw;
3051
+ blendColorPixelOptions.h = bh;
3052
+ blendColorPixelDataBinaryMask2(writer.target, color, mask, blendColorPixelOptions);
3053
+ }
3054
+ };
3055
+ });
3056
+
2072
3057
  // src/PixelData/blendColorPixelData.ts
2073
3058
  function blendColorPixelData(dst, color, opts = {}) {
2074
3059
  const {
@@ -2077,22 +3062,13 @@ function blendColorPixelData(dst, color, opts = {}) {
2077
3062
  w: width = dst.width,
2078
3063
  h: height = dst.height,
2079
3064
  alpha: globalAlpha = 255,
2080
- blendFn = sourceOverFast,
2081
- mask,
2082
- maskType = 0 /* ALPHA */,
2083
- mw,
2084
- mx = 0,
2085
- my = 0,
2086
- invertMask = false
3065
+ blendFn = sourceOverPerfect
2087
3066
  } = opts;
2088
3067
  if (globalAlpha === 0) return;
2089
3068
  const baseSrcAlpha = color >>> 24;
2090
- const isOverwrite = blendFn.isOverwrite;
3069
+ const isOverwrite = blendFn.isOverwrite || false;
2091
3070
  if (baseSrcAlpha === 0 && !isOverwrite) return;
2092
- let x = targetX;
2093
- let y = targetY;
2094
- let w = width;
2095
- let h = height;
3071
+ let x = targetX, y = targetY, w = width, h = height;
2096
3072
  if (x < 0) {
2097
3073
  w += x;
2098
3074
  x = 0;
@@ -2104,76 +3080,33 @@ function blendColorPixelData(dst, color, opts = {}) {
2104
3080
  const actualW = Math.min(w, dst.width - x);
2105
3081
  const actualH = Math.min(h, dst.height - y);
2106
3082
  if (actualW <= 0 || actualH <= 0) return;
3083
+ let finalSrcColor = color;
3084
+ if (globalAlpha < 255) {
3085
+ const a = baseSrcAlpha * globalAlpha + 128 >> 8;
3086
+ if (a === 0 && !isOverwrite) return;
3087
+ finalSrcColor = (color & 16777215 | a << 24) >>> 0;
3088
+ }
2107
3089
  const dst32 = dst.data32;
2108
3090
  const dw = dst.width;
2109
- const mPitch = mw ?? width;
2110
- const isAlphaMask = maskType === 0 /* ALPHA */;
2111
- const dx = x - targetX;
2112
- const dy = y - targetY;
2113
- let dIdx = y * dw + x;
2114
- let mIdx = (my + dy) * mPitch + (mx + dx);
2115
- const dStride = dw - actualW;
2116
- const mStride = mPitch - actualW;
3091
+ let dIdx = y * dw + x | 0;
3092
+ const dStride = dw - actualW | 0;
2117
3093
  for (let iy = 0; iy < actualH; iy++) {
2118
3094
  for (let ix = 0; ix < actualW; ix++) {
2119
- let weight = globalAlpha;
2120
- if (mask) {
2121
- const mVal = mask[mIdx];
2122
- if (isAlphaMask) {
2123
- const effectiveM = invertMask ? 255 - mVal : mVal;
2124
- if (effectiveM === 0) {
2125
- dIdx++;
2126
- mIdx++;
2127
- continue;
2128
- }
2129
- if (globalAlpha === 255) {
2130
- weight = effectiveM;
2131
- } else if (effectiveM === 255) {
2132
- weight = globalAlpha;
2133
- } else {
2134
- weight = effectiveM * globalAlpha + 128 >> 8;
2135
- }
2136
- } else {
2137
- const isHit = invertMask ? mVal === 0 : mVal === 1;
2138
- if (!isHit) {
2139
- dIdx++;
2140
- mIdx++;
2141
- continue;
2142
- }
2143
- weight = globalAlpha;
2144
- }
2145
- if (weight === 0) {
2146
- dIdx++;
2147
- mIdx++;
2148
- continue;
2149
- }
2150
- }
2151
- let currentSrcColor = color;
2152
- if (weight < 255) {
2153
- let currentSrcAlpha = baseSrcAlpha;
2154
- if (baseSrcAlpha === 255) {
2155
- currentSrcAlpha = weight;
2156
- } else {
2157
- currentSrcAlpha = baseSrcAlpha * weight + 128 >> 8;
2158
- }
2159
- if (!isOverwrite && currentSrcAlpha === 0) {
2160
- dIdx++;
2161
- mIdx++;
2162
- continue;
2163
- }
2164
- currentSrcColor = (color & 16777215 | currentSrcAlpha << 24) >>> 0;
2165
- }
2166
- dst32[dIdx] = blendFn(currentSrcColor, dst32[dIdx]);
3095
+ dst32[dIdx] = blendFn(finalSrcColor, dst32[dIdx]);
2167
3096
  dIdx++;
2168
- mIdx++;
2169
3097
  }
2170
3098
  dIdx += dStride;
2171
- mIdx += mStride;
2172
3099
  }
2173
3100
  }
2174
3101
 
2175
3102
  // src/History/PixelMutator/mutatorBlendColor.ts
2176
- function mutatorBlendColor(writer) {
3103
+ var defaults10 = {
3104
+ blendColorPixelData
3105
+ };
3106
+ var mutatorBlendColor = ((writer, deps = defaults10) => {
3107
+ const {
3108
+ blendColorPixelData: blendColorPixelData2 = defaults10.blendColorPixelData
3109
+ } = deps;
2177
3110
  return {
2178
3111
  blendColor(color, opts = {}) {
2179
3112
  const {
@@ -2183,10 +3116,10 @@ function mutatorBlendColor(writer) {
2183
3116
  h = writer.target.height
2184
3117
  } = opts;
2185
3118
  writer.accumulator.storeRegionBeforeState(x, y, w, h);
2186
- blendColorPixelData(writer.target, color, opts);
3119
+ blendColorPixelData2(writer.target, color, opts);
2187
3120
  }
2188
3121
  };
2189
- }
3122
+ });
2190
3123
 
2191
3124
  // src/History/PixelMutator/mutatorBlendPixel.ts
2192
3125
  function mutatorBlendPixel(writer) {
@@ -2220,13 +3153,7 @@ function blendPixelData(dst, src, opts) {
2220
3153
  w: width = src.width,
2221
3154
  h: height = src.height,
2222
3155
  alpha: globalAlpha = 255,
2223
- blendFn = sourceOverFast,
2224
- mask,
2225
- maskType = 0 /* ALPHA */,
2226
- mw,
2227
- mx = 0,
2228
- my = 0,
2229
- invertMask = false
3156
+ blendFn = sourceOverPerfect
2230
3157
  } = opts;
2231
3158
  if (globalAlpha === 0) return;
2232
3159
  let x = targetX;
@@ -2264,91 +3191,48 @@ function blendPixelData(dst, src, opts) {
2264
3191
  const src32 = src.data32;
2265
3192
  const dw = dst.width;
2266
3193
  const sw = src.width;
2267
- const mPitch = mw ?? width;
2268
- const isAlphaMask = maskType === 0 /* ALPHA */;
2269
- const dx = x - targetX;
2270
- const dy = y - targetY;
2271
- let dIdx = y * dw + x;
2272
- let sIdx = sy * sw + sx;
2273
- let mIdx = (my + dy) * mPitch + (mx + dx);
2274
- const dStride = dw - actualW;
2275
- const sStride = sw - actualW;
2276
- const mStride = mPitch - actualW;
3194
+ let dIdx = y * dw + x | 0;
3195
+ let sIdx = sy * sw + sx | 0;
3196
+ const dStride = dw - actualW | 0;
3197
+ const sStride = sw - actualW | 0;
3198
+ const isOpaque = globalAlpha === 255;
2277
3199
  const isOverwrite = blendFn.isOverwrite;
2278
3200
  for (let iy = 0; iy < actualH; iy++) {
2279
3201
  for (let ix = 0; ix < actualW; ix++) {
2280
- const baseSrcColor = src32[sIdx];
2281
- const baseSrcAlpha = baseSrcColor >>> 24;
2282
- if (baseSrcAlpha === 0 && !isOverwrite) {
3202
+ const srcCol = src32[sIdx];
3203
+ const srcAlpha = srcCol >>> 24;
3204
+ if (srcAlpha === 0 && !isOverwrite) {
2283
3205
  dIdx++;
2284
3206
  sIdx++;
2285
- mIdx++;
2286
3207
  continue;
2287
3208
  }
2288
- let weight = globalAlpha;
2289
- if (mask) {
2290
- const mVal = mask[mIdx];
2291
- if (isAlphaMask) {
2292
- const effectiveM = invertMask ? 255 - mVal : mVal;
2293
- if (effectiveM === 0) {
2294
- dIdx++;
2295
- sIdx++;
2296
- mIdx++;
2297
- continue;
2298
- }
2299
- if (globalAlpha === 255) {
2300
- weight = effectiveM;
2301
- } else if (effectiveM === 255) {
2302
- weight = globalAlpha;
2303
- } else {
2304
- weight = effectiveM * globalAlpha + 128 >> 8;
2305
- }
2306
- } else {
2307
- const isHit = invertMask ? mVal === 0 : mVal === 1;
2308
- if (!isHit) {
2309
- dIdx++;
2310
- sIdx++;
2311
- mIdx++;
2312
- continue;
2313
- }
2314
- weight = globalAlpha;
2315
- }
2316
- if (weight === 0) {
2317
- dIdx++;
2318
- sIdx++;
2319
- mIdx++;
2320
- continue;
2321
- }
2322
- }
2323
- let currentSrcColor = baseSrcColor;
2324
- if (weight < 255) {
2325
- let currentSrcAlpha = baseSrcAlpha;
2326
- if (baseSrcAlpha === 255) {
2327
- currentSrcAlpha = weight;
2328
- } else {
2329
- currentSrcAlpha = baseSrcAlpha * weight + 128 >> 8;
2330
- }
2331
- if (!isOverwrite && currentSrcAlpha === 0) {
3209
+ let finalCol = srcCol;
3210
+ if (!isOpaque) {
3211
+ const a = srcAlpha * globalAlpha + 128 >> 8;
3212
+ if (a === 0 && !isOverwrite) {
2332
3213
  dIdx++;
2333
3214
  sIdx++;
2334
- mIdx++;
2335
3215
  continue;
2336
3216
  }
2337
- currentSrcColor = (baseSrcColor & 16777215 | currentSrcAlpha << 24) >>> 0;
3217
+ finalCol = (srcCol & 16777215 | a << 24) >>> 0;
2338
3218
  }
2339
- dst32[dIdx] = blendFn(currentSrcColor, dst32[dIdx]);
3219
+ dst32[dIdx] = blendFn(finalCol, dst32[dIdx]);
2340
3220
  dIdx++;
2341
3221
  sIdx++;
2342
- mIdx++;
2343
3222
  }
2344
3223
  dIdx += dStride;
2345
3224
  sIdx += sStride;
2346
- mIdx += mStride;
2347
3225
  }
2348
3226
  }
2349
3227
 
2350
3228
  // src/History/PixelMutator/mutatorBlendPixelData.ts
2351
- function mutatorBlendPixelData(writer) {
3229
+ var defaults11 = {
3230
+ blendPixelData
3231
+ };
3232
+ var mutatorBlendPixelData = ((writer, deps = defaults11) => {
3233
+ const {
3234
+ blendPixelData: blendPixelData2 = defaults11.blendPixelData
3235
+ } = deps;
2352
3236
  return {
2353
3237
  blendPixelData(src, opts) {
2354
3238
  const {
@@ -2358,12 +3242,13 @@ function mutatorBlendPixelData(writer) {
2358
3242
  h = src.height
2359
3243
  } = opts;
2360
3244
  writer.accumulator.storeRegionBeforeState(x, y, w, h);
2361
- blendPixelData(writer.target, src, opts);
3245
+ blendPixelData2(writer.target, src, opts);
2362
3246
  }
2363
3247
  };
2364
- }
3248
+ });
2365
3249
 
2366
3250
  // src/PixelData/fillPixelData.ts
3251
+ var SCRATCH_RECT = makeClippedRect();
2367
3252
  function fillPixelData(dst, color, _x, _y, _w, _h) {
2368
3253
  let x;
2369
3254
  let y;
@@ -2385,34 +3270,57 @@ function fillPixelData(dst, color, _x, _y, _w, _h) {
2385
3270
  w = dst.width;
2386
3271
  h = dst.height;
2387
3272
  }
2388
- if (x < 0) {
2389
- w += x;
2390
- x = 0;
2391
- }
2392
- if (y < 0) {
2393
- h += y;
2394
- y = 0;
2395
- }
2396
- const actualW = Math.min(w, dst.width - x);
2397
- const actualH = Math.min(h, dst.height - y);
2398
- if (actualW <= 0 || actualH <= 0) {
2399
- return;
2400
- }
3273
+ const clip = resolveRectClipping(x, y, w, h, dst.width, dst.height, SCRATCH_RECT);
3274
+ if (!clip.inBounds) return;
3275
+ const {
3276
+ x: finalX,
3277
+ y: finalY,
3278
+ w: actualW,
3279
+ h: actualH
3280
+ } = clip;
2401
3281
  const dst32 = dst.data32;
2402
3282
  const dw = dst.width;
2403
- if (actualW === dw && actualH === dst.height && x === 0 && y === 0) {
3283
+ if (actualW === dw && actualH === dst.height && finalX === 0 && finalY === 0) {
2404
3284
  dst32.fill(color);
2405
3285
  return;
2406
3286
  }
2407
3287
  for (let iy = 0; iy < actualH; iy++) {
2408
- const start = (y + iy) * dw + x;
3288
+ const start = (finalY + iy) * dw + finalX;
2409
3289
  const end = start + actualW;
2410
3290
  dst32.fill(color, start, end);
2411
3291
  }
2412
3292
  }
2413
3293
 
2414
- // src/History/PixelMutator/mutatorFillPixelData.ts
2415
- function mutatorFill(writer) {
3294
+ // src/History/PixelMutator/mutatorClear.ts
3295
+ var defaults12 = {
3296
+ fillPixelData
3297
+ };
3298
+ var mutatorClear = ((writer, deps = defaults12) => {
3299
+ const {
3300
+ fillPixelData: fillPixelData2 = defaults12.fillPixelData
3301
+ } = deps;
3302
+ return {
3303
+ clear(rect = {}) {
3304
+ const {
3305
+ x = 0,
3306
+ y = 0,
3307
+ w = writer.target.width,
3308
+ h = writer.target.height
3309
+ } = rect;
3310
+ writer.accumulator.storeRegionBeforeState(x, y, w, h);
3311
+ fillPixelData2(writer.target, 0, x, y, w, h);
3312
+ }
3313
+ };
3314
+ });
3315
+
3316
+ // src/History/PixelMutator/mutatorFill.ts
3317
+ var defaults13 = {
3318
+ fillPixelData
3319
+ };
3320
+ var mutatorFill = ((writer, deps = defaults13) => {
3321
+ const {
3322
+ fillPixelData: fillPixelData2 = defaults13.fillPixelData
3323
+ } = deps;
2416
3324
  return {
2417
3325
  fill(color, rect = {}) {
2418
3326
  const {
@@ -2422,12 +3330,13 @@ function mutatorFill(writer) {
2422
3330
  h = writer.target.height
2423
3331
  } = rect;
2424
3332
  writer.accumulator.storeRegionBeforeState(x, y, w, h);
2425
- fillPixelData(writer.target, color, x, y, w, h);
3333
+ fillPixelData2(writer.target, color, x, y, w, h);
2426
3334
  }
2427
3335
  };
2428
- }
3336
+ });
2429
3337
 
2430
3338
  // src/PixelData/invertPixelData.ts
3339
+ var SCRATCH_RECT2 = makeClippedRect();
2431
3340
  function invertPixelData(pixelData, opts = {}) {
2432
3341
  const dst = pixelData;
2433
3342
  const {
@@ -2441,21 +3350,14 @@ function invertPixelData(pixelData, opts = {}) {
2441
3350
  my = 0,
2442
3351
  invertMask = false
2443
3352
  } = opts;
2444
- let x = targetX;
2445
- let y = targetY;
2446
- let w = width;
2447
- let h = height;
2448
- if (x < 0) {
2449
- w += x;
2450
- x = 0;
2451
- }
2452
- if (y < 0) {
2453
- h += y;
2454
- y = 0;
2455
- }
2456
- const actualW = Math.min(w, dst.width - x);
2457
- const actualH = Math.min(h, dst.height - y);
2458
- if (actualW <= 0 || actualH <= 0) return;
3353
+ const clip = resolveRectClipping(targetX, targetY, width, height, dst.width, dst.height, SCRATCH_RECT2);
3354
+ if (!clip.inBounds) return;
3355
+ const {
3356
+ x,
3357
+ y,
3358
+ w: actualW,
3359
+ h: actualH
3360
+ } = clip;
2459
3361
  const dst32 = dst.data32;
2460
3362
  const dw = dst.width;
2461
3363
  const mPitch = mw ?? width;
@@ -2491,7 +3393,13 @@ function invertPixelData(pixelData, opts = {}) {
2491
3393
  }
2492
3394
 
2493
3395
  // src/History/PixelMutator/mutatorInvert.ts
2494
- function mutatorInvert(writer) {
3396
+ var defaults14 = {
3397
+ invertPixelData
3398
+ };
3399
+ var mutatorInvert = ((writer, deps = defaults14) => {
3400
+ const {
3401
+ invertPixelData: invertPixelData2 = defaults14.invertPixelData
3402
+ } = deps;
2495
3403
  return {
2496
3404
  invert(opts = {}) {
2497
3405
  const {
@@ -2501,44 +3409,121 @@ function mutatorInvert(writer) {
2501
3409
  h = writer.target.height
2502
3410
  } = opts;
2503
3411
  writer.accumulator.storeRegionBeforeState(x, y, w, h);
2504
- invertPixelData(writer.target, opts);
3412
+ invertPixelData2(writer.target, opts);
2505
3413
  }
2506
3414
  };
2507
- }
3415
+ });
2508
3416
 
2509
3417
  // src/History/PixelMutator.ts
2510
3418
  function makeFullPixelMutator(writer) {
2511
3419
  return {
2512
- ...mutatorApplyMask(writer),
3420
+ ...mutatorApplyAlphaMask(writer),
3421
+ ...mutatorApplyBinaryMask(writer),
2513
3422
  ...mutatorBlendPixelData(writer),
2514
3423
  ...mutatorBlendColor(writer),
2515
3424
  ...mutatorBlendPixel(writer),
2516
3425
  ...mutatorFill(writer),
2517
- ...mutatorInvert(writer)
3426
+ ...mutatorInvert(writer),
3427
+ ...mutatorApplyCircleBrush(writer),
3428
+ ...mutatorApplyCircleBrushStroke(writer),
3429
+ ...mutatorApplyCirclePencilStroke(writer),
3430
+ ...mutatorApplyRectBrush(writer),
3431
+ ...mutatorApplyRectBrushStroke(writer),
3432
+ ...mutatorApplyRectPencil(writer),
3433
+ ...mutatorApplyRectPencilStroke(writer),
3434
+ ...mutatorClear(writer)
2518
3435
  };
2519
3436
  }
2520
3437
 
2521
- // src/ImageData/ReusableImageData.ts
2522
- function makeReusableImageData() {
2523
- let imageData = null;
2524
- let buffer = null;
2525
- return function getReusableImageData(width, height) {
2526
- const hasInstance = !!imageData;
2527
- const widthMatches = hasInstance && imageData.width === width;
2528
- const heightMatches = hasInstance && imageData.height === height;
2529
- if (!widthMatches || !heightMatches) {
2530
- const buffer2 = new Uint8ClampedArray(width * height * 4);
2531
- imageData = new ImageData(buffer2, width, height);
3438
+ // src/History/PixelWriter.ts
3439
+ var PixelWriter = class {
3440
+ target;
3441
+ historyManager;
3442
+ accumulator;
3443
+ config;
3444
+ mutator;
3445
+ constructor(target, mutatorFactory, {
3446
+ tileSize = 256,
3447
+ maxHistorySteps = 50,
3448
+ historyManager = new HistoryManager(maxHistorySteps)
3449
+ } = {}) {
3450
+ this.target = target;
3451
+ this.config = new PixelEngineConfig(tileSize);
3452
+ this.historyManager = historyManager;
3453
+ this.accumulator = new PixelAccumulator(target, this.config);
3454
+ this.mutator = mutatorFactory(this);
3455
+ }
3456
+ withHistory(cb) {
3457
+ cb(this.mutator);
3458
+ this.captureHistory();
3459
+ }
3460
+ captureHistory() {
3461
+ const beforeTiles = this.accumulator.beforeTiles;
3462
+ if (beforeTiles.length === 0) return;
3463
+ const afterTiles = this.accumulator.extractAfterTiles();
3464
+ const patch = {
3465
+ beforeTiles,
3466
+ afterTiles
3467
+ };
3468
+ const target = this.target;
3469
+ const tileSize = this.config.tileSize;
3470
+ const accumulator = this.accumulator;
3471
+ const action = {
3472
+ undo: () => applyPatchTiles(target, patch.beforeTiles, tileSize),
3473
+ redo: () => applyPatchTiles(target, patch.afterTiles, tileSize),
3474
+ dispose: () => accumulator.recyclePatch(patch)
3475
+ };
3476
+ this.historyManager.commit(action);
3477
+ this.accumulator.reset();
3478
+ }
3479
+ };
3480
+
3481
+ // src/History/PixelMutator/mutatorApplyCirclePencil.ts
3482
+ var defaults15 = {
3483
+ applyCircleBrushToPixelData,
3484
+ getCircleBrushOrPencilBounds,
3485
+ fallOff: () => 1
3486
+ };
3487
+ var mutatorApplyCirclePencil = ((writer, deps = defaults15) => {
3488
+ const {
3489
+ applyCircleBrushToPixelData: applyCircleBrushToPixelData2 = defaults15.applyCircleBrushToPixelData,
3490
+ getCircleBrushOrPencilBounds: getCircleBrushOrPencilBounds2 = defaults15.getCircleBrushOrPencilBounds,
3491
+ fallOff = defaults15.fallOff
3492
+ } = deps;
3493
+ const boundsOut = {
3494
+ x: 0,
3495
+ y: 0,
3496
+ w: 0,
3497
+ h: 0
3498
+ };
3499
+ return {
3500
+ applyCirclePencil(color, centerX, centerY, brushSize, alpha = 255, blendFn) {
3501
+ const bounds = getCircleBrushOrPencilBounds2(centerX, centerY, brushSize, writer.target.width, writer.target.height, boundsOut);
3502
+ const {
3503
+ x,
3504
+ y,
3505
+ w,
3506
+ h
3507
+ } = bounds;
3508
+ writer.accumulator.storeRegionBeforeState(x, y, w, h);
3509
+ applyCircleBrushToPixelData2(writer.target, color, centerX, centerY, brushSize, alpha, fallOff, blendFn, bounds);
2532
3510
  }
2533
- return imageData;
2534
3511
  };
2535
- }
3512
+ });
2536
3513
 
2537
3514
  // src/ImageData/copyImageData.ts
2538
- function copyImageData({ data, width, height }) {
3515
+ function copyImageData({
3516
+ data,
3517
+ width,
3518
+ height
3519
+ }) {
2539
3520
  return new ImageData(data.slice(), width, height);
2540
3521
  }
2541
- function copyImageDataLike({ data, width, height }) {
3522
+ function copyImageDataLike({
3523
+ data,
3524
+ width,
3525
+ height
3526
+ }) {
2542
3527
  return {
2543
3528
  data: data.slice(),
2544
3529
  width,
@@ -2546,20 +3531,15 @@ function copyImageDataLike({ data, width, height }) {
2546
3531
  };
2547
3532
  }
2548
3533
 
2549
- // src/PixelData/pixelDataToAlphaMask.ts
2550
- function pixelDataToAlphaMask(pixelData) {
2551
- const {
2552
- data32,
3534
+ // src/ImageData/ImageDataLike.ts
3535
+ function makeImageDataLike(width, height, data) {
3536
+ const size = width * height * 4;
3537
+ const buffer = data ? new Uint8ClampedArray(data.buffer, data.byteOffset, size) : new Uint8ClampedArray(size);
3538
+ return {
2553
3539
  width,
2554
- height
2555
- } = pixelData;
2556
- const len = data32.length;
2557
- const mask = new Uint8Array(width * height);
2558
- for (let i = 0; i < len; i++) {
2559
- const val = data32[i];
2560
- mask[i] = val >>> 24 & 255;
2561
- }
2562
- return mask;
3540
+ height,
3541
+ data: buffer
3542
+ };
2563
3543
  }
2564
3544
 
2565
3545
  // src/ImageData/imageDataToAlphaMask.ts
@@ -2569,11 +3549,7 @@ function imageDataToAlphaMask(imageData) {
2569
3549
  height,
2570
3550
  data
2571
3551
  } = imageData;
2572
- const data32 = new Uint32Array(
2573
- data.buffer,
2574
- data.byteOffset,
2575
- data.byteLength >> 2
2576
- );
3552
+ const data32 = new Uint32Array(data.buffer, data.byteOffset, data.byteLength >> 2);
2577
3553
  const len = data32.length;
2578
3554
  const mask = new Uint8Array(width * height);
2579
3555
  for (let i = 0; i < len; i++) {
@@ -2586,7 +3562,10 @@ function imageDataToAlphaMask(imageData) {
2586
3562
  // src/ImageData/imageDataToDataUrl.ts
2587
3563
  var get = makeReusableCanvas();
2588
3564
  function imageDataToDataUrl(imageData) {
2589
- const { canvas, ctx } = get(imageData.width, imageData.height);
3565
+ const {
3566
+ canvas,
3567
+ ctx
3568
+ } = get(imageData.width, imageData.height);
2590
3569
  ctx.putImageData(imageData, 0, 0);
2591
3570
  return canvas.toDataURL();
2592
3571
  }
@@ -2644,7 +3623,11 @@ function resample32(srcData32, srcW, srcH, factor) {
2644
3623
  // src/ImageData/resampleImageData.ts
2645
3624
  function resampleImageData(source, factor) {
2646
3625
  const src32 = new Uint32Array(source.data.buffer);
2647
- const { data, width, height } = resample32(src32, source.width, source.height, factor);
3626
+ const {
3627
+ data,
3628
+ width,
3629
+ height
3630
+ } = resample32(src32, source.width, source.height, factor);
2648
3631
  const uint8ClampedArray = new Uint8ClampedArray(data.buffer);
2649
3632
  return new ImageData(uint8ClampedArray, width, height);
2650
3633
  }
@@ -2673,14 +3656,25 @@ function resizeImageData(current, newWidth, newHeight, offsetX = 0, offsetY = 0)
2673
3656
  const srcX = x0 - offsetX;
2674
3657
  const dstStart = (dstY * newWidth + x0) * 4;
2675
3658
  const srcStart = (srcY * oldW + srcX) * 4;
2676
- newData.set(
2677
- oldData.subarray(srcStart, srcStart + rowLen),
2678
- dstStart
2679
- );
3659
+ newData.set(oldData.subarray(srcStart, srcStart + rowLen), dstStart);
2680
3660
  }
2681
3661
  return result;
2682
3662
  }
2683
3663
 
3664
+ // src/ImageData/ReusableImageData.ts
3665
+ function makeReusableImageData() {
3666
+ let imageData = null;
3667
+ return function getReusableImageData(width, height) {
3668
+ const hasInstance = !!imageData;
3669
+ const widthMatches = hasInstance && imageData.width === width;
3670
+ const heightMatches = hasInstance && imageData.height === height;
3671
+ if (!widthMatches || !heightMatches) {
3672
+ imageData = new ImageData(width, height);
3673
+ }
3674
+ return imageData;
3675
+ };
3676
+ }
3677
+
2684
3678
  // src/ImageData/serialization.ts
2685
3679
  function base64EncodeArrayBuffer(buffer) {
2686
3680
  const uint8 = new Uint8Array(buffer);
@@ -2723,32 +3717,53 @@ function deserializeNullableImageData(serialized) {
2723
3717
  return deserializeImageData(serialized);
2724
3718
  }
2725
3719
 
3720
+ // src/ImageData/uInt32ArrayToImageData.ts
3721
+ function uInt32ArrayToImageData(data, width, height) {
3722
+ const buffer = data.buffer;
3723
+ const byteOffset = data.byteOffset;
3724
+ const byteLength = data.byteLength;
3725
+ const clampedArray = new Uint8ClampedArray(buffer, byteOffset, byteLength);
3726
+ return new ImageData(clampedArray, width, height);
3727
+ }
3728
+ function uInt32ArrayToImageDataLike(data, width, height) {
3729
+ const buffer = data.buffer;
3730
+ const byteOffset = data.byteOffset;
3731
+ const byteLength = data.byteLength;
3732
+ const clampedArray = new Uint8ClampedArray(buffer, byteOffset, byteLength);
3733
+ return {
3734
+ width,
3735
+ height,
3736
+ data: clampedArray
3737
+ };
3738
+ }
3739
+
2726
3740
  // src/ImageData/writeImageData.ts
3741
+ var SCRATCH_BLIT2 = makeClippedBlit();
2727
3742
  function writeImageData(target, source, x, y, sx = 0, sy = 0, sw = source.width, sh = source.height, mask = null, maskType = 1 /* BINARY */) {
2728
3743
  const dstW = target.width;
2729
3744
  const dstH = target.height;
2730
3745
  const dstData = target.data;
2731
3746
  const srcW = source.width;
2732
3747
  const srcData = source.data;
2733
- const x0 = Math.max(0, x);
2734
- const y0 = Math.max(0, y);
2735
- const x1 = Math.min(dstW, x + sw);
2736
- const y1 = Math.min(dstH, y + sh);
2737
- if (x1 <= x0 || y1 <= y0) {
2738
- return;
2739
- }
3748
+ const clip = resolveBlitClipping(x, y, sx, sy, sw, sh, dstW, dstH, srcW, source.height, SCRATCH_BLIT2);
3749
+ if (!clip.inBounds) return;
3750
+ const {
3751
+ x: dstX,
3752
+ y: dstY,
3753
+ sx: srcX,
3754
+ sy: srcY,
3755
+ w: copyW,
3756
+ h: copyH
3757
+ } = clip;
2740
3758
  const useMask = !!mask;
2741
- const rowCount = y1 - y0;
2742
- const rowLenPixels = x1 - x0;
2743
- for (let row = 0; row < rowCount; row++) {
2744
- const dstY = y0 + row;
2745
- const srcY = sy + (dstY - y);
2746
- const srcXBase = sx + (x0 - x);
2747
- const dstStart = (dstY * dstW + x0) * 4;
2748
- const srcStart = (srcY * srcW + srcXBase) * 4;
3759
+ for (let row = 0; row < copyH; row++) {
3760
+ const currentDstY = dstY + row;
3761
+ const currentSrcY = srcY + row;
3762
+ const dstStart = (currentDstY * dstW + dstX) * 4;
3763
+ const srcStart = (currentSrcY * srcW + srcX) * 4;
2749
3764
  if (useMask && mask) {
2750
- for (let ix = 0; ix < rowLenPixels; ix++) {
2751
- const mi = srcY * srcW + (srcXBase + ix);
3765
+ for (let ix = 0; ix < copyW; ix++) {
3766
+ const mi = currentSrcY * srcW + (srcX + ix);
2752
3767
  const alpha = mask[mi];
2753
3768
  if (alpha === 0) {
2754
3769
  continue;
@@ -2770,7 +3785,7 @@ function writeImageData(target, source, x, y, sx = 0, sy = 0, sw = source.width,
2770
3785
  }
2771
3786
  }
2772
3787
  } else {
2773
- const byteLen = rowLenPixels * 4;
3788
+ const byteLen = copyW * 4;
2774
3789
  const sub = srcData.subarray(srcStart, srcStart + byteLen);
2775
3790
  dstData.set(sub, dstStart);
2776
3791
  }
@@ -2778,24 +3793,52 @@ function writeImageData(target, source, x, y, sx = 0, sy = 0, sw = source.width,
2778
3793
  }
2779
3794
 
2780
3795
  // src/ImageData/writeImageDataBuffer.ts
3796
+ var SCRATCH_BLIT3 = makeClippedBlit();
2781
3797
  function writeImageDataBuffer(imageData, data, _x, _y, _w, _h) {
2782
- const { x, y, w, h } = typeof _x === "object" ? _x : { x: _x, y: _y, w: _w, h: _h };
2783
- const { width: dstW, height: dstH, data: dst } = imageData;
2784
- const x0 = Math.max(0, x);
2785
- const y0 = Math.max(0, y);
2786
- const x1 = Math.min(dstW, x + w);
2787
- const y1 = Math.min(dstH, y + h);
2788
- if (x1 <= x0 || y1 <= y0) return;
2789
- const rowLen = (x1 - x0) * 4;
2790
- const srcCol = x0 - x;
2791
- const srcYOffset = y0 - y;
2792
- const actualH = y1 - y0;
2793
- for (let row = 0; row < actualH; row++) {
2794
- const dstStart = ((y0 + row) * dstW + x0) * 4;
2795
- const srcRow = srcYOffset + row;
2796
- const o = (srcRow * w + srcCol) * 4;
2797
- dst.set(data.subarray(o, o + rowLen), dstStart);
3798
+ const {
3799
+ x,
3800
+ y,
3801
+ w,
3802
+ h
3803
+ } = typeof _x === "object" ? _x : {
3804
+ x: _x,
3805
+ y: _y,
3806
+ w: _w,
3807
+ h: _h
3808
+ };
3809
+ const {
3810
+ width: dstW,
3811
+ height: dstH,
3812
+ data: dst
3813
+ } = imageData;
3814
+ const clip = resolveBlitClipping(x, y, 0, 0, w, h, dstW, dstH, w, h, SCRATCH_BLIT3);
3815
+ if (!clip.inBounds) return;
3816
+ const {
3817
+ x: dstX,
3818
+ y: dstY,
3819
+ sx: srcX,
3820
+ sy: srcY,
3821
+ w: copyW,
3822
+ h: copyH
3823
+ } = clip;
3824
+ const rowLen = copyW * 4;
3825
+ for (let row = 0; row < copyH; row++) {
3826
+ const dstStart = ((dstY + row) * dstW + dstX) * 4;
3827
+ const srcStart = ((srcY + row) * w + srcX) * 4;
3828
+ dst.set(data.subarray(srcStart, srcStart + rowLen), dstStart);
3829
+ }
3830
+ }
3831
+
3832
+ // src/IndexedImage/getIndexedImageColorCounts.ts
3833
+ function getIndexedImageColorCounts(indexedImage) {
3834
+ const data = indexedImage.data;
3835
+ const palette = indexedImage.palette;
3836
+ const frequencies = new Int32Array(palette.length);
3837
+ for (let i = 0; i < data.length; i++) {
3838
+ const colorIndex = data[i];
3839
+ frequencies[colorIndex]++;
2798
3840
  }
3841
+ return frequencies;
2799
3842
  }
2800
3843
 
2801
3844
  // src/IndexedImage/IndexedImage.ts
@@ -2861,13 +3904,7 @@ var IndexedImage = class _IndexedImage {
2861
3904
  indexedData[i] = id;
2862
3905
  }
2863
3906
  const palette = Uint32Array.from(colorMap.keys());
2864
- return new _IndexedImage(
2865
- width,
2866
- height,
2867
- indexedData,
2868
- palette,
2869
- transparentPalletIndex
2870
- );
3907
+ return new _IndexedImage(width, height, indexedData, palette, transparentPalletIndex);
2871
3908
  }
2872
3909
  /**
2873
3910
  * Retrieves the 32-bit packed color value at the given coordinates.
@@ -2882,21 +3919,13 @@ var IndexedImage = class _IndexedImage {
2882
3919
  }
2883
3920
  };
2884
3921
 
2885
- // src/IndexedImage/getIndexedImageColorCounts.ts
2886
- function getIndexedImageColorCounts(indexedImage) {
2887
- const data = indexedImage.data;
2888
- const palette = indexedImage.palette;
2889
- const frequencies = new Int32Array(palette.length);
2890
- for (let i = 0; i < data.length; i++) {
2891
- const colorIndex = data[i];
2892
- frequencies[colorIndex]++;
2893
- }
2894
- return frequencies;
2895
- }
2896
-
2897
3922
  // src/IndexedImage/indexedImageToAverageColor.ts
2898
3923
  function indexedImageToAverageColor(indexedImage, includeTransparent = false) {
2899
- const { data, palette, transparentPalletIndex } = indexedImage;
3924
+ const {
3925
+ data,
3926
+ palette,
3927
+ transparentPalletIndex
3928
+ } = indexedImage;
2900
3929
  const counts = new Uint32Array(palette.length);
2901
3930
  for (let i = 0; i < data.length; i++) {
2902
3931
  const id = data[i];
@@ -2936,26 +3965,14 @@ function indexedImageToAverageColor(indexedImage, includeTransparent = false) {
2936
3965
  return packColor(r, g, b, a);
2937
3966
  }
2938
3967
 
2939
- // src/IndexedImage/resampleIndexedImage.ts
2940
- function resampleIndexedImage(source, factor) {
2941
- const { data, width, height } = resample32(
2942
- source.data,
2943
- source.width,
2944
- source.height,
2945
- factor
2946
- );
2947
- return new IndexedImage(
3968
+ // src/IndexedImage/indexedImageToImageData.ts
3969
+ function indexedImageToImageData(indexedImage) {
3970
+ const {
2948
3971
  width,
2949
3972
  height,
2950
3973
  data,
2951
- source.palette,
2952
- source.transparentPalletIndex
2953
- );
2954
- }
2955
-
2956
- // src/IndexedImage/indexedImageToImageData.ts
2957
- function indexedImageToImageData(indexedImage) {
2958
- const { width, height, data, palette } = indexedImage;
3974
+ palette
3975
+ } = indexedImage;
2959
3976
  const result = new ImageData(width, height);
2960
3977
  const data32 = new Uint32Array(result.data.buffer);
2961
3978
  for (let i = 0; i < data.length; i++) {
@@ -2966,6 +3983,16 @@ function indexedImageToImageData(indexedImage) {
2966
3983
  return result;
2967
3984
  }
2968
3985
 
3986
+ // src/IndexedImage/resampleIndexedImage.ts
3987
+ function resampleIndexedImage(source, factor) {
3988
+ const {
3989
+ data,
3990
+ width,
3991
+ height
3992
+ } = resample32(source.data, source.width, source.height, factor);
3993
+ return new IndexedImage(width, height, data, source.palette, source.transparentPalletIndex);
3994
+ }
3995
+
2969
3996
  // src/Input/fileInputChangeToImageData.ts
2970
3997
  async function fileInputChangeToImageData(event) {
2971
3998
  const target = event.target;
@@ -2989,23 +4016,11 @@ async function fileToImageData(file) {
2989
4016
  let bitmap = null;
2990
4017
  try {
2991
4018
  bitmap = await createImageBitmap(file);
2992
- const canvas = new OffscreenCanvas(
2993
- bitmap.width,
2994
- bitmap.height
2995
- );
4019
+ const canvas = new OffscreenCanvas(bitmap.width, bitmap.height);
2996
4020
  const ctx = canvas.getContext("2d");
2997
4021
  if (!ctx) throw new Error(OFFSCREEN_CANVAS_CTX_FAILED);
2998
- ctx.drawImage(
2999
- bitmap,
3000
- 0,
3001
- 0
3002
- );
3003
- return ctx.getImageData(
3004
- 0,
3005
- 0,
3006
- bitmap.width,
3007
- bitmap.height
3008
- );
4022
+ ctx.drawImage(bitmap, 0, 0);
4023
+ return ctx.getImageData(0, 0, bitmap.width, bitmap.height);
3009
4024
  } finally {
3010
4025
  bitmap?.close();
3011
4026
  }
@@ -3013,32 +4028,23 @@ async function fileToImageData(file) {
3013
4028
 
3014
4029
  // src/Input/getSupportedRasterFormats.ts
3015
4030
  var formatsPromise = null;
3016
- var defaultRasterMimes = [
3017
- "image/png",
3018
- "image/jpeg",
3019
- "image/webp",
3020
- "image/avif",
3021
- "image/gif",
3022
- "image/bmp"
3023
- ];
4031
+ var defaultRasterMimes = ["image/png", "image/jpeg", "image/webp", "image/avif", "image/gif", "image/bmp"];
3024
4032
  async function getSupportedPixelFormats(rasterMimes = defaultRasterMimes) {
3025
4033
  if (formatsPromise) {
3026
4034
  return formatsPromise;
3027
4035
  }
3028
4036
  const probeCanvas = async () => {
3029
4037
  const canvas = new OffscreenCanvas(1, 1);
3030
- const results = await Promise.all(
3031
- rasterMimes.map(async (mime) => {
3032
- try {
3033
- const blob = await canvas.convertToBlob({
3034
- type: mime
3035
- });
3036
- return blob.type === mime ? mime : null;
3037
- } catch {
3038
- return null;
3039
- }
3040
- })
3041
- );
4038
+ const results = await Promise.all(rasterMimes.map(async (mime) => {
4039
+ try {
4040
+ const blob = await canvas.convertToBlob({
4041
+ type: mime
4042
+ });
4043
+ return blob.type === mime ? mime : null;
4044
+ } catch {
4045
+ return null;
4046
+ }
4047
+ }));
3042
4048
  return results.filter((type) => {
3043
4049
  return type !== null;
3044
4050
  });
@@ -3050,6 +4056,73 @@ async function getSupportedPixelFormats(rasterMimes = defaultRasterMimes) {
3050
4056
  return formatsPromise;
3051
4057
  }
3052
4058
 
4059
+ // src/Mask/applyBinaryMaskToAlphaMask.ts
4060
+ function applyBinaryMaskToAlphaMask(alphaMaskDst, dstWidth, binaryMaskSrc, srcWidth, opts = {}) {
4061
+ const {
4062
+ x: targetX = 0,
4063
+ y: targetY = 0,
4064
+ w: reqWidth = 0,
4065
+ h: reqHeight = 0,
4066
+ mx = 0,
4067
+ my = 0,
4068
+ invertMask = false
4069
+ } = opts;
4070
+ if (dstWidth <= 0) return;
4071
+ if (binaryMaskSrc.length === 0) return;
4072
+ if (srcWidth <= 0) return;
4073
+ const dstHeight = alphaMaskDst.length / dstWidth | 0;
4074
+ const srcHeight = binaryMaskSrc.length / srcWidth | 0;
4075
+ if (dstHeight <= 0) return;
4076
+ if (srcHeight <= 0) return;
4077
+ const dstX0 = Math.max(0, targetX);
4078
+ const dstY0 = Math.max(0, targetY);
4079
+ const dstX1 = reqWidth > 0 ? Math.min(dstWidth, targetX + reqWidth) : dstWidth;
4080
+ const dstY1 = reqHeight > 0 ? Math.min(dstHeight, targetY + reqHeight) : dstHeight;
4081
+ if (dstX0 >= dstX1) return;
4082
+ if (dstY0 >= dstY1) return;
4083
+ const srcX0 = mx + (dstX0 - targetX);
4084
+ const srcY0 = my + (dstY0 - targetY);
4085
+ if (srcX0 >= srcWidth) return;
4086
+ if (srcY0 >= srcHeight) return;
4087
+ if (srcX0 + (dstX1 - dstX0) <= 0) return;
4088
+ if (srcY0 + (dstY1 - dstY0) <= 0) return;
4089
+ const iterW = Math.min(dstX1 - dstX0, srcWidth - srcX0);
4090
+ const iterH = Math.min(dstY1 - dstY0, srcHeight - srcY0);
4091
+ let dstIdx = dstY0 * dstWidth + dstX0;
4092
+ let srcIdx = srcY0 * srcWidth + srcX0;
4093
+ if (invertMask) {
4094
+ for (let row = 0; row < iterH; row++) {
4095
+ const dstEnd = dstIdx + iterW;
4096
+ let d = dstIdx;
4097
+ let s = srcIdx;
4098
+ while (d < dstEnd) {
4099
+ if (binaryMaskSrc[s] !== 0) {
4100
+ alphaMaskDst[d] = 0;
4101
+ }
4102
+ d++;
4103
+ s++;
4104
+ }
4105
+ dstIdx += dstWidth;
4106
+ srcIdx += srcWidth;
4107
+ }
4108
+ } else {
4109
+ for (let row = 0; row < iterH; row++) {
4110
+ const dstEnd = dstIdx + iterW;
4111
+ let d = dstIdx;
4112
+ let s = srcIdx;
4113
+ while (d < dstEnd) {
4114
+ if (binaryMaskSrc[s] === 0) {
4115
+ alphaMaskDst[d] = 0;
4116
+ }
4117
+ d++;
4118
+ s++;
4119
+ }
4120
+ dstIdx += dstWidth;
4121
+ srcIdx += srcWidth;
4122
+ }
4123
+ }
4124
+ }
4125
+
3053
4126
  // src/Mask/copyMask.ts
3054
4127
  function copyMask(src) {
3055
4128
  return src.slice();
@@ -3069,69 +4142,124 @@ function invertAlphaMask(dst) {
3069
4142
  }
3070
4143
  }
3071
4144
 
3072
- // src/Mask/mergeMasks.ts
3073
- function mergeMasks(dst, dstWidth, src, opts) {
4145
+ // src/Mask/mergeAlphaMasks.ts
4146
+ function mergeAlphaMasks(dst, dstWidth, src, srcWidth, opts) {
3074
4147
  const {
3075
4148
  x: targetX = 0,
3076
4149
  y: targetY = 0,
3077
4150
  w: width = 0,
3078
4151
  h: height = 0,
3079
4152
  alpha: globalAlpha = 255,
3080
- maskType = 0 /* ALPHA */,
3081
- mw,
3082
4153
  mx = 0,
3083
4154
  my = 0,
3084
4155
  invertMask = false
3085
4156
  } = opts;
3086
- if (width <= 0 || height <= 0 || globalAlpha === 0) {
3087
- return;
4157
+ const dstHeight = dst.length / dstWidth | 0;
4158
+ const srcHeight = src.length / srcWidth | 0;
4159
+ if (width <= 0) return;
4160
+ if (height <= 0) return;
4161
+ if (globalAlpha === 0) return;
4162
+ const startX = Math.max(0, -targetX, -mx);
4163
+ const startY = Math.max(0, -targetY, -my);
4164
+ const endX = Math.min(width, dstWidth - targetX, srcWidth - mx);
4165
+ const endY = Math.min(height, dstHeight - targetY, srcHeight - my);
4166
+ if (startX >= endX) return;
4167
+ if (startY >= endY) return;
4168
+ for (let iy = startY; iy < endY; iy++) {
4169
+ const dy = targetY + iy;
4170
+ const sy = my + iy;
4171
+ let dIdx = dy * dstWidth + targetX + startX;
4172
+ let sIdx = sy * srcWidth + mx + startX;
4173
+ for (let ix = startX; ix < endX; ix++) {
4174
+ const rawM = src[sIdx];
4175
+ const effectiveM = invertMask ? 255 - rawM : rawM;
4176
+ let weight = 0;
4177
+ if (effectiveM === 0) {
4178
+ weight = 0;
4179
+ } else if (effectiveM === 255) {
4180
+ weight = globalAlpha;
4181
+ } else if (globalAlpha === 255) {
4182
+ weight = effectiveM;
4183
+ } else {
4184
+ weight = effectiveM * globalAlpha + 128 >> 8;
4185
+ }
4186
+ if (weight !== 255) {
4187
+ if (weight === 0) {
4188
+ dst[dIdx] = 0;
4189
+ } else {
4190
+ const da = dst[dIdx];
4191
+ if (da === 255) {
4192
+ dst[dIdx] = weight;
4193
+ } else if (da !== 0) {
4194
+ dst[dIdx] = da * weight + 128 >> 8;
4195
+ }
4196
+ }
4197
+ }
4198
+ sIdx++;
4199
+ dIdx++;
4200
+ }
4201
+ }
4202
+ }
4203
+
4204
+ // src/Mask/mergeBinaryMasks.ts
4205
+ function mergeBinaryMasks(dst, dstWidth, src, srcWidth, opts) {
4206
+ const {
4207
+ x: targetX = 0,
4208
+ y: targetY = 0,
4209
+ w: width = 0,
4210
+ h: height = 0,
4211
+ mx = 0,
4212
+ my = 0,
4213
+ invertMask = false
4214
+ } = opts;
4215
+ if (dstWidth <= 0) return;
4216
+ if (srcWidth <= 0) return;
4217
+ const dstHeight = dst.length / dstWidth | 0;
4218
+ const srcHeight = src.length / srcWidth | 0;
4219
+ let x = targetX;
4220
+ let y = targetY;
4221
+ let w = width;
4222
+ let h = height;
4223
+ if (x < 0) {
4224
+ w += x;
4225
+ x = 0;
4226
+ }
4227
+ if (y < 0) {
4228
+ h += y;
4229
+ y = 0;
3088
4230
  }
3089
- const sPitch = mw ?? width;
3090
- const isAlpha = maskType === 0 /* ALPHA */;
3091
- for (let iy = 0; iy < height; iy++) {
3092
- const dy = targetY + iy;
3093
- const sy = my + iy;
3094
- if (dy < 0 || sy < 0) {
3095
- continue;
3096
- }
3097
- for (let ix = 0; ix < width; ix++) {
3098
- const dx = targetX + ix;
3099
- const sx = mx + ix;
3100
- if (dx < 0 || dx >= dstWidth || sx < 0 || sx >= sPitch) {
3101
- continue;
3102
- }
3103
- const dIdx = dy * dstWidth + dx;
3104
- const sIdx = sy * sPitch + sx;
4231
+ w = Math.min(w, dstWidth - x);
4232
+ h = Math.min(h, dstHeight - y);
4233
+ if (w <= 0) return;
4234
+ if (h <= 0) return;
4235
+ const startX = mx + (x - targetX);
4236
+ const startY = my + (y - targetY);
4237
+ const sX0 = Math.max(0, startX);
4238
+ const sY0 = Math.max(0, startY);
4239
+ const sX1 = Math.min(srcWidth, startX + w);
4240
+ const sY1 = Math.min(srcHeight, startY + h);
4241
+ const finalW = sX1 - sX0;
4242
+ const finalH = sY1 - sY0;
4243
+ if (finalW <= 0) return;
4244
+ if (finalH <= 0) return;
4245
+ const xShift = sX0 - startX;
4246
+ const yShift = sY0 - startY;
4247
+ const dStride = dstWidth - finalW;
4248
+ const sStride = srcWidth - finalW;
4249
+ let dIdx = (y + yShift) * dstWidth + (x + xShift);
4250
+ let sIdx = sY0 * srcWidth + sX0;
4251
+ for (let iy = 0; iy < finalH; iy++) {
4252
+ for (let ix = 0; ix < finalW; ix++) {
3105
4253
  const mVal = src[sIdx];
3106
- let weight = globalAlpha;
3107
- if (isAlpha) {
3108
- const effectiveM = invertMask ? 255 - mVal : mVal;
3109
- if (effectiveM === 0) {
3110
- dst[dIdx] = 0;
3111
- continue;
3112
- }
3113
- weight = globalAlpha === 255 ? effectiveM : effectiveM * globalAlpha + 128 >> 8;
3114
- } else {
3115
- const isHit = invertMask ? mVal === 0 : mVal === 1;
3116
- if (!isHit) {
3117
- dst[dIdx] = 0;
3118
- continue;
3119
- }
3120
- weight = globalAlpha;
3121
- }
3122
- if (weight === 0) {
4254
+ const isMaskedOut = invertMask ? mVal !== 0 : mVal === 0;
4255
+ if (isMaskedOut) {
3123
4256
  dst[dIdx] = 0;
3124
- continue;
3125
- }
3126
- const da = dst[dIdx];
3127
- if (da === 0) {
3128
- } else if (weight === 255) {
3129
- } else if (da === 255) {
3130
- dst[dIdx] = weight;
3131
- } else {
3132
- dst[dIdx] = da * weight + 128 >> 8;
3133
4257
  }
4258
+ dIdx++;
4259
+ sIdx++;
3134
4260
  }
4261
+ dIdx += dStride;
4262
+ sIdx += sStride;
3135
4263
  }
3136
4264
  }
3137
4265
 
@@ -3139,118 +4267,258 @@ function mergeMasks(dst, dstWidth, src, opts) {
3139
4267
  var PixelData = class _PixelData {
3140
4268
  data32;
3141
4269
  imageData;
3142
- get width() {
3143
- return this.imageData.width;
3144
- }
3145
- get height() {
3146
- return this.imageData.height;
3147
- }
4270
+ width;
4271
+ height;
3148
4272
  constructor(imageData) {
3149
4273
  this.data32 = imageDataToUInt32Array(imageData);
3150
4274
  this.imageData = imageData;
4275
+ this.width = imageData.width;
4276
+ this.height = imageData.height;
3151
4277
  }
3152
4278
  set(imageData) {
4279
+ ;
3153
4280
  this.imageData = imageData;
3154
4281
  this.data32 = imageDataToUInt32Array(imageData);
4282
+ this.width = imageData.width;
4283
+ this.height = imageData.height;
3155
4284
  }
3156
- /**
3157
- * Creates a deep copy of the PixelData using the environment's ImageData constructor.
3158
- */
4285
+ // should only be used for debug and testing
3159
4286
  copy() {
3160
- const buffer = new Uint8ClampedArray(this.imageData.data);
3161
- const ImageConstructor = typeof ImageData !== "undefined" ? ImageData : this.imageData.constructor;
3162
- const newImageData = new ImageConstructor(
3163
- buffer,
3164
- this.width,
3165
- this.height
3166
- );
4287
+ const data = this.imageData.data;
4288
+ const buffer = new Uint8ClampedArray(data);
4289
+ const Ctor = this.imageData.constructor;
4290
+ const isCtorValid = typeof Ctor === "function";
4291
+ let newImageData;
4292
+ if (isCtorValid && Ctor !== Object) {
4293
+ const ImageConstructor = Ctor;
4294
+ newImageData = new ImageConstructor(buffer, this.width, this.height);
4295
+ } else {
4296
+ newImageData = {
4297
+ width: this.width,
4298
+ height: this.height,
4299
+ data: buffer
4300
+ };
4301
+ }
3167
4302
  return new _PixelData(newImageData);
3168
4303
  }
3169
4304
  };
3170
4305
 
3171
- // src/PixelData/applyCircleBrushToPixelData.ts
3172
- function applyCircleBrushToPixelData(target, color, centerX, centerY, brushSize, alpha = 255, fallOff, blendFn = sourceOverPerfect) {
3173
- const r = brushSize / 2;
3174
- const rSqr = r * r;
3175
- const centerOffset = brushSize % 2 === 0 ? 0.5 : 0;
3176
- const xStart = Math.max(0, Math.ceil(centerX - r));
3177
- const xEnd = Math.min(target.width - 1, Math.floor(centerX + r));
3178
- const yStart = Math.max(0, Math.ceil(centerY - r));
3179
- const yEnd = Math.min(target.height - 1, Math.floor(centerY + r));
3180
- const data32 = target.data32;
3181
- const targetWidth = target.width;
3182
- const baseColor = color & 16777215;
3183
- const invR = 1 / r;
3184
- const constantSrc = (alpha << 24 | baseColor) >>> 0;
3185
- for (let cy = yStart; cy <= yEnd; cy++) {
3186
- const dy = cy - centerY + centerOffset;
3187
- const dySqr = dy * dy;
3188
- const rowOffset = cy * targetWidth;
3189
- for (let cx = xStart; cx <= xEnd; cx++) {
3190
- const dx = cx - centerX + centerOffset;
3191
- const dSqr = dx * dx + dySqr;
3192
- if (dSqr <= rSqr) {
3193
- const idx = rowOffset + cx;
3194
- if (fallOff) {
3195
- const strength = fallOff(Math.sqrt(dSqr) * invR);
3196
- const fAlpha = alpha * strength & 255;
3197
- const src = (fAlpha << 24 | baseColor) >>> 0;
3198
- data32[idx] = blendFn(src, data32[idx]);
3199
- } else {
3200
- data32[idx] = blendFn(constantSrc, data32[idx]);
4306
+ // src/PixelData/blendPixelDataAlphaMask.ts
4307
+ function blendPixelDataAlphaMask(dst, src, alphaMask, opts = {}) {
4308
+ const {
4309
+ x: targetX = 0,
4310
+ y: targetY = 0,
4311
+ sx: sourceX = 0,
4312
+ sy: sourceY = 0,
4313
+ w: width = src.width,
4314
+ h: height = src.height,
4315
+ alpha: globalAlpha = 255,
4316
+ blendFn = sourceOverPerfect,
4317
+ mw = src.width,
4318
+ mx = 0,
4319
+ my = 0,
4320
+ invertMask = false
4321
+ } = opts;
4322
+ if (globalAlpha === 0) return;
4323
+ let x = targetX;
4324
+ let y = targetY;
4325
+ let sx = sourceX;
4326
+ let sy = sourceY;
4327
+ let w = width;
4328
+ let h = height;
4329
+ if (sx < 0) {
4330
+ x -= sx;
4331
+ w += sx;
4332
+ sx = 0;
4333
+ }
4334
+ if (sy < 0) {
4335
+ y -= sy;
4336
+ h += sy;
4337
+ sy = 0;
4338
+ }
4339
+ w = Math.min(w, src.width - sx);
4340
+ h = Math.min(h, src.height - sy);
4341
+ if (x < 0) {
4342
+ sx -= x;
4343
+ w += x;
4344
+ x = 0;
4345
+ }
4346
+ if (y < 0) {
4347
+ sy -= y;
4348
+ h += y;
4349
+ y = 0;
4350
+ }
4351
+ const actualW = Math.min(w, dst.width - x);
4352
+ const actualH = Math.min(h, dst.height - y);
4353
+ if (actualW <= 0 || actualH <= 0) return;
4354
+ const dw = dst.width;
4355
+ const sw = src.width;
4356
+ const mPitch = mw;
4357
+ const dx = x - targetX | 0;
4358
+ const dy = y - targetY | 0;
4359
+ const dst32 = dst.data32;
4360
+ const src32 = src.data32;
4361
+ let dIdx = y * dw + x | 0;
4362
+ let sIdx = sy * sw + sx | 0;
4363
+ let mIdx = (my + dy) * mPitch + (mx + dx) | 0;
4364
+ const dStride = dw - actualW | 0;
4365
+ const sStride = sw - actualW | 0;
4366
+ const mStride = mPitch - actualW | 0;
4367
+ const isOpaque = globalAlpha === 255;
4368
+ const isOverwrite = blendFn.isOverwrite || false;
4369
+ for (let iy = 0; iy < actualH; iy++) {
4370
+ for (let ix = 0; ix < actualW; ix++) {
4371
+ const mVal = alphaMask[mIdx];
4372
+ const effM = invertMask ? 255 - mVal : mVal;
4373
+ if (effM === 0) {
4374
+ dIdx++;
4375
+ sIdx++;
4376
+ mIdx++;
4377
+ continue;
4378
+ }
4379
+ const srcCol = src32[sIdx];
4380
+ const srcAlpha = srcCol >>> 24;
4381
+ if (srcAlpha === 0 && !isOverwrite) {
4382
+ dIdx++;
4383
+ sIdx++;
4384
+ mIdx++;
4385
+ continue;
4386
+ }
4387
+ let weight = globalAlpha;
4388
+ if (isOpaque) {
4389
+ weight = effM;
4390
+ } else if (effM !== 255) {
4391
+ weight = effM * globalAlpha + 128 >> 8;
4392
+ }
4393
+ if (weight === 0) {
4394
+ dIdx++;
4395
+ sIdx++;
4396
+ mIdx++;
4397
+ continue;
4398
+ }
4399
+ let finalCol = srcCol;
4400
+ if (weight < 255) {
4401
+ const a = srcAlpha * weight + 128 >> 8;
4402
+ if (a === 0 && !isOverwrite) {
4403
+ dIdx++;
4404
+ sIdx++;
4405
+ mIdx++;
4406
+ continue;
3201
4407
  }
4408
+ finalCol = (srcCol & 16777215 | a << 24) >>> 0;
3202
4409
  }
4410
+ dst32[dIdx] = blendFn(finalCol, dst32[dIdx]);
4411
+ dIdx++;
4412
+ sIdx++;
4413
+ mIdx++;
3203
4414
  }
4415
+ dIdx += dStride;
4416
+ sIdx += sStride;
4417
+ mIdx += mStride;
3204
4418
  }
3205
4419
  }
3206
4420
 
3207
- // src/PixelData/applyRectBrushToPixelData.ts
3208
- function applyRectBrushToPixelData(target, color, centerX, centerY, brushWidth, brushHeight, alpha = 255, fallOff, blendFn = sourceOverPerfect) {
3209
- const targetWidth = target.width;
3210
- const targetHeight = target.height;
3211
- const data32 = target.data32;
3212
- const rawStartX = Math.floor(centerX - brushWidth / 2);
3213
- const rawStartY = Math.floor(centerY - brushHeight / 2);
3214
- const endX = Math.min(targetWidth, rawStartX + brushWidth);
3215
- const endY = Math.min(targetHeight, rawStartY + brushHeight);
3216
- const startX = Math.max(0, rawStartX);
3217
- const startY = Math.max(0, rawStartY);
3218
- const baseColor = color & 16777215;
3219
- const constantSrc = (alpha << 24 | baseColor) >>> 0;
3220
- const invHalfW = 1 / (brushWidth / 2);
3221
- const invHalfH = 1 / (brushHeight / 2);
3222
- for (let py = startY; py < endY; py++) {
3223
- const rowOffset = py * targetWidth;
3224
- const dy = Math.abs(py + 0.5 - centerY) * invHalfH;
3225
- for (let px = startX; px < endX; px++) {
3226
- if (fallOff) {
3227
- const dx = Math.abs(px + 0.5 - centerX) * invHalfW;
3228
- const dist = dx > dy ? dx : dy;
3229
- const fAlpha = alpha * fallOff(dist) | 0;
3230
- const src = (fAlpha << 24 | baseColor) >>> 0;
3231
- data32[rowOffset + px] = blendFn(src, data32[rowOffset + px]);
3232
- } else {
3233
- data32[rowOffset + px] = blendFn(constantSrc, data32[rowOffset + px]);
4421
+ // src/PixelData/blendPixelDataBinaryMask.ts
4422
+ function blendPixelDataBinaryMask(dst, src, binaryMask, opts) {
4423
+ const {
4424
+ x: targetX = 0,
4425
+ y: targetY = 0,
4426
+ sx: sourceX = 0,
4427
+ sy: sourceY = 0,
4428
+ w: width = src.width,
4429
+ h: height = src.height,
4430
+ alpha: globalAlpha = 255,
4431
+ blendFn = sourceOverPerfect,
4432
+ mw = src.width,
4433
+ mx = 0,
4434
+ my = 0,
4435
+ invertMask = false
4436
+ } = opts;
4437
+ if (globalAlpha === 0) return;
4438
+ let x = targetX;
4439
+ let y = targetY;
4440
+ let sx = sourceX;
4441
+ let sy = sourceY;
4442
+ let w = width;
4443
+ let h = height;
4444
+ if (sx < 0) {
4445
+ x -= sx;
4446
+ w += sx;
4447
+ sx = 0;
4448
+ }
4449
+ if (sy < 0) {
4450
+ y -= sy;
4451
+ h += sy;
4452
+ sy = 0;
4453
+ }
4454
+ w = Math.min(w, src.width - sx);
4455
+ h = Math.min(h, src.height - sy);
4456
+ if (x < 0) {
4457
+ sx -= x;
4458
+ w += x;
4459
+ x = 0;
4460
+ }
4461
+ if (y < 0) {
4462
+ sy -= y;
4463
+ h += y;
4464
+ y = 0;
4465
+ }
4466
+ const actualW = Math.min(w, dst.width - x);
4467
+ const actualH = Math.min(h, dst.height - y);
4468
+ if (actualW <= 0 || actualH <= 0) return;
4469
+ const dx = x - targetX | 0;
4470
+ const dy = y - targetY | 0;
4471
+ const dst32 = dst.data32;
4472
+ const src32 = src.data32;
4473
+ const dw = dst.width;
4474
+ const sw = src.width;
4475
+ const mPitch = mw;
4476
+ let dIdx = y * dw + x | 0;
4477
+ let sIdx = sy * sw + sx | 0;
4478
+ let mIdx = (my + dy) * mPitch + (mx + dx) | 0;
4479
+ const dStride = dw - actualW | 0;
4480
+ const sStride = sw - actualW | 0;
4481
+ const mStride = mPitch - actualW | 0;
4482
+ const skipVal = invertMask ? 1 : 0;
4483
+ const isOpaque = globalAlpha === 255;
4484
+ const isOverwrite = blendFn.isOverwrite || false;
4485
+ for (let iy = 0; iy < actualH; iy++) {
4486
+ for (let ix = 0; ix < actualW; ix++) {
4487
+ if (binaryMask[mIdx] === skipVal) {
4488
+ dIdx++;
4489
+ sIdx++;
4490
+ mIdx++;
4491
+ continue;
4492
+ }
4493
+ const srcCol = src32[sIdx];
4494
+ const srcAlpha = srcCol >>> 24;
4495
+ if (srcAlpha === 0 && !isOverwrite) {
4496
+ dIdx++;
4497
+ sIdx++;
4498
+ mIdx++;
4499
+ continue;
3234
4500
  }
4501
+ let finalCol = srcCol;
4502
+ if (!isOpaque) {
4503
+ const a = srcAlpha * globalAlpha + 128 >> 8;
4504
+ if (a === 0 && !isOverwrite) {
4505
+ dIdx++;
4506
+ sIdx++;
4507
+ mIdx++;
4508
+ continue;
4509
+ }
4510
+ finalCol = (srcCol & 16777215 | a << 24) >>> 0;
4511
+ }
4512
+ dst32[dIdx] = blendFn(finalCol, dst32[dIdx]);
4513
+ dIdx++;
4514
+ sIdx++;
4515
+ mIdx++;
3235
4516
  }
4517
+ dIdx += dStride;
4518
+ sIdx += sStride;
4519
+ mIdx += mStride;
3236
4520
  }
3237
4521
  }
3238
- function getRectBrushBounds(centerX, centerY, brushWidth, brushHeight, targetWidth, targetHeight) {
3239
- const rawStartX = Math.floor(centerX - brushWidth / 2);
3240
- const rawStartY = Math.floor(centerY - brushHeight / 2);
3241
- const rawEndX = rawStartX + brushWidth;
3242
- const rawEndY = rawStartY + brushHeight;
3243
- const startX = targetWidth !== void 0 ? Math.max(0, rawStartX) : rawStartX;
3244
- const startY = targetHeight !== void 0 ? Math.max(0, rawStartY) : rawStartY;
3245
- const endX = targetWidth !== void 0 ? Math.min(targetWidth, rawEndX) : rawEndX;
3246
- const endY = targetHeight !== void 0 ? Math.min(targetHeight, rawEndY) : rawEndY;
3247
- return {
3248
- x: startX,
3249
- y: startY,
3250
- w: endX - startX,
3251
- h: endY - startY
3252
- };
3253
- }
3254
4522
 
3255
4523
  // src/PixelData/clearPixelData.ts
3256
4524
  function clearPixelData(dst, rect) {
@@ -3258,32 +4526,40 @@ function clearPixelData(dst, rect) {
3258
4526
  }
3259
4527
 
3260
4528
  // src/PixelData/extractPixelDataBuffer.ts
4529
+ var SCRATCH_BLIT4 = makeClippedBlit();
3261
4530
  function extractPixelDataBuffer(source, _x, _y, _w, _h) {
3262
- const { x, y, w, h } = typeof _x === "object" ? _x : { x: _x, y: _y, w: _w, h: _h };
4531
+ const {
4532
+ x,
4533
+ y,
4534
+ w,
4535
+ h
4536
+ } = typeof _x === "object" ? _x : {
4537
+ x: _x,
4538
+ y: _y,
4539
+ w: _w,
4540
+ h: _h
4541
+ };
3263
4542
  const srcW = source.width;
3264
4543
  const srcH = source.height;
3265
4544
  const srcData = source.data32;
3266
4545
  if (w <= 0 || h <= 0) {
3267
4546
  return new Uint32Array(0);
3268
4547
  }
3269
- const dstImageData = new ImageData(w, h);
3270
- const dstData = new Uint32Array(dstImageData.data.buffer);
3271
- const x0 = Math.max(0, x);
3272
- const y0 = Math.max(0, y);
3273
- const x1 = Math.min(srcW, x + w);
3274
- const y1 = Math.min(srcH, y + h);
3275
- if (x1 <= x0 || y1 <= y0) {
3276
- return dstData;
3277
- }
3278
- const copyWidth = x1 - x0;
3279
- const copyHeight = y1 - y0;
3280
- for (let row = 0; row < copyHeight; row++) {
3281
- const srcRow = y0 + row;
3282
- const srcStart = srcRow * srcW + x0;
3283
- const dstRow = y0 - y + row;
3284
- const dstCol = x0 - x;
3285
- const dstStart = dstRow * w + dstCol;
3286
- const chunk = srcData.subarray(srcStart, srcStart + copyWidth);
4548
+ const dstData = new Uint32Array(w * h);
4549
+ const clip = resolveBlitClipping(0, 0, x, y, w, h, w, h, srcW, srcH, SCRATCH_BLIT4);
4550
+ if (!clip.inBounds) return dstData;
4551
+ const {
4552
+ x: dstX,
4553
+ y: dstY,
4554
+ sx: srcX,
4555
+ sy: srcY,
4556
+ w: copyW,
4557
+ h: copyH
4558
+ } = clip;
4559
+ for (let row = 0; row < copyH; row++) {
4560
+ const srcStart = (srcY + row) * srcW + srcX;
4561
+ const dstStart = (dstY + row) * w + dstX;
4562
+ const chunk = srcData.subarray(srcStart, srcStart + copyW);
3287
4563
  dstData.set(chunk, dstStart);
3288
4564
  }
3289
4565
  return dstData;
@@ -3291,13 +4567,59 @@ function extractPixelDataBuffer(source, _x, _y, _w, _h) {
3291
4567
 
3292
4568
  // src/PixelData/extractPixelData.ts
3293
4569
  function extractPixelData(source, _x, _y, _w, _h) {
3294
- const { x, y, w, h } = typeof _x === "object" ? _x : { x: _x, y: _y, w: _w, h: _h };
4570
+ const {
4571
+ x,
4572
+ y,
4573
+ w,
4574
+ h
4575
+ } = typeof _x === "object" ? _x : {
4576
+ x: _x,
4577
+ y: _y,
4578
+ w: _w,
4579
+ h: _h
4580
+ };
3295
4581
  const result = new PixelData(new ImageData(w, h));
3296
4582
  const buffer = extractPixelDataBuffer(source, x, y, w, h);
3297
4583
  result.data32.set(buffer);
3298
4584
  return result;
3299
4585
  }
3300
4586
 
4587
+ // src/PixelData/PixelBuffer32.ts
4588
+ var PixelBuffer32 = class _PixelBuffer32 {
4589
+ constructor(width, height, data32) {
4590
+ this.width = width;
4591
+ this.height = height;
4592
+ this.data32 = data32 ?? new Uint32Array(width * height);
4593
+ }
4594
+ data32;
4595
+ set(width, height, data32) {
4596
+ ;
4597
+ this.data32 = data32 ?? new Uint32Array(width * height);
4598
+ this.width = width;
4599
+ this.height = height;
4600
+ }
4601
+ copy() {
4602
+ const newData32 = new Uint32Array(this.data32);
4603
+ return new _PixelBuffer32(this.width, this.height, newData32);
4604
+ }
4605
+ };
4606
+
4607
+ // src/PixelData/pixelDataToAlphaMask.ts
4608
+ function pixelDataToAlphaMask(pixelData) {
4609
+ const {
4610
+ data32,
4611
+ width,
4612
+ height
4613
+ } = pixelData;
4614
+ const len = data32.length;
4615
+ const mask = new Uint8Array(width * height);
4616
+ for (let i = 0; i < len; i++) {
4617
+ const val = data32[i];
4618
+ mask[i] = val >>> 24 & 255;
4619
+ }
4620
+ return mask;
4621
+ }
4622
+
3301
4623
  // src/PixelData/reflectPixelData.ts
3302
4624
  function reflectPixelDataHorizontal(pixelData) {
3303
4625
  const width = pixelData.width;
@@ -3335,12 +4657,12 @@ function reflectPixelDataVertical(pixelData) {
3335
4657
 
3336
4658
  // src/PixelData/resamplePixelData.ts
3337
4659
  function resamplePixelData(pixelData, factor) {
3338
- const { data, width, height } = resample32(pixelData.data32, pixelData.width, pixelData.height, factor);
3339
- return new PixelData(new ImageData(
3340
- new Uint8ClampedArray(data.buffer),
4660
+ const {
4661
+ data,
3341
4662
  width,
3342
4663
  height
3343
- ));
4664
+ } = resample32(pixelData.data32, pixelData.width, pixelData.height, factor);
4665
+ return new PixelData(new ImageData(new Uint8ClampedArray(data.buffer), width, height));
3344
4666
  }
3345
4667
 
3346
4668
  // src/PixelData/rotatePixelData.ts
@@ -3364,11 +4686,7 @@ function rotatePixelData(pixelData) {
3364
4686
  newData32[newIdx] = data[oldIdx];
3365
4687
  }
3366
4688
  }
3367
- const newImageData = new ImageData(
3368
- new Uint8ClampedArray(newData32.buffer),
3369
- newWidth,
3370
- newHeight
3371
- );
4689
+ const newImageData = new ImageData(new Uint8ClampedArray(newData32.buffer), newWidth, newHeight);
3372
4690
  pixelData.set(newImageData);
3373
4691
  }
3374
4692
  function rotateSquareInPlace(pixelData) {
@@ -3390,8 +4708,14 @@ function rotateSquareInPlace(pixelData) {
3390
4708
  }
3391
4709
 
3392
4710
  // src/PixelData/writePixelDataBuffer.ts
4711
+ var SCRATCH_BLIT5 = makeClippedBlit();
3393
4712
  function writePixelDataBuffer(target, data, _x, _y, _w, _h) {
3394
- const { x, y, w, h } = typeof _x === "object" ? _x : {
4713
+ const {
4714
+ x,
4715
+ y,
4716
+ w,
4717
+ h
4718
+ } = typeof _x === "object" ? _x : {
3395
4719
  x: _x,
3396
4720
  y: _y,
3397
4721
  w: _w,
@@ -3400,22 +4724,20 @@ function writePixelDataBuffer(target, data, _x, _y, _w, _h) {
3400
4724
  const dstW = target.width;
3401
4725
  const dstH = target.height;
3402
4726
  const dstData = target.data32;
3403
- const x0 = Math.max(0, x);
3404
- const y0 = Math.max(0, y);
3405
- const x1 = Math.min(dstW, x + w);
3406
- const y1 = Math.min(dstH, y + h);
3407
- if (x1 <= x0 || y1 <= y0) {
3408
- return;
3409
- }
3410
- const rowLen = x1 - x0;
3411
- const srcCol = x0 - x;
3412
- const srcYOffset = y0 - y;
3413
- const actualH = y1 - y0;
3414
- for (let row = 0; row < actualH; row++) {
3415
- const dstStart = (y0 + row) * dstW + x0;
3416
- const srcRow = srcYOffset + row;
3417
- const srcStart = srcRow * w + srcCol;
3418
- dstData.set(data.subarray(srcStart, srcStart + rowLen), dstStart);
4727
+ const clip = resolveBlitClipping(x, y, 0, 0, w, h, dstW, dstH, w, h, SCRATCH_BLIT5);
4728
+ if (!clip.inBounds) return;
4729
+ const {
4730
+ x: dstX,
4731
+ y: dstY,
4732
+ sx: srcX,
4733
+ sy: srcY,
4734
+ w: copyW,
4735
+ h: copyH
4736
+ } = clip;
4737
+ for (let row = 0; row < copyH; row++) {
4738
+ const dstStart = (dstY + row) * dstW + dstX;
4739
+ const srcStart = (srcY + row) * w + srcX;
4740
+ dstData.set(data.subarray(srcStart, srcStart + copyW), dstStart);
3419
4741
  }
3420
4742
  }
3421
4743
  // Annotate the CommonJS export names for ESM import in node:
@@ -3423,23 +4745,32 @@ function writePixelDataBuffer(target, data, _x, _y, _w, _h) {
3423
4745
  BASE_FAST_BLEND_MODE_FUNCTIONS,
3424
4746
  BASE_PERFECT_BLEND_MODE_FUNCTIONS,
3425
4747
  BaseBlendMode,
4748
+ CANVAS_CTX_FAILED,
3426
4749
  HistoryManager,
3427
4750
  IndexedImage,
3428
4751
  MaskType,
4752
+ OFFSCREEN_CANVAS_CTX_FAILED,
3429
4753
  PixelAccumulator,
4754
+ PixelBuffer32,
3430
4755
  PixelData,
3431
4756
  PixelEngineConfig,
3432
4757
  PixelTile,
3433
4758
  PixelWriter,
3434
4759
  UnsupportedFormatError,
4760
+ applyAlphaMaskToPixelData,
4761
+ applyBinaryMaskToAlphaMask,
4762
+ applyBinaryMaskToPixelData,
3435
4763
  applyCircleBrushToPixelData,
3436
- applyMaskToPixelData,
3437
4764
  applyPatchTiles,
3438
4765
  applyRectBrushToPixelData,
3439
4766
  base64DecodeArrayBuffer,
3440
4767
  base64EncodeArrayBuffer,
3441
4768
  blendColorPixelData,
4769
+ blendColorPixelDataAlphaMask,
4770
+ blendColorPixelDataBinaryMask,
3442
4771
  blendPixelData,
4772
+ blendPixelDataAlphaMask,
4773
+ blendPixelDataBinaryMask,
3443
4774
  clearPixelData,
3444
4775
  color32ToCssRGBA,
3445
4776
  color32ToHex,
@@ -3472,9 +4803,13 @@ function writePixelDataBuffer(target, data, _x, _y, _w, _h) {
3472
4803
  fileToImageData,
3473
4804
  fillPixelData,
3474
4805
  floodFillSelection,
4806
+ forEachLinePoint,
4807
+ getCircleBrushOrPencilBounds,
4808
+ getCircleBrushOrPencilStrokeBounds,
3475
4809
  getImageDataFromClipboard,
3476
4810
  getIndexedImageColorCounts,
3477
- getRectBrushBounds,
4811
+ getRectBrushOrPencilBounds,
4812
+ getRectBrushOrPencilStrokeBounds,
3478
4813
  getSupportedPixelFormats,
3479
4814
  hardLightFast,
3480
4815
  hardLightPerfect,
@@ -3506,17 +4841,29 @@ function writePixelDataBuffer(target, data, _x, _y, _w, _h) {
3506
4841
  makeBlendModeRegistry,
3507
4842
  makeFastBlendModeRegistry,
3508
4843
  makeFullPixelMutator,
4844
+ makeImageDataLike,
3509
4845
  makePerfectBlendModeRegistry,
3510
4846
  makePixelCanvas,
3511
4847
  makeReusableCanvas,
3512
4848
  makeReusableImageData,
3513
- mergeMasks,
4849
+ mergeAlphaMasks,
4850
+ mergeBinaryMasks,
3514
4851
  multiplyFast,
3515
4852
  multiplyPerfect,
3516
- mutatorApplyMask,
4853
+ mutatorApplyAlphaMask,
4854
+ mutatorApplyBinaryMask,
4855
+ mutatorApplyCircleBrush,
4856
+ mutatorApplyCircleBrushStroke,
4857
+ mutatorApplyCirclePencil,
4858
+ mutatorApplyCirclePencilStroke,
4859
+ mutatorApplyRectBrush,
4860
+ mutatorApplyRectBrushStroke,
4861
+ mutatorApplyRectPencil,
4862
+ mutatorApplyRectPencilStroke,
3517
4863
  mutatorBlendColor,
3518
4864
  mutatorBlendPixel,
3519
4865
  mutatorBlendPixelData,
4866
+ mutatorClear,
3520
4867
  mutatorFill,
3521
4868
  mutatorInvert,
3522
4869
  overlayFast,
@@ -3546,7 +4893,10 @@ function writePixelDataBuffer(target, data, _x, _y, _w, _h) {
3546
4893
  sourceOverPerfect,
3547
4894
  subtractFast,
3548
4895
  subtractPerfect,
4896
+ toBlendModeIndexAndName,
3549
4897
  trimRectBounds,
4898
+ uInt32ArrayToImageData,
4899
+ uInt32ArrayToImageDataLike,
3550
4900
  unpackAlpha,
3551
4901
  unpackBlue,
3552
4902
  unpackColor,