pixel-data-js 0.15.1 → 0.17.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 (36) hide show
  1. package/dist/index.dev.cjs +1884 -1203
  2. package/dist/index.dev.cjs.map +1 -1
  3. package/dist/index.dev.js +1858 -1190
  4. package/dist/index.dev.js.map +1 -1
  5. package/dist/index.prod.cjs +1884 -1203
  6. package/dist/index.prod.cjs.map +1 -1
  7. package/dist/index.prod.d.ts +320 -66
  8. package/dist/index.prod.js +1858 -1190
  9. package/dist/index.prod.js.map +1 -1
  10. package/package.json +1 -1
  11. package/src/BlendModes/BlendModeRegistry.ts +62 -0
  12. package/src/BlendModes/blend-modes-fast.ts +31 -84
  13. package/src/BlendModes/blend-modes-perfect.ts +343 -215
  14. package/src/BlendModes/blend-modes.ts +28 -30
  15. package/src/History/HistoryManager.ts +83 -0
  16. package/src/History/PixelAccumulator.ts +191 -0
  17. package/src/History/PixelEngineConfig.ts +18 -0
  18. package/src/History/PixelMutator/mutatorApplyMask.ts +20 -0
  19. package/src/History/PixelMutator/mutatorBlendColor.ts +22 -0
  20. package/src/History/PixelMutator/mutatorBlendPixel.ts +37 -0
  21. package/src/History/PixelMutator/mutatorBlendPixelData.ts +24 -0
  22. package/src/History/PixelMutator/mutatorFillPixelData.ts +21 -0
  23. package/src/History/PixelMutator/mutatorInvert.ts +18 -0
  24. package/src/History/PixelMutator.ts +18 -0
  25. package/src/History/PixelPatchTiles.ts +52 -0
  26. package/src/History/PixelWriter.ts +79 -0
  27. package/src/ImageData/{writeImageDataPixels.ts → writeImageDataBuffer.ts} +3 -3
  28. package/src/PixelData/applyCircleBrushToPixelData.ts +69 -0
  29. package/src/PixelData/applyMaskToPixelData.ts +1 -1
  30. package/src/PixelData/applyRectBrushToPixelData.ts +102 -0
  31. package/src/PixelData/blendPixelData.ts +2 -3
  32. package/src/PixelData/invertPixelData.ts +74 -7
  33. package/src/PixelData/writePixelDataBuffer.ts +65 -0
  34. package/src/_types.ts +31 -11
  35. package/src/index.ts +20 -10
  36. package/src/BlendModes/blend-mode-getters.ts +0 -14
package/dist/index.dev.js CHANGED
@@ -1,33 +1,70 @@
1
1
  // src/BlendModes/blend-modes.ts
2
- var BlendMode = /* @__PURE__ */ ((BlendMode2) => {
3
- BlendMode2[BlendMode2["overwrite"] = 0] = "overwrite";
4
- BlendMode2[BlendMode2["sourceOver"] = 1] = "sourceOver";
5
- BlendMode2[BlendMode2["darken"] = 2] = "darken";
6
- BlendMode2[BlendMode2["multiply"] = 3] = "multiply";
7
- BlendMode2[BlendMode2["colorBurn"] = 4] = "colorBurn";
8
- BlendMode2[BlendMode2["linearBurn"] = 5] = "linearBurn";
9
- BlendMode2[BlendMode2["darkerColor"] = 6] = "darkerColor";
10
- BlendMode2[BlendMode2["lighten"] = 7] = "lighten";
11
- BlendMode2[BlendMode2["screen"] = 8] = "screen";
12
- BlendMode2[BlendMode2["colorDodge"] = 9] = "colorDodge";
13
- BlendMode2[BlendMode2["linearDodge"] = 10] = "linearDodge";
14
- BlendMode2[BlendMode2["lighterColor"] = 11] = "lighterColor";
15
- BlendMode2[BlendMode2["overlay"] = 12] = "overlay";
16
- BlendMode2[BlendMode2["softLight"] = 13] = "softLight";
17
- BlendMode2[BlendMode2["hardLight"] = 14] = "hardLight";
18
- BlendMode2[BlendMode2["vividLight"] = 15] = "vividLight";
19
- BlendMode2[BlendMode2["linearLight"] = 16] = "linearLight";
20
- BlendMode2[BlendMode2["pinLight"] = 17] = "pinLight";
21
- BlendMode2[BlendMode2["hardMix"] = 18] = "hardMix";
22
- BlendMode2[BlendMode2["difference"] = 19] = "difference";
23
- BlendMode2[BlendMode2["exclusion"] = 20] = "exclusion";
24
- BlendMode2[BlendMode2["subtract"] = 21] = "subtract";
25
- BlendMode2[BlendMode2["divide"] = 22] = "divide";
26
- return BlendMode2;
27
- })(BlendMode || {});
2
+ var BaseBlendMode = {
3
+ overwrite: 0,
4
+ sourceOver: 1,
5
+ darken: 2,
6
+ multiply: 3,
7
+ colorBurn: 4,
8
+ linearBurn: 5,
9
+ darkerColor: 6,
10
+ lighten: 7,
11
+ screen: 8,
12
+ colorDodge: 9,
13
+ linearDodge: 10,
14
+ lighterColor: 11,
15
+ overlay: 12,
16
+ softLight: 13,
17
+ hardLight: 14,
18
+ vividLight: 15,
19
+ linearLight: 16,
20
+ pinLight: 17,
21
+ hardMix: 18,
22
+ difference: 19,
23
+ exclusion: 20,
24
+ subtract: 21,
25
+ divide: 22
26
+ };
28
27
  var overwriteBase = (src, _dst) => src;
29
28
  overwriteBase.isOverwrite = true;
30
29
 
30
+ // src/BlendModes/BlendModeRegistry.ts
31
+ function makeBlendModeRegistry(blendModes, initialEntries) {
32
+ const blendToName = /* @__PURE__ */ new Map();
33
+ const blendToIndex = /* @__PURE__ */ new Map();
34
+ const indexToName = [];
35
+ const indexToBlend = [];
36
+ const nameToBlend = {};
37
+ const nameToIndex = {};
38
+ const add = (name, index, blendFn) => {
39
+ if (!Number.isFinite(index)) {
40
+ throw new Error(`Index "${index}" is not a number. Attempting to add name: "${name}", index: "${index}"`);
41
+ }
42
+ if (indexToBlend[index]) {
43
+ throw new Error(`Blend Mode index: ${index} is already used. Attempting to add name: "${name}", index: "${index}"`);
44
+ }
45
+ indexToName[index] = name;
46
+ indexToBlend[index] = blendFn;
47
+ blendToIndex.set(blendFn, index);
48
+ blendToName.set(blendFn, name);
49
+ nameToBlend[name] = blendFn;
50
+ nameToIndex[name] = index;
51
+ };
52
+ for (const [name, index] of Object.entries(blendModes)) {
53
+ const blend = initialEntries[index];
54
+ add(name, index, blend);
55
+ }
56
+ return {
57
+ nameToBlend,
58
+ nameToIndex,
59
+ blendToIndex,
60
+ blendToName,
61
+ indexToBlend,
62
+ indexToName,
63
+ indexType: null,
64
+ nameType: null
65
+ };
66
+ }
67
+
31
68
  // src/BlendModes/blend-modes-fast.ts
32
69
  var overwriteFast = overwriteBase;
33
70
  var sourceOverFast = (src, dst) => {
@@ -413,76 +450,34 @@ var divideFast = (src, dst) => {
413
450
  const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
414
451
  return (a << 24 | b << 16 | g << 8 | r) >>> 0;
415
452
  };
416
- var FAST_BLENDER_REGISTRY = [
417
- [0 /* overwrite */, overwriteFast],
418
- [1 /* sourceOver */, sourceOverFast],
419
- [2 /* darken */, darkenFast],
420
- [3 /* multiply */, multiplyFast],
421
- [4 /* colorBurn */, colorBurnFast],
422
- [5 /* linearBurn */, linearBurnFast],
423
- [6 /* darkerColor */, darkerFast],
424
- [7 /* lighten */, lightenFast],
425
- [8 /* screen */, screenFast],
426
- [9 /* colorDodge */, colorDodgeFast],
427
- [10 /* linearDodge */, linearDodgeFast],
428
- [11 /* lighterColor */, lighterFast],
429
- [12 /* overlay */, overlayFast],
430
- [13 /* softLight */, softLightFast],
431
- [14 /* hardLight */, hardLightFast],
432
- [15 /* vividLight */, vividLightFast],
433
- [16 /* linearLight */, linearLightFast],
434
- [17 /* pinLight */, pinLightFast],
435
- [18 /* hardMix */, hardMixFast],
436
- [19 /* difference */, differenceFast],
437
- [20 /* exclusion */, exclusionFast],
438
- [21 /* subtract */, subtractFast],
439
- [22 /* divide */, divideFast]
440
- ];
441
- var FAST_BLEND_MODES = [];
442
- for (const [index, blend] of FAST_BLENDER_REGISTRY) {
443
- FAST_BLEND_MODES[index] = blend;
444
- }
445
- var FAST_BLEND_TO_INDEX = new Map(
446
- FAST_BLENDER_REGISTRY.map((entry, index) => {
447
- return [
448
- entry[1],
449
- index
450
- ];
451
- })
452
- );
453
- var INDEX_TO_FAST_BLEND = new Map(
454
- FAST_BLENDER_REGISTRY.map((entry, index) => {
455
- return [
456
- index,
457
- entry[1]
458
- ];
459
- })
460
- );
461
- var FAST_BLEND_MODE_BY_NAME = {
462
- overwrite: overwriteFast,
463
- sourceOver: sourceOverFast,
464
- darken: darkenFast,
465
- multiply: multiplyFast,
466
- colorBurn: colorBurnFast,
467
- linearBurn: linearBurnFast,
468
- darkerColor: darkerFast,
469
- lighten: lightenFast,
470
- screen: screenFast,
471
- colorDodge: colorDodgeFast,
472
- linearDodge: linearDodgeFast,
473
- lighterColor: lighterFast,
474
- overlay: overlayFast,
475
- softLight: softLightFast,
476
- hardLight: hardLightFast,
477
- vividLight: vividLightFast,
478
- linearLight: linearLightFast,
479
- pinLight: pinLightFast,
480
- hardMix: hardMixFast,
481
- difference: differenceFast,
482
- exclusion: exclusionFast,
483
- subtract: subtractFast,
484
- divide: divideFast
453
+ var BASE_FAST_BLEND_MODE_FUNCTIONS = {
454
+ [BaseBlendMode.overwrite]: overwriteFast,
455
+ [BaseBlendMode.sourceOver]: sourceOverFast,
456
+ [BaseBlendMode.darken]: darkenFast,
457
+ [BaseBlendMode.multiply]: multiplyFast,
458
+ [BaseBlendMode.colorBurn]: colorBurnFast,
459
+ [BaseBlendMode.linearBurn]: linearBurnFast,
460
+ [BaseBlendMode.darkerColor]: darkerFast,
461
+ [BaseBlendMode.lighten]: lightenFast,
462
+ [BaseBlendMode.screen]: screenFast,
463
+ [BaseBlendMode.colorDodge]: colorDodgeFast,
464
+ [BaseBlendMode.linearDodge]: linearDodgeFast,
465
+ [BaseBlendMode.lighterColor]: lighterFast,
466
+ [BaseBlendMode.overlay]: overlayFast,
467
+ [BaseBlendMode.softLight]: softLightFast,
468
+ [BaseBlendMode.hardLight]: hardLightFast,
469
+ [BaseBlendMode.vividLight]: vividLightFast,
470
+ [BaseBlendMode.linearLight]: linearLightFast,
471
+ [BaseBlendMode.pinLight]: pinLightFast,
472
+ [BaseBlendMode.hardMix]: hardMixFast,
473
+ [BaseBlendMode.difference]: differenceFast,
474
+ [BaseBlendMode.exclusion]: exclusionFast,
475
+ [BaseBlendMode.subtract]: subtractFast,
476
+ [BaseBlendMode.divide]: divideFast
485
477
  };
478
+ function makeFastBlendModeRegistry() {
479
+ return makeBlendModeRegistry(BaseBlendMode, BASE_FAST_BLEND_MODE_FUNCTIONS);
480
+ }
486
481
 
487
482
  // src/_types.ts
488
483
  var MaskType = /* @__PURE__ */ ((MaskType2) => {
@@ -849,14 +844,18 @@ var sourceOverPerfect = (src, dst) => {
849
844
  const sa = src >>> 24 & 255;
850
845
  if (sa === 255) return src;
851
846
  if (sa === 0) return dst;
847
+ const invA = 255 - sa;
852
848
  const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
853
849
  const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
854
850
  const da = dst >>> 24 & 255;
855
- const invA = 255 - sa;
856
- const r = (sr * sa + dr * invA) / 255 | 0;
857
- const g = (sg * sa + dg * invA) / 255 | 0;
858
- const b = (sb * sa + db * invA) / 255 | 0;
859
- const a = (255 * sa + da * invA) / 255 | 0;
851
+ const tR = sr * sa + dr * invA;
852
+ const r = tR + 1 + (tR >> 8) >> 8;
853
+ const tG = sg * sa + dg * invA;
854
+ const g = tG + 1 + (tG >> 8) >> 8;
855
+ const tB = sb * sa + db * invA;
856
+ const b = tB + 1 + (tB >> 8) >> 8;
857
+ const tA = 255 * sa + da * invA;
858
+ const a = tA + 1 + (tA >> 8) >> 8;
860
859
  return (a << 24 | b << 16 | g << 8 | r) >>> 0;
861
860
  };
862
861
  var darkenPerfect = (src, dst) => {
@@ -869,44 +868,71 @@ var darkenPerfect = (src, dst) => {
869
868
  const bb = sb < db ? sb : db;
870
869
  if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
871
870
  const invA = 255 - sa;
872
- const r = (br * sa + dr * invA) / 255 | 0;
873
- const g = (bg * sa + dg * invA) / 255 | 0;
874
- const b = (bb * sa + db * invA) / 255 | 0;
875
- const a = (255 * sa + (dst >>> 24 & 255) * invA) / 255 | 0;
871
+ const da = dst >>> 24 & 255;
872
+ const tR = br * sa + dr * invA;
873
+ const r = tR + 1 + (tR >> 8) >> 8;
874
+ const tG = bg * sa + dg * invA;
875
+ const g = tG + 1 + (tG >> 8) >> 8;
876
+ const tB = bb * sa + db * invA;
877
+ const b = tB + 1 + (tB >> 8) >> 8;
878
+ const tA = 255 * sa + da * invA;
879
+ const a = tA + 1 + (tA >> 8) >> 8;
876
880
  return (a << 24 | b << 16 | g << 8 | r) >>> 0;
877
881
  };
878
882
  var multiplyPerfect = (src, dst) => {
879
883
  const sa = src >>> 24 & 255;
880
884
  if (sa === 0) return dst;
881
- const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
882
- const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
883
- const br = sr * dr / 255 | 0;
884
- const bg = sg * dg / 255 | 0;
885
- const bb = sb * db / 255 | 0;
885
+ const dr = dst & 255;
886
+ const dg = dst >>> 8 & 255;
887
+ const db = dst >>> 16 & 255;
888
+ const da = dst >>> 24 & 255;
889
+ const sr = src & 255;
890
+ const sg = src >>> 8 & 255;
891
+ const sb = src >>> 16 & 255;
892
+ const mR = sr * dr;
893
+ const br = mR + 1 + (mR >> 8) >> 8;
894
+ const mG = sg * dg;
895
+ const bg = mG + 1 + (mG >> 8) >> 8;
896
+ const mB = sb * db;
897
+ const bb = mB + 1 + (mB >> 8) >> 8;
886
898
  if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
887
899
  const invA = 255 - sa;
888
- const da = dst >>> 24 & 255;
889
- const r = (br * sa + dr * invA) / 255 | 0;
890
- const g = (bg * sa + dg * invA) / 255 | 0;
891
- const b = (bb * sa + db * invA) / 255 | 0;
892
- const a = (255 * sa + da * invA) / 255 | 0;
900
+ const tR = br * sa + dr * invA;
901
+ const r = tR + 1 + (tR >> 8) >> 8;
902
+ const tG = bg * sa + dg * invA;
903
+ const g = tG + 1 + (tG >> 8) >> 8;
904
+ const tB = bb * sa + db * invA;
905
+ const b = tB + 1 + (tB >> 8) >> 8;
906
+ const tA = 255 * sa + da * invA;
907
+ const a = tA + 1 + (tA >> 8) >> 8;
893
908
  return (a << 24 | b << 16 | g << 8 | r) >>> 0;
894
909
  };
895
910
  var colorBurnPerfect = (src, dst) => {
896
911
  const sa = src >>> 24 & 255;
897
912
  if (sa === 0) return dst;
898
- const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
899
- const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
900
- const br = dr === 255 ? 255 : sr === 0 ? 0 : Math.max(0, 255 - ((255 - dr) * 255 / sr | 0));
901
- const bg = dg === 255 ? 255 : sg === 0 ? 0 : Math.max(0, 255 - ((255 - dg) * 255 / sg | 0));
902
- const bb = db === 255 ? 255 : sb === 0 ? 0 : Math.max(0, 255 - ((255 - db) * 255 / sb | 0));
913
+ const dr = dst & 255;
914
+ const dg = dst >>> 8 & 255;
915
+ const db = dst >>> 16 & 255;
916
+ const sr = src & 255;
917
+ const sg = src >>> 8 & 255;
918
+ const sb = src >>> 16 & 255;
919
+ const resR = dr === 255 ? 255 : sr === 0 ? 0 : 255 - ((255 - dr) * 255 / sr | 0);
920
+ const br = resR < 0 ? 0 : resR;
921
+ const resG = dg === 255 ? 255 : sg === 0 ? 0 : 255 - ((255 - dg) * 255 / sg | 0);
922
+ const bg = resG < 0 ? 0 : resG;
923
+ const resB = db === 255 ? 255 : sb === 0 ? 0 : 255 - ((255 - db) * 255 / sb | 0);
924
+ const bb = resB < 0 ? 0 : resB;
903
925
  if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
904
926
  const invA = 255 - sa;
905
927
  const da = dst >>> 24 & 255;
906
- const r = (br * sa + dr * invA) / 255 | 0;
907
- const g = (bg * sa + dg * invA) / 255 | 0;
908
- const b = (bb * sa + db * invA) / 255 | 0;
909
- const a = (255 * sa + da * invA) / 255 | 0;
928
+ const tR = br * sa + dr * invA;
929
+ const r = tR + 1 + (tR >> 8) >> 8;
930
+ const tG = bg * sa + dg * invA;
931
+ const g = tG + 1 + (tG >> 8) >> 8;
932
+ const tB = bb * sa + db * invA;
933
+ const b = tB + 1 + (tB >> 8) >> 8;
934
+ const tA = 255 * sa + da * invA;
935
+ const a = tA + 1 + (tA >> 8) >> 8;
910
936
  return (a << 24 | b << 16 | g << 8 | r) >>> 0;
911
937
  };
912
938
  var linearBurnPerfect = (src, dst) => {
@@ -922,10 +948,15 @@ var linearBurnPerfect = (src, dst) => {
922
948
  const bb = bbU < 0 ? 0 : bbU;
923
949
  if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
924
950
  const invA = 255 - sa;
925
- const r = (br * sa + dr * invA) / 255 | 0;
926
- const g = (bg * sa + dg * invA) / 255 | 0;
927
- const b = (bb * sa + db * invA) / 255 | 0;
928
- const a = (255 * sa + (dst >>> 24 & 255) * invA) / 255 | 0;
951
+ const da = dst >>> 24 & 255;
952
+ const tR = br * sa + dr * invA;
953
+ const r = tR + 1 + (tR >> 8) >> 8;
954
+ const tG = bg * sa + dg * invA;
955
+ const g = tG + 1 + (tG >> 8) >> 8;
956
+ const tB = bb * sa + db * invA;
957
+ const b = tB + 1 + (tB >> 8) >> 8;
958
+ const tA = 255 * sa + da * invA;
959
+ const a = tA + 1 + (tA >> 8) >> 8;
929
960
  return (a << 24 | b << 16 | g << 8 | r) >>> 0;
930
961
  };
931
962
  var darkerPerfect = (src, dst) => {
@@ -947,10 +978,15 @@ var darkerPerfect = (src, dst) => {
947
978
  }
948
979
  if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
949
980
  const invA = 255 - sa;
950
- const r = (br * sa + dr * invA) / 255 | 0;
951
- const g = (bg * sa + dg * invA) / 255 | 0;
952
- const b = (bb * sa + db * invA) / 255 | 0;
953
- const a = (255 * sa + (dst >>> 24 & 255) * invA) / 255 | 0;
981
+ const da = dst >>> 24 & 255;
982
+ const tR = br * sa + dr * invA;
983
+ const r = tR + 1 + (tR >> 8) >> 8;
984
+ const tG = bg * sa + dg * invA;
985
+ const g = tG + 1 + (tG >> 8) >> 8;
986
+ const tB = bb * sa + db * invA;
987
+ const b = tB + 1 + (tB >> 8) >> 8;
988
+ const tA = 255 * sa + da * invA;
989
+ const a = tA + 1 + (tA >> 8) >> 8;
954
990
  return (a << 24 | b << 16 | g << 8 | r) >>> 0;
955
991
  };
956
992
  var lightenPerfect = (src, dst) => {
@@ -962,10 +998,15 @@ var lightenPerfect = (src, dst) => {
962
998
  const bb = (src >>> 16 & 255) > db ? src >>> 16 & 255 : db;
963
999
  if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
964
1000
  const invA = 255 - sa;
965
- const r = (br * sa + dr * invA) / 255 | 0;
966
- const g = (bg * sa + dg * invA) / 255 | 0;
967
- const b = (bb * sa + db * invA) / 255 | 0;
968
- const a = (255 * sa + (dst >>> 24 & 255) * invA) / 255 | 0;
1001
+ const da = dst >>> 24 & 255;
1002
+ const tR = br * sa + dr * invA;
1003
+ const r = tR + 1 + (tR >> 8) >> 8;
1004
+ const tG = bg * sa + dg * invA;
1005
+ const g = tG + 1 + (tG >> 8) >> 8;
1006
+ const tB = bb * sa + db * invA;
1007
+ const b = tB + 1 + (tB >> 8) >> 8;
1008
+ const tA = 255 * sa + da * invA;
1009
+ const a = tA + 1 + (tA >> 8) >> 8;
969
1010
  return (a << 24 | b << 16 | g << 8 | r) >>> 0;
970
1011
  };
971
1012
  var screenPerfect = (src, dst) => {
@@ -977,26 +1018,43 @@ var screenPerfect = (src, dst) => {
977
1018
  const bb = 255 - ((255 - (src >>> 16 & 255)) * (255 - db) / 255 | 0);
978
1019
  if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
979
1020
  const invA = 255 - sa;
980
- const r = (br * sa + dr * invA) / 255 | 0;
981
- const g = (bg * sa + dg * invA) / 255 | 0;
982
- const b = (bb * sa + db * invA) / 255 | 0;
983
- const a = (255 * sa + (dst >>> 24 & 255) * invA) / 255 | 0;
1021
+ const da = dst >>> 24 & 255;
1022
+ const tR = br * sa + dr * invA;
1023
+ const r = tR + 1 + (tR >> 8) >> 8;
1024
+ const tG = bg * sa + dg * invA;
1025
+ const g = tG + 1 + (tG >> 8) >> 8;
1026
+ const tB = bb * sa + db * invA;
1027
+ const b = tB + 1 + (tB >> 8) >> 8;
1028
+ const tA = 255 * sa + da * invA;
1029
+ const a = tA + 1 + (tA >> 8) >> 8;
984
1030
  return (a << 24 | b << 16 | g << 8 | r) >>> 0;
985
1031
  };
986
1032
  var colorDodgePerfect = (src, dst) => {
987
1033
  const sa = src >>> 24 & 255;
988
1034
  if (sa === 0) return dst;
989
- const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
990
- const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
991
- const br = sr === 255 ? 255 : Math.min(255, dr * 255 / (255 - sr) | 0);
992
- const bg = sg === 255 ? 255 : Math.min(255, dg * 255 / (255 - sg) | 0);
993
- const bb = sb === 255 ? 255 : Math.min(255, db * 255 / (255 - sb) | 0);
1035
+ const dr = dst & 255;
1036
+ const dg = dst >>> 8 & 255;
1037
+ const db = dst >>> 16 & 255;
1038
+ const sr = src & 255;
1039
+ const sg = src >>> 8 & 255;
1040
+ const sb = src >>> 16 & 255;
1041
+ const resR = sr === 255 ? 255 : dr * 255 / (255 - sr) | 0;
1042
+ const br = resR > 255 ? 255 : resR;
1043
+ const resG = sg === 255 ? 255 : dg * 255 / (255 - sg) | 0;
1044
+ const bg = resG > 255 ? 255 : resG;
1045
+ const resB = sb === 255 ? 255 : db * 255 / (255 - sb) | 0;
1046
+ const bb = resB > 255 ? 255 : resB;
994
1047
  if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
995
1048
  const invA = 255 - sa;
996
- const r = (br * sa + dr * invA) / 255 | 0;
997
- const g = (bg * sa + dg * invA) / 255 | 0;
998
- const b = (bb * sa + db * invA) / 255 | 0;
999
- const a = (255 * sa + (dst >>> 24 & 255) * invA) / 255 | 0;
1049
+ const da = dst >>> 24 & 255;
1050
+ const tR = br * sa + dr * invA;
1051
+ const r = tR + 1 + (tR >> 8) >> 8;
1052
+ const tG = bg * sa + dg * invA;
1053
+ const g = tG + 1 + (tG >> 8) >> 8;
1054
+ const tB = bb * sa + db * invA;
1055
+ const b = tB + 1 + (tB >> 8) >> 8;
1056
+ const tA = 255 * sa + da * invA;
1057
+ const a = tA + 1 + (tA >> 8) >> 8;
1000
1058
  return (a << 24 | b << 16 | g << 8 | r) >>> 0;
1001
1059
  };
1002
1060
  var linearDodgePerfect = (src, dst) => {
@@ -1011,10 +1069,15 @@ var linearDodgePerfect = (src, dst) => {
1011
1069
  const bb = bbU > 255 ? 255 : bbU;
1012
1070
  if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
1013
1071
  const invA = 255 - sa;
1014
- const r = (br * sa + dr * invA) / 255 | 0;
1015
- const g = (bg * sa + dg * invA) / 255 | 0;
1016
- const b = (bb * sa + db * invA) / 255 | 0;
1017
- const a = (255 * sa + (dst >>> 24 & 255) * invA) / 255 | 0;
1072
+ const da = dst >>> 24 & 255;
1073
+ const tR = br * sa + dr * invA;
1074
+ const r = tR + 1 + (tR >> 8) >> 8;
1075
+ const tG = bg * sa + dg * invA;
1076
+ const g = tG + 1 + (tG >> 8) >> 8;
1077
+ const tB = bb * sa + db * invA;
1078
+ const b = tB + 1 + (tB >> 8) >> 8;
1079
+ const tA = 255 * sa + da * invA;
1080
+ const a = tA + 1 + (tA >> 8) >> 8;
1018
1081
  return (a << 24 | b << 16 | g << 8 | r) >>> 0;
1019
1082
  };
1020
1083
  var lighterPerfect = (src, dst) => {
@@ -1036,10 +1099,15 @@ var lighterPerfect = (src, dst) => {
1036
1099
  }
1037
1100
  if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
1038
1101
  const invA = 255 - sa;
1039
- const r = (br * sa + dr * invA) / 255 | 0;
1040
- const g = (bg * sa + dg * invA) / 255 | 0;
1041
- const b = (bb * sa + db * invA) / 255 | 0;
1042
- const a = (255 * sa + (dst >>> 24 & 255) * invA) / 255 | 0;
1102
+ const da = dst >>> 24 & 255;
1103
+ const tR = br * sa + dr * invA;
1104
+ const r = tR + 1 + (tR >> 8) >> 8;
1105
+ const tG = bg * sa + dg * invA;
1106
+ const g = tG + 1 + (tG >> 8) >> 8;
1107
+ const tB = bb * sa + db * invA;
1108
+ const b = tB + 1 + (tB >> 8) >> 8;
1109
+ const tA = 255 * sa + da * invA;
1110
+ const a = tA + 1 + (tA >> 8) >> 8;
1043
1111
  return (a << 24 | b << 16 | g << 8 | r) >>> 0;
1044
1112
  };
1045
1113
  var overlayPerfect = (src, dst) => {
@@ -1052,10 +1120,15 @@ var overlayPerfect = (src, dst) => {
1052
1120
  const bb = db < 128 ? 2 * sb * db / 255 | 0 : 255 - (2 * (255 - sb) * (255 - db) / 255 | 0);
1053
1121
  if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
1054
1122
  const invA = 255 - sa;
1055
- const r = (br * sa + dr * invA) / 255 | 0;
1056
- const g = (bg * sa + dg * invA) / 255 | 0;
1057
- const b = (bb * sa + db * invA) / 255 | 0;
1058
- const a = (255 * sa + (dst >>> 24 & 255) * invA) / 255 | 0;
1123
+ const da = dst >>> 24 & 255;
1124
+ const tR = br * sa + dr * invA;
1125
+ const r = tR + 1 + (tR >> 8) >> 8;
1126
+ const tG = bg * sa + dg * invA;
1127
+ const g = tG + 1 + (tG >> 8) >> 8;
1128
+ const tB = bb * sa + db * invA;
1129
+ const b = tB + 1 + (tB >> 8) >> 8;
1130
+ const tA = 255 * sa + da * invA;
1131
+ const a = tA + 1 + (tA >> 8) >> 8;
1059
1132
  return (a << 24 | b << 16 | g << 8 | r) >>> 0;
1060
1133
  };
1061
1134
  var softLightPerfect = (src, dst) => {
@@ -1063,15 +1136,26 @@ var softLightPerfect = (src, dst) => {
1063
1136
  if (sa === 0) return dst;
1064
1137
  const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
1065
1138
  const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
1066
- const br = ((255 - dr) * (sr * dr / 255 | 0) + dr * (255 - ((255 - sr) * (255 - dr) / 255 | 0))) / 255 | 0;
1067
- const bg = ((255 - dg) * (sg * dg / 255 | 0) + dg * (255 - ((255 - sg) * (255 - dg) / 255 | 0))) / 255 | 0;
1068
- const bb = ((255 - db) * (sb * db / 255 | 0) + db * (255 - ((255 - sb) * (255 - db) / 255 | 0))) / 255 | 0;
1139
+ const mR = sr * dr;
1140
+ const scR = (255 - sr) * (255 - dr);
1141
+ const br = (255 - dr) * (mR + 1 + (mR >> 8) >> 8) + dr * (255 - (scR + 1 + (scR >> 8) >> 8)) + 1 + ((255 - dr) * (mR + 1 + (mR >> 8) >> 8) + dr * (255 - (scR + 1 + (scR >> 8) >> 8)) >> 8) >> 8;
1142
+ const mG = sg * dg;
1143
+ const scG = (255 - sg) * (255 - dg);
1144
+ const bg = (255 - dg) * (mG + 1 + (mG >> 8) >> 8) + dg * (255 - (scG + 1 + (scG >> 8) >> 8)) + 1 + ((255 - dg) * (mG + 1 + (mG >> 8) >> 8) + dg * (255 - (scG + 1 + (scG >> 8) >> 8)) >> 8) >> 8;
1145
+ const mB = sb * db;
1146
+ const scB = (255 - sb) * (255 - db);
1147
+ const bb = (255 - db) * (mB + 1 + (mB >> 8) >> 8) + db * (255 - (scB + 1 + (scB >> 8) >> 8)) + 1 + ((255 - db) * (mB + 1 + (mB >> 8) >> 8) + db * (255 - (scB + 1 + (scB >> 8) >> 8)) >> 8) >> 8;
1069
1148
  if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
1070
1149
  const invA = 255 - sa;
1071
- const r = (br * sa + dr * invA) / 255 | 0;
1072
- const g = (bg * sa + dg * invA) / 255 | 0;
1073
- const b = (bb * sa + db * invA) / 255 | 0;
1074
- const a = (255 * sa + (dst >>> 24 & 255) * invA) / 255 | 0;
1150
+ const da = dst >>> 24 & 255;
1151
+ const tR = br * sa + dr * invA;
1152
+ const r = tR + 1 + (tR >> 8) >> 8;
1153
+ const tG = bg * sa + dg * invA;
1154
+ const g = tG + 1 + (tG >> 8) >> 8;
1155
+ const tB = bb * sa + db * invA;
1156
+ const b = tB + 1 + (tB >> 8) >> 8;
1157
+ const tA = 255 * sa + da * invA;
1158
+ const a = tA + 1 + (tA >> 8) >> 8;
1075
1159
  return (a << 24 | b << 16 | g << 8 | r) >>> 0;
1076
1160
  };
1077
1161
  var hardLightPerfect = (src, dst) => {
@@ -1084,10 +1168,15 @@ var hardLightPerfect = (src, dst) => {
1084
1168
  const bb = sb < 128 ? 2 * sb * db / 255 | 0 : 255 - (2 * (255 - sb) * (255 - db) / 255 | 0);
1085
1169
  if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
1086
1170
  const invA = 255 - sa;
1087
- const r = (br * sa + dr * invA) / 255 | 0;
1088
- const g = (bg * sa + dg * invA) / 255 | 0;
1089
- const b = (bb * sa + db * invA) / 255 | 0;
1090
- const a = (255 * sa + (dst >>> 24 & 255) * invA) / 255 | 0;
1171
+ const da = dst >>> 24 & 255;
1172
+ const tR = br * sa + dr * invA;
1173
+ const r = tR + 1 + (tR >> 8) >> 8;
1174
+ const tG = bg * sa + dg * invA;
1175
+ const g = tG + 1 + (tG >> 8) >> 8;
1176
+ const tB = bb * sa + db * invA;
1177
+ const b = tB + 1 + (tB >> 8) >> 8;
1178
+ const tA = 255 * sa + da * invA;
1179
+ const a = tA + 1 + (tA >> 8) >> 8;
1091
1180
  return (a << 24 | b << 16 | g << 8 | r) >>> 0;
1092
1181
  };
1093
1182
  var vividLightPerfect = (src, dst) => {
@@ -1100,10 +1189,15 @@ var vividLightPerfect = (src, dst) => {
1100
1189
  const bb = sb < 128 ? sb === 0 ? 0 : Math.max(0, 255 - ((255 - db) * 255 / (2 * sb) | 0)) : sb === 255 ? 255 : Math.min(255, db * 255 / (2 * (255 - sb)) | 0);
1101
1190
  if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
1102
1191
  const invA = 255 - sa;
1103
- const r = (br * sa + dr * invA) / 255 | 0;
1104
- const g = (bg * sa + dg * invA) / 255 | 0;
1105
- const b = (bb * sa + db * invA) / 255 | 0;
1106
- const a = (255 * sa + (dst >>> 24 & 255) * invA) / 255 | 0;
1192
+ const da = dst >>> 24 & 255;
1193
+ const tR = br * sa + dr * invA;
1194
+ const r = tR + 1 + (tR >> 8) >> 8;
1195
+ const tG = bg * sa + dg * invA;
1196
+ const g = tG + 1 + (tG >> 8) >> 8;
1197
+ const tB = bb * sa + db * invA;
1198
+ const b = tB + 1 + (tB >> 8) >> 8;
1199
+ const tA = 255 * sa + da * invA;
1200
+ const a = tA + 1 + (tA >> 8) >> 8;
1107
1201
  return (a << 24 | b << 16 | g << 8 | r) >>> 0;
1108
1202
  };
1109
1203
  var linearLightPerfect = (src, dst) => {
@@ -1119,10 +1213,15 @@ var linearLightPerfect = (src, dst) => {
1119
1213
  const bb = bbU < 0 ? 0 : bbU > 255 ? 255 : bbU;
1120
1214
  if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
1121
1215
  const invA = 255 - sa;
1122
- const r = (br * sa + dr * invA) / 255 | 0;
1123
- const g = (bg * sa + dg * invA) / 255 | 0;
1124
- const b = (bb * sa + db * invA) / 255 | 0;
1125
- const a = (255 * sa + (dst >>> 24 & 255) * invA) / 255 | 0;
1216
+ const da = dst >>> 24 & 255;
1217
+ const tR = br * sa + dr * invA;
1218
+ const r = tR + 1 + (tR >> 8) >> 8;
1219
+ const tG = bg * sa + dg * invA;
1220
+ const g = tG + 1 + (tG >> 8) >> 8;
1221
+ const tB = bb * sa + db * invA;
1222
+ const b = tB + 1 + (tB >> 8) >> 8;
1223
+ const tA = 255 * sa + da * invA;
1224
+ const a = tA + 1 + (tA >> 8) >> 8;
1126
1225
  return (a << 24 | b << 16 | g << 8 | r) >>> 0;
1127
1226
  };
1128
1227
  var pinLightPerfect = (src, dst) => {
@@ -1140,10 +1239,14 @@ var pinLightPerfect = (src, dst) => {
1140
1239
  if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
1141
1240
  const invA = 255 - sa;
1142
1241
  const da = dst >>> 24 & 255;
1143
- const r = (br * sa + dr * invA + 128) / 255 | 0;
1144
- const g = (bg * sa + dg * invA + 128) / 255 | 0;
1145
- const b = (bb * sa + db * invA + 128) / 255 | 0;
1146
- const a = (255 * sa + da * invA + 128) / 255 | 0;
1242
+ const tR = br * sa + dr * invA;
1243
+ const r = tR + 1 + (tR >> 8) >> 8;
1244
+ const tG = bg * sa + dg * invA;
1245
+ const g = tG + 1 + (tG >> 8) >> 8;
1246
+ const tB = bb * sa + db * invA;
1247
+ const b = tB + 1 + (tB >> 8) >> 8;
1248
+ const tA = 255 * sa + da * invA;
1249
+ const a = tA + 1 + (tA >> 8) >> 8;
1147
1250
  return (a << 24 | b << 16 | g << 8 | r) >>> 0;
1148
1251
  };
1149
1252
  var hardMixPerfect = (src, dst) => {
@@ -1156,33 +1259,36 @@ var hardMixPerfect = (src, dst) => {
1156
1259
  const bb = (sb < 128 ? sb === 0 ? 0 : Math.max(0, 255 - ((255 - db) * 255 / (2 * sb) | 0)) : sb === 255 ? 255 : Math.min(255, db * 255 / (2 * (255 - sb)) | 0)) < 128 ? 0 : 255;
1157
1260
  if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
1158
1261
  const invA = 255 - sa;
1159
- const r = (br * sa + dr * invA) / 255 | 0;
1160
- const g = (bg * sa + dg * invA) / 255 | 0;
1161
- const b = (bb * sa + db * invA) / 255 | 0;
1162
- const a = (255 * sa + (dst >>> 24 & 255) * invA) / 255 | 0;
1262
+ const da = dst >>> 24 & 255;
1263
+ const tR = br * sa + dr * invA;
1264
+ const r = tR + 1 + (tR >> 8) >> 8;
1265
+ const tG = bg * sa + dg * invA;
1266
+ const g = tG + 1 + (tG >> 8) >> 8;
1267
+ const tB = bb * sa + db * invA;
1268
+ const b = tB + 1 + (tB >> 8) >> 8;
1269
+ const tA = 255 * sa + da * invA;
1270
+ const a = tA + 1 + (tA >> 8) >> 8;
1163
1271
  return (a << 24 | b << 16 | g << 8 | r) >>> 0;
1164
1272
  };
1165
1273
  var differencePerfect = (src, dst) => {
1166
1274
  const sa = src >>> 24 & 255;
1167
1275
  if (sa === 0) return dst;
1168
- const dr = dst & 255;
1169
- const dg = dst >>> 8 & 255;
1170
- const db = dst >>> 16 & 255;
1171
- const sr = src & 255;
1172
- const sg = src >>> 8 & 255;
1173
- const sb = src >>> 16 & 255;
1174
- const br = Math.abs(dr - sr);
1175
- const bg = Math.abs(dg - sg);
1176
- const bb = Math.abs(db - sb);
1177
- if (sa === 255) {
1178
- return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
1179
- }
1276
+ const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
1277
+ const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
1278
+ const br = dr > sr ? dr - sr : sr - dr;
1279
+ const bg = dg > sg ? dg - sg : sg - dg;
1280
+ const bb = db > sb ? db - sb : sb - db;
1281
+ if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
1180
1282
  const invA = 255 - sa;
1181
1283
  const da = dst >>> 24 & 255;
1182
- const r = (br * sa + dr * invA + 128) / 255 | 0;
1183
- const g = (bg * sa + dg * invA + 128) / 255 | 0;
1184
- const b = (bb * sa + db * invA + 128) / 255 | 0;
1185
- const a = (255 * sa + da * invA + 128) / 255 | 0;
1284
+ const tR = br * sa + dr * invA;
1285
+ const r = tR + 1 + (tR >> 8) >> 8;
1286
+ const tG = bg * sa + dg * invA;
1287
+ const g = tG + 1 + (tG >> 8) >> 8;
1288
+ const tB = bb * sa + db * invA;
1289
+ const b = tB + 1 + (tB >> 8) >> 8;
1290
+ const tA = 255 * sa + da * invA;
1291
+ const a = tA + 1 + (tA >> 8) >> 8;
1186
1292
  return (a << 24 | b << 16 | g << 8 | r) >>> 0;
1187
1293
  };
1188
1294
  var exclusionPerfect = (src, dst) => {
@@ -1194,18 +1300,23 @@ var exclusionPerfect = (src, dst) => {
1194
1300
  const sr = src & 255;
1195
1301
  const sg = src >>> 8 & 255;
1196
1302
  const sb = src >>> 16 & 255;
1197
- const br = dr + sr - (dr * sr >> 7);
1198
- const bg = dg + sg - (dg * sg >> 7);
1199
- const bb = db + sb - (db * sb >> 7);
1200
- if (sa === 255) {
1201
- return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
1202
- }
1303
+ const r2 = dr * sr;
1304
+ const br = dr + sr - (r2 + r2 + 1 + (r2 + r2 >> 8) >> 8);
1305
+ const g2 = dg * sg;
1306
+ const bg = dg + sg - (g2 + g2 + 1 + (g2 + g2 >> 8) >> 8);
1307
+ const b2 = db * sb;
1308
+ const bb = db + sb - (b2 + b2 + 1 + (b2 + b2 >> 8) >> 8);
1309
+ if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
1203
1310
  const invA = 255 - sa;
1204
1311
  const da = dst >>> 24 & 255;
1205
- const r = (br * sa + dr * invA) / 255 | 0;
1206
- const g = (bg * sa + dg * invA) / 255 | 0;
1207
- const b = (bb * sa + db * invA) / 255 | 0;
1208
- const a = (255 * sa + da * invA) / 255 | 0;
1312
+ const tR = br * sa + dr * invA;
1313
+ const r = tR + 1 + (tR >> 8) >> 8;
1314
+ const tG = bg * sa + dg * invA;
1315
+ const g = tG + 1 + (tG >> 8) >> 8;
1316
+ const tB = bb * sa + db * invA;
1317
+ const b = tB + 1 + (tB >> 8) >> 8;
1318
+ const tA = 255 * sa + da * invA;
1319
+ const a = tA + 1 + (tA >> 8) >> 8;
1209
1320
  return (a << 24 | b << 16 | g << 8 | r) >>> 0;
1210
1321
  };
1211
1322
  var subtractPerfect = (src, dst) => {
@@ -1221,10 +1332,15 @@ var subtractPerfect = (src, dst) => {
1221
1332
  const bb = bbU < 0 ? 0 : bbU;
1222
1333
  if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
1223
1334
  const invA = 255 - sa;
1224
- const r = (br * sa + dr * invA) / 255 | 0;
1225
- const g = (bg * sa + dg * invA) / 255 | 0;
1226
- const b = (bb * sa + db * invA) / 255 | 0;
1227
- const a = (255 * sa + (dst >>> 24 & 255) * invA) / 255 | 0;
1335
+ const da = dst >>> 24 & 255;
1336
+ const tR = br * sa + dr * invA;
1337
+ const r = tR + 1 + (tR >> 8) >> 8;
1338
+ const tG = bg * sa + dg * invA;
1339
+ const g = tG + 1 + (tG >> 8) >> 8;
1340
+ const tB = bb * sa + db * invA;
1341
+ const b = tB + 1 + (tB >> 8) >> 8;
1342
+ const tA = 255 * sa + da * invA;
1343
+ const a = tA + 1 + (tA >> 8) >> 8;
1228
1344
  return (a << 24 | b << 16 | g << 8 | r) >>> 0;
1229
1345
  };
1230
1346
  var dividePerfect = (src, dst) => {
@@ -1237,82 +1353,45 @@ var dividePerfect = (src, dst) => {
1237
1353
  const bb = sb === 0 ? 255 : Math.min(255, db * 255 / sb | 0);
1238
1354
  if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
1239
1355
  const invA = 255 - sa;
1240
- const r = (br * sa + dr * invA) / 255 | 0;
1241
- const g = (bg * sa + dg * invA) / 255 | 0;
1242
- const b = (bb * sa + db * invA) / 255 | 0;
1243
- const a = (255 * sa + (dst >>> 24 & 255) * invA) / 255 | 0;
1356
+ const da = dst >>> 24 & 255;
1357
+ const tR = br * sa + dr * invA;
1358
+ const r = tR + 1 + (tR >> 8) >> 8;
1359
+ const tG = bg * sa + dg * invA;
1360
+ const g = tG + 1 + (tG >> 8) >> 8;
1361
+ const tB = bb * sa + db * invA;
1362
+ const b = tB + 1 + (tB >> 8) >> 8;
1363
+ const tA = 255 * sa + da * invA;
1364
+ const a = tA + 1 + (tA >> 8) >> 8;
1244
1365
  return (a << 24 | b << 16 | g << 8 | r) >>> 0;
1245
1366
  };
1246
- var PERFECT_BLENDER_REGISTRY = [
1247
- [0 /* overwrite */, overwritePerfect],
1248
- [1 /* sourceOver */, sourceOverPerfect],
1249
- [2 /* darken */, darkenPerfect],
1250
- [3 /* multiply */, multiplyPerfect],
1251
- [4 /* colorBurn */, colorBurnPerfect],
1252
- [5 /* linearBurn */, linearBurnPerfect],
1253
- [6 /* darkerColor */, darkerPerfect],
1254
- [7 /* lighten */, lightenPerfect],
1255
- [8 /* screen */, screenPerfect],
1256
- [9 /* colorDodge */, colorDodgePerfect],
1257
- [10 /* linearDodge */, linearDodgePerfect],
1258
- [11 /* lighterColor */, lighterPerfect],
1259
- [12 /* overlay */, overlayPerfect],
1260
- [13 /* softLight */, softLightPerfect],
1261
- [14 /* hardLight */, hardLightPerfect],
1262
- [15 /* vividLight */, vividLightPerfect],
1263
- [16 /* linearLight */, linearLightPerfect],
1264
- [17 /* pinLight */, pinLightPerfect],
1265
- [18 /* hardMix */, hardMixPerfect],
1266
- [19 /* difference */, differencePerfect],
1267
- [20 /* exclusion */, exclusionPerfect],
1268
- [21 /* subtract */, subtractPerfect],
1269
- [22 /* divide */, dividePerfect]
1270
- ];
1271
- var PERFECT_BLEND_MODES = [];
1272
- for (const [index, blend] of PERFECT_BLENDER_REGISTRY) {
1273
- PERFECT_BLEND_MODES[index] = blend;
1274
- }
1275
- var PERFECT_BLEND_TO_INDEX = new Map(
1276
- PERFECT_BLENDER_REGISTRY.map((entry, index) => {
1277
- return [
1278
- entry[1],
1279
- index
1280
- ];
1281
- })
1282
- );
1283
- var INDEX_TO_PERFECT_BLEND = new Map(
1284
- PERFECT_BLENDER_REGISTRY.map((entry, index) => {
1285
- return [
1286
- index,
1287
- entry[1]
1288
- ];
1289
- })
1290
- );
1291
- var PERFECT_BLEND_MODE_BY_NAME = {
1292
- overwrite: overwritePerfect,
1293
- sourceOver: sourceOverPerfect,
1294
- darken: darkenPerfect,
1295
- multiply: multiplyPerfect,
1296
- colorBurn: colorBurnPerfect,
1297
- linearBurn: linearBurnPerfect,
1298
- darkerColor: darkerPerfect,
1299
- lighten: lightenPerfect,
1300
- screen: screenPerfect,
1301
- colorDodge: colorDodgePerfect,
1302
- linearDodge: linearDodgePerfect,
1303
- lighterColor: lighterPerfect,
1304
- overlay: overlayPerfect,
1305
- softLight: softLightPerfect,
1306
- hardLight: hardLightPerfect,
1307
- vividLight: vividLightPerfect,
1308
- linearLight: linearLightPerfect,
1309
- pinLight: pinLightPerfect,
1310
- hardMix: hardMixPerfect,
1311
- difference: differencePerfect,
1312
- exclusion: exclusionPerfect,
1313
- subtract: subtractPerfect,
1314
- divide: dividePerfect
1367
+ var BASE_PERFECT_BLEND_MODE_FUNCTIONS = {
1368
+ [BaseBlendMode.overwrite]: overwritePerfect,
1369
+ [BaseBlendMode.sourceOver]: sourceOverPerfect,
1370
+ [BaseBlendMode.darken]: darkenPerfect,
1371
+ [BaseBlendMode.multiply]: multiplyPerfect,
1372
+ [BaseBlendMode.colorBurn]: colorBurnPerfect,
1373
+ [BaseBlendMode.linearBurn]: linearBurnPerfect,
1374
+ [BaseBlendMode.darkerColor]: darkerPerfect,
1375
+ [BaseBlendMode.lighten]: lightenPerfect,
1376
+ [BaseBlendMode.screen]: screenPerfect,
1377
+ [BaseBlendMode.colorDodge]: colorDodgePerfect,
1378
+ [BaseBlendMode.linearDodge]: linearDodgePerfect,
1379
+ [BaseBlendMode.lighterColor]: lighterPerfect,
1380
+ [BaseBlendMode.overlay]: overlayPerfect,
1381
+ [BaseBlendMode.softLight]: softLightPerfect,
1382
+ [BaseBlendMode.hardLight]: hardLightPerfect,
1383
+ [BaseBlendMode.vividLight]: vividLightPerfect,
1384
+ [BaseBlendMode.linearLight]: linearLightPerfect,
1385
+ [BaseBlendMode.pinLight]: pinLightPerfect,
1386
+ [BaseBlendMode.hardMix]: hardMixPerfect,
1387
+ [BaseBlendMode.difference]: differencePerfect,
1388
+ [BaseBlendMode.exclusion]: exclusionPerfect,
1389
+ [BaseBlendMode.subtract]: subtractPerfect,
1390
+ [BaseBlendMode.divide]: dividePerfect
1315
1391
  };
1392
+ function makePerfectBlendModeRegistry() {
1393
+ return makeBlendModeRegistry(BaseBlendMode, BASE_PERFECT_BLEND_MODE_FUNCTIONS);
1394
+ }
1316
1395
 
1317
1396
  // src/Canvas/_constants.ts
1318
1397
  var OFFSCREEN_CANVAS_CTX_FAILED = "Failed to create OffscreenCanvas context";
@@ -1427,763 +1506,776 @@ async function writeImageDataToClipboard(imageData) {
1427
1506
  return writeImgBlobToClipboard(blob);
1428
1507
  }
1429
1508
 
1430
- // src/ImageData/ReusableImageData.ts
1431
- function makeReusableImageData() {
1432
- let imageData = null;
1433
- let buffer = null;
1434
- return function getReusableImageData(width, height) {
1435
- const hasInstance = !!imageData;
1436
- const widthMatches = hasInstance && imageData.width === width;
1437
- const heightMatches = hasInstance && imageData.height === height;
1438
- if (!widthMatches || !heightMatches) {
1439
- const buffer2 = new Uint8ClampedArray(width * height * 4);
1440
- imageData = new ImageData(buffer2, width, height);
1509
+ // src/History/PixelPatchTiles.ts
1510
+ var PixelTile = class {
1511
+ constructor(id, tx, ty, tileArea) {
1512
+ this.id = id;
1513
+ this.tx = tx;
1514
+ this.ty = ty;
1515
+ this.data32 = new Uint32Array(tileArea);
1516
+ }
1517
+ data32;
1518
+ };
1519
+ function applyPatchTiles(target, tiles, tileSize = 256) {
1520
+ for (let i = 0; i < tiles.length; i++) {
1521
+ const tile = tiles[i];
1522
+ if (!tile) continue;
1523
+ const dst = target.data32;
1524
+ const src = tile.data32;
1525
+ const dstWidth = target.width;
1526
+ const dstHeight = target.height;
1527
+ const startX = tile.tx * tileSize;
1528
+ const startY = tile.ty * tileSize;
1529
+ const copyWidth = Math.max(0, Math.min(tileSize, dstWidth - startX));
1530
+ if (copyWidth <= 0) return;
1531
+ for (let ly = 0; ly < tileSize; ly++) {
1532
+ const globalY = startY + ly;
1533
+ if (globalY >= dstHeight) break;
1534
+ const dstIndex = globalY * dstWidth + startX;
1535
+ const srcIndex = ly * tileSize;
1536
+ const rowData = src.subarray(srcIndex, srcIndex + copyWidth);
1537
+ dst.set(rowData, dstIndex);
1441
1538
  }
1442
- return imageData;
1443
- };
1444
- }
1445
-
1446
- // src/ImageData/copyImageData.ts
1447
- function copyImageData({ data, width, height }) {
1448
- return new ImageData(data.slice(), width, height);
1449
- }
1450
- function copyImageDataLike({ data, width, height }) {
1451
- return {
1452
- data: data.slice(),
1453
- width,
1454
- height
1455
- };
1456
- }
1457
-
1458
- // src/PixelData/pixelDataToAlphaMask.ts
1459
- function pixelDataToAlphaMask(pixelData) {
1460
- const {
1461
- data32,
1462
- width,
1463
- height
1464
- } = pixelData;
1465
- const len = data32.length;
1466
- const mask = new Uint8Array(width * height);
1467
- for (let i = 0; i < len; i++) {
1468
- const val = data32[i];
1469
- mask[i] = val >>> 24 & 255;
1470
1539
  }
1471
- return mask;
1472
1540
  }
1473
1541
 
1474
- // src/ImageData/imageDataToAlphaMask.ts
1475
- function imageDataToAlphaMask(imageData) {
1476
- const {
1477
- width,
1478
- height,
1479
- data
1480
- } = imageData;
1481
- const data32 = new Uint32Array(
1482
- data.buffer,
1483
- data.byteOffset,
1484
- data.byteLength >> 2
1485
- );
1486
- const len = data32.length;
1487
- const mask = new Uint8Array(width * height);
1488
- for (let i = 0; i < len; i++) {
1489
- const val = data32[i];
1490
- mask[i] = val >>> 24 & 255;
1542
+ // src/History/PixelAccumulator.ts
1543
+ var PixelAccumulator = class {
1544
+ constructor(target, config) {
1545
+ this.target = target;
1546
+ this.config = config;
1547
+ this.lookup = [];
1548
+ this.beforeTiles = [];
1549
+ this.pool = [];
1550
+ }
1551
+ lookup;
1552
+ beforeTiles;
1553
+ pool;
1554
+ getTile(id, tx, ty) {
1555
+ let tile = this.pool.pop();
1556
+ if (tile) {
1557
+ tile.id = id;
1558
+ tile.tx = tx;
1559
+ tile.ty = ty;
1560
+ return tile;
1561
+ }
1562
+ return new PixelTile(
1563
+ id,
1564
+ tx,
1565
+ ty,
1566
+ this.config.tileArea
1567
+ );
1491
1568
  }
1492
- return mask;
1493
- }
1494
-
1495
- // src/ImageData/imageDataToDataUrl.ts
1496
- var get = makeReusableCanvas();
1497
- function imageDataToDataUrl(imageData) {
1498
- const { canvas, ctx } = get(imageData.width, imageData.height);
1499
- ctx.putImageData(imageData, 0, 0);
1500
- return canvas.toDataURL();
1501
- }
1502
- imageDataToDataUrl.reset = get.reset;
1503
-
1504
- // src/ImageData/imageDataToUInt32Array.ts
1505
- function imageDataToUInt32Array(imageData) {
1506
- return new Uint32Array(
1507
- imageData.data.buffer,
1508
- imageData.data.byteOffset,
1509
- // Shift right by 2 is a fast bitwise division by 4.
1510
- imageData.data.byteLength >> 2
1511
- );
1512
- }
1513
-
1514
- // src/ImageData/invertImageData.ts
1515
- function invertImageData(imageData) {
1516
- const data = imageData.data;
1517
- let length = data.length;
1518
- for (let i = 0; i < length; i += 4) {
1519
- data[i] = 255 - data[i];
1520
- data[i + 1] = 255 - data[i + 1];
1521
- data[i + 2] = 255 - data[i + 2];
1569
+ recyclePatch(patch) {
1570
+ const before = patch.beforeTiles;
1571
+ for (let i = 0; i < before.length; i++) {
1572
+ let tile = before[i];
1573
+ if (tile) {
1574
+ this.pool.push(tile);
1575
+ }
1576
+ }
1577
+ const after = patch.afterTiles;
1578
+ for (let i = 0; i < after.length; i++) {
1579
+ let tile = after[i];
1580
+ if (tile) {
1581
+ this.pool.push(tile);
1582
+ }
1583
+ }
1584
+ }
1585
+ /**
1586
+ * @param x pixel x coordinate
1587
+ * @param y pixel y coordinate
1588
+ */
1589
+ storeTileBeforeState(x, y) {
1590
+ let target = this.target;
1591
+ let shift = this.config.tileShift;
1592
+ let columns = target.width + this.config.tileMask >> shift;
1593
+ let tx = x >> shift;
1594
+ let ty = y >> shift;
1595
+ let id = ty * columns + tx;
1596
+ let tile = this.lookup[id];
1597
+ if (!tile) {
1598
+ tile = this.getTile(
1599
+ id,
1600
+ tx,
1601
+ ty
1602
+ );
1603
+ this.extractState(tile);
1604
+ this.lookup[id] = tile;
1605
+ this.beforeTiles.push(tile);
1606
+ }
1607
+ }
1608
+ /**
1609
+ *
1610
+ * @param x pixel x coordinate
1611
+ * @param y pixel y coordinate
1612
+ * @param w pixel width
1613
+ * @param h pixel height
1614
+ */
1615
+ storeRegionBeforeState(x, y, w, h) {
1616
+ let target = this.target;
1617
+ let shift = this.config.tileShift;
1618
+ let columns = target.width + this.config.tileMask >> shift;
1619
+ let startX = x >> shift;
1620
+ let startY = y >> shift;
1621
+ let endX = x + w - 1 >> shift;
1622
+ let endY = y + h - 1 >> shift;
1623
+ for (let ty = startY; ty <= endY; ty++) {
1624
+ for (let tx = startX; tx <= endX; tx++) {
1625
+ let id = ty * columns + tx;
1626
+ let tile = this.lookup[id];
1627
+ if (!tile) {
1628
+ tile = this.getTile(
1629
+ id,
1630
+ tx,
1631
+ ty
1632
+ );
1633
+ this.extractState(tile);
1634
+ this.lookup[id] = tile;
1635
+ this.beforeTiles.push(tile);
1636
+ }
1637
+ }
1638
+ }
1639
+ }
1640
+ extractState(tile) {
1641
+ let target = this.target;
1642
+ let TILE_SIZE = this.config.tileSize;
1643
+ let dst = tile.data32;
1644
+ let src = target.data32;
1645
+ let startX = tile.tx * TILE_SIZE;
1646
+ let startY = tile.ty * TILE_SIZE;
1647
+ let targetWidth = target.width;
1648
+ let targetHeight = target.height;
1649
+ let copyWidth = Math.max(0, Math.min(TILE_SIZE, targetWidth - startX));
1650
+ for (let ly = 0; ly < TILE_SIZE; ly++) {
1651
+ let globalY = startY + ly;
1652
+ let dstIndex = ly * TILE_SIZE;
1653
+ if (globalY < 0 || globalY >= targetHeight || copyWidth === 0) {
1654
+ dst.fill(0, dstIndex, dstIndex + TILE_SIZE);
1655
+ continue;
1656
+ }
1657
+ let srcIndex = globalY * targetWidth + startX;
1658
+ let rowData = src.subarray(srcIndex, srcIndex + copyWidth);
1659
+ dst.set(rowData, dstIndex);
1660
+ if (copyWidth < TILE_SIZE) {
1661
+ dst.fill(0, dstIndex + copyWidth, dstIndex + TILE_SIZE);
1662
+ }
1663
+ }
1664
+ }
1665
+ extractAfterTiles() {
1666
+ let afterTiles = [];
1667
+ let length = this.beforeTiles.length;
1668
+ for (let i = 0; i < length; i++) {
1669
+ let beforeTile = this.beforeTiles[i];
1670
+ if (beforeTile) {
1671
+ let afterTile = this.getTile(
1672
+ beforeTile.id,
1673
+ beforeTile.tx,
1674
+ beforeTile.ty
1675
+ );
1676
+ this.extractState(afterTile);
1677
+ afterTiles.push(afterTile);
1678
+ }
1679
+ }
1680
+ return afterTiles;
1681
+ }
1682
+ reset() {
1683
+ this.lookup = [];
1684
+ this.beforeTiles = [];
1522
1685
  }
1523
- return imageData;
1524
- }
1525
-
1526
- // src/Internal/resample32.ts
1527
- var resample32Scratch = {
1528
- data: null,
1529
- width: 0,
1530
- height: 0
1531
1686
  };
1532
- function resample32(srcData32, srcW, srcH, factor) {
1533
- const dstW = Math.max(1, srcW * factor | 0);
1534
- const dstH = Math.max(1, srcH * factor | 0);
1535
- const dstData = new Int32Array(dstW * dstH);
1536
- const scaleX = srcW / dstW;
1537
- const scaleY = srcH / dstH;
1538
- for (let y = 0; y < dstH; y++) {
1539
- const srcY = Math.min(srcH - 1, y * scaleY | 0);
1540
- const srcRowOffset = srcY * srcW;
1541
- const dstRowOffset = y * dstW;
1542
- for (let x = 0; x < dstW; x++) {
1543
- const srcX = Math.min(srcW - 1, x * scaleX | 0);
1544
- dstData[dstRowOffset + x] = srcData32[srcRowOffset + srcX];
1687
+
1688
+ // src/History/HistoryManager.ts
1689
+ var HistoryManager = class {
1690
+ constructor(maxSteps = 50) {
1691
+ this.maxSteps = maxSteps;
1692
+ this.undoStack = [];
1693
+ this.redoStack = [];
1694
+ this.listeners = /* @__PURE__ */ new Set();
1695
+ }
1696
+ undoStack;
1697
+ redoStack;
1698
+ listeners;
1699
+ get canUndo() {
1700
+ return this.undoStack.length > 0;
1701
+ }
1702
+ get canRedo() {
1703
+ return this.redoStack.length > 0;
1704
+ }
1705
+ subscribe(fn) {
1706
+ this.listeners.add(fn);
1707
+ return () => this.listeners.delete(fn);
1708
+ }
1709
+ notify() {
1710
+ this.listeners.forEach((fn) => fn());
1711
+ }
1712
+ commit(action) {
1713
+ this.undoStack.push(action);
1714
+ this.clearRedoStack();
1715
+ if (this.undoStack.length > this.maxSteps) {
1716
+ this.undoStack.shift()?.dispose?.();
1717
+ }
1718
+ this.notify();
1719
+ }
1720
+ undo() {
1721
+ let action = this.undoStack.pop();
1722
+ if (!action) return;
1723
+ this.redoStack.push(action);
1724
+ action.undo();
1725
+ this.notify();
1726
+ }
1727
+ redo() {
1728
+ let action = this.redoStack.pop();
1729
+ if (!action) return;
1730
+ this.undoStack.push(action);
1731
+ action.redo();
1732
+ this.notify();
1733
+ }
1734
+ clearRedoStack() {
1735
+ let length = this.redoStack.length;
1736
+ for (let i = 0; i < length; i++) {
1737
+ let action = this.redoStack[i];
1738
+ if (action) {
1739
+ action.dispose?.();
1740
+ }
1545
1741
  }
1742
+ this.redoStack.length = 0;
1546
1743
  }
1547
- resample32Scratch.data = dstData;
1548
- resample32Scratch.width = dstW;
1549
- resample32Scratch.height = dstH;
1550
- return resample32Scratch;
1551
- }
1744
+ };
1552
1745
 
1553
- // src/ImageData/resampleImageData.ts
1554
- function resampleImageData(source, factor) {
1555
- const src32 = new Uint32Array(source.data.buffer);
1556
- const { data, width, height } = resample32(src32, source.width, source.height, factor);
1557
- const uint8ClampedArray = new Uint8ClampedArray(data.buffer);
1558
- return new ImageData(uint8ClampedArray, width, height);
1559
- }
1746
+ // src/History/PixelEngineConfig.ts
1747
+ var PixelEngineConfig = class {
1748
+ tileSize;
1749
+ tileShift;
1750
+ tileMask;
1751
+ tileArea;
1752
+ constructor(tileSize = 256) {
1753
+ if ((tileSize & tileSize - 1) !== 0) {
1754
+ throw new Error("tileSize must be a power of 2");
1755
+ }
1756
+ this.tileSize = tileSize;
1757
+ this.tileShift = Math.log2(tileSize);
1758
+ this.tileMask = tileSize - 1;
1759
+ this.tileArea = tileSize * tileSize;
1760
+ }
1761
+ };
1560
1762
 
1561
- // src/ImageData/resizeImageData.ts
1562
- function resizeImageData(current, newWidth, newHeight, offsetX = 0, offsetY = 0) {
1563
- const result = new ImageData(newWidth, newHeight);
1763
+ // src/PixelData/applyMaskToPixelData.ts
1764
+ function applyMaskToPixelData(dst, mask, opts = {}) {
1564
1765
  const {
1565
- width: oldW,
1566
- height: oldH,
1567
- data: oldData
1568
- } = current;
1569
- const newData = result.data;
1570
- const x0 = Math.max(0, offsetX);
1571
- const y0 = Math.max(0, offsetY);
1572
- const x1 = Math.min(newWidth, offsetX + oldW);
1573
- const y1 = Math.min(newHeight, offsetY + oldH);
1574
- if (x1 <= x0 || y1 <= y0) {
1575
- return result;
1576
- }
1577
- const rowCount = y1 - y0;
1578
- const rowLen = (x1 - x0) * 4;
1579
- for (let row = 0; row < rowCount; row++) {
1580
- const dstY = y0 + row;
1581
- const srcY = dstY - offsetY;
1582
- const srcX = x0 - offsetX;
1583
- const dstStart = (dstY * newWidth + x0) * 4;
1584
- const srcStart = (srcY * oldW + srcX) * 4;
1585
- newData.set(
1586
- oldData.subarray(srcStart, srcStart + rowLen),
1587
- dstStart
1588
- );
1766
+ x: targetX = 0,
1767
+ y: targetY = 0,
1768
+ w: width = dst.width,
1769
+ h: height = dst.height,
1770
+ alpha: globalAlpha = 255,
1771
+ maskType = 0 /* ALPHA */,
1772
+ mw,
1773
+ mx = 0,
1774
+ my = 0,
1775
+ invertMask = false
1776
+ } = opts;
1777
+ let x = targetX;
1778
+ let y = targetY;
1779
+ let w = width;
1780
+ let h = height;
1781
+ if (x < 0) {
1782
+ w += x;
1783
+ x = 0;
1589
1784
  }
1590
- return result;
1591
- }
1592
-
1593
- // src/ImageData/serialization.ts
1594
- function base64EncodeArrayBuffer(buffer) {
1595
- const uint8 = new Uint8Array(buffer);
1596
- const decoder = new TextDecoder("latin1");
1597
- const binary = decoder.decode(uint8);
1598
- return btoa(binary);
1599
- }
1600
- function base64DecodeArrayBuffer(encoded) {
1601
- const binary = atob(encoded);
1602
- const bytes = new Uint8ClampedArray(binary.length);
1603
- for (let i = 0; i < binary.length; i++) {
1604
- bytes[i] = binary.charCodeAt(i);
1785
+ if (y < 0) {
1786
+ h += y;
1787
+ y = 0;
1605
1788
  }
1606
- return bytes;
1607
- }
1608
- function serializeImageData(imageData) {
1609
- return {
1610
- width: imageData.width,
1611
- height: imageData.height,
1612
- data: base64EncodeArrayBuffer(imageData.data.buffer)
1613
- };
1614
- }
1615
- function serializeNullableImageData(imageData) {
1616
- if (!imageData) return null;
1617
- return serializeImageData(imageData);
1618
- }
1619
- function deserializeRawImageData(serialized) {
1620
- return {
1621
- width: serialized.width,
1622
- height: serialized.height,
1623
- data: base64DecodeArrayBuffer(serialized.data)
1624
- };
1625
- }
1626
- function deserializeImageData(serialized) {
1627
- const data = base64DecodeArrayBuffer(serialized.data);
1628
- return new ImageData(data, serialized.width, serialized.height);
1629
- }
1630
- function deserializeNullableImageData(serialized) {
1631
- if (!serialized) return null;
1632
- return deserializeImageData(serialized);
1633
- }
1634
-
1635
- // src/ImageData/writeImageData.ts
1636
- function writeImageData(target, source, x, y, sx = 0, sy = 0, sw = source.width, sh = source.height, mask = null, maskType = 1 /* BINARY */) {
1637
- const dstW = target.width;
1638
- const dstH = target.height;
1639
- const dstData = target.data;
1640
- const srcW = source.width;
1641
- const srcData = source.data;
1642
- const x0 = Math.max(0, x);
1643
- const y0 = Math.max(0, y);
1644
- const x1 = Math.min(dstW, x + sw);
1645
- const y1 = Math.min(dstH, y + sh);
1646
- if (x1 <= x0 || y1 <= y0) {
1789
+ const actualW = Math.min(w, dst.width - x);
1790
+ const actualH = Math.min(h, dst.height - y);
1791
+ if (actualW <= 0 || actualH <= 0 || globalAlpha === 0) {
1647
1792
  return;
1648
1793
  }
1649
- const useMask = !!mask;
1650
- const rowCount = y1 - y0;
1651
- const rowLenPixels = x1 - x0;
1652
- for (let row = 0; row < rowCount; row++) {
1653
- const dstY = y0 + row;
1654
- const srcY = sy + (dstY - y);
1655
- const srcXBase = sx + (x0 - x);
1656
- const dstStart = (dstY * dstW + x0) * 4;
1657
- const srcStart = (srcY * srcW + srcXBase) * 4;
1658
- if (useMask && mask) {
1659
- for (let ix = 0; ix < rowLenPixels; ix++) {
1660
- const mi = srcY * srcW + (srcXBase + ix);
1661
- const alpha = mask[mi];
1662
- if (alpha === 0) {
1794
+ const dst32 = dst.data32;
1795
+ const dw = dst.width;
1796
+ const mPitch = mw ?? width;
1797
+ const isAlpha = maskType === 0 /* ALPHA */;
1798
+ const dx = x - targetX;
1799
+ const dy = y - targetY;
1800
+ let dIdx = y * dw + x;
1801
+ let mIdx = (my + dy) * mPitch + (mx + dx);
1802
+ const dStride = dw - actualW;
1803
+ const mStride = mPitch - actualW;
1804
+ for (let iy = 0; iy < actualH; iy++) {
1805
+ for (let ix = 0; ix < actualW; ix++) {
1806
+ const mVal = mask[mIdx];
1807
+ let weight = globalAlpha;
1808
+ if (isAlpha) {
1809
+ const effectiveM = invertMask ? 255 - mVal : mVal;
1810
+ if (effectiveM === 0) {
1811
+ dst32[dIdx] = (dst32[dIdx] & 16777215) >>> 0;
1812
+ dIdx++;
1813
+ mIdx++;
1663
1814
  continue;
1664
1815
  }
1665
- const di = dstStart + ix * 4;
1666
- const si = srcStart + ix * 4;
1667
- if (maskType === 1 /* BINARY */ || alpha === 255) {
1668
- dstData[di] = srcData[si];
1669
- dstData[di + 1] = srcData[si + 1];
1670
- dstData[di + 2] = srcData[si + 2];
1671
- dstData[di + 3] = srcData[si + 3];
1816
+ weight = globalAlpha === 255 ? effectiveM : effectiveM * globalAlpha + 128 >> 8;
1817
+ } else {
1818
+ const isHit = invertMask ? mVal === 0 : mVal === 1;
1819
+ if (!isHit) {
1820
+ dst32[dIdx] = (dst32[dIdx] & 16777215) >>> 0;
1821
+ dIdx++;
1822
+ mIdx++;
1823
+ continue;
1824
+ }
1825
+ weight = globalAlpha;
1826
+ }
1827
+ if (weight === 0) {
1828
+ dst32[dIdx] = (dst32[dIdx] & 16777215) >>> 0;
1829
+ } else {
1830
+ const d = dst32[dIdx];
1831
+ const da = d >>> 24;
1832
+ let finalAlpha = da;
1833
+ if (da === 0) {
1834
+ } else if (weight === 255) {
1835
+ } else if (da === 255) {
1836
+ finalAlpha = weight;
1672
1837
  } else {
1673
- const a = alpha / 255;
1674
- const invA = 1 - a;
1675
- dstData[di] = srcData[si] * a + dstData[di] * invA;
1676
- dstData[di + 1] = srcData[si + 1] * a + dstData[di + 1] * invA;
1677
- dstData[di + 2] = srcData[si + 2] * a + dstData[di + 2] * invA;
1678
- dstData[di + 3] = srcData[si + 3] * a + dstData[di + 3] * invA;
1838
+ finalAlpha = da * weight + 128 >> 8;
1679
1839
  }
1840
+ dst32[dIdx] = (d & 16777215 | finalAlpha << 24) >>> 0;
1680
1841
  }
1681
- } else {
1682
- const byteLen = rowLenPixels * 4;
1683
- const sub = srcData.subarray(srcStart, srcStart + byteLen);
1684
- dstData.set(sub, dstStart);
1842
+ dIdx++;
1843
+ mIdx++;
1685
1844
  }
1845
+ dIdx += dStride;
1846
+ mIdx += mStride;
1686
1847
  }
1687
1848
  }
1688
1849
 
1689
- // src/ImageData/writeImageDataPixels.ts
1690
- function writeImageDataPixels(imageData, data, _x, _y, _w, _h) {
1691
- const { x, y, w, h } = typeof _x === "object" ? _x : { x: _x, y: _y, w: _w, h: _h };
1692
- const { width: dstW, height: dstH, data: dst } = imageData;
1693
- const x0 = Math.max(0, x);
1694
- const y0 = Math.max(0, y);
1695
- const x1 = Math.min(dstW, x + w);
1696
- const y1 = Math.min(dstH, y + h);
1697
- if (x1 <= x0 || y1 <= y0) return;
1698
- const rowLen = (x1 - x0) * 4;
1699
- const srcCol = x0 - x;
1700
- const srcYOffset = y0 - y;
1701
- const actualH = y1 - y0;
1702
- for (let row = 0; row < actualH; row++) {
1703
- const dstStart = ((y0 + row) * dstW + x0) * 4;
1704
- const srcRow = srcYOffset + row;
1705
- const o = (srcRow * w + srcCol) * 4;
1706
- dst.set(data.subarray(o, o + rowLen), dstStart);
1850
+ // src/History/PixelWriter.ts
1851
+ var PixelWriter = class {
1852
+ target;
1853
+ historyManager;
1854
+ accumulator;
1855
+ config;
1856
+ mutator;
1857
+ constructor(target, mutatorFactory, {
1858
+ tileSize = 256,
1859
+ maxHistorySteps = 50,
1860
+ historyManager = new HistoryManager(maxHistorySteps)
1861
+ } = {}) {
1862
+ this.target = target;
1863
+ this.config = new PixelEngineConfig(tileSize);
1864
+ this.historyManager = historyManager;
1865
+ this.accumulator = new PixelAccumulator(target, this.config);
1866
+ this.mutator = mutatorFactory(this);
1867
+ }
1868
+ withHistory(cb) {
1869
+ cb(this.mutator);
1870
+ const beforeTiles = this.accumulator.beforeTiles;
1871
+ if (beforeTiles.length === 0) return;
1872
+ const afterTiles = this.accumulator.extractAfterTiles();
1873
+ const patch = {
1874
+ beforeTiles,
1875
+ afterTiles
1876
+ };
1877
+ const target = this.target;
1878
+ const tileSize = this.config.tileSize;
1879
+ const accumulator = this.accumulator;
1880
+ const action = {
1881
+ undo: () => applyPatchTiles(target, patch.beforeTiles, tileSize),
1882
+ redo: () => applyPatchTiles(target, patch.afterTiles, tileSize),
1883
+ dispose: () => accumulator.recyclePatch(patch)
1884
+ };
1885
+ this.historyManager.commit(action);
1886
+ this.accumulator.reset();
1707
1887
  }
1888
+ };
1889
+
1890
+ // src/History/PixelMutator/mutatorApplyMask.ts
1891
+ function mutatorApplyMask(writer) {
1892
+ return {
1893
+ applyMask: (mask, opts = {}) => {
1894
+ let target = writer.target;
1895
+ const {
1896
+ x = 0,
1897
+ y = 0,
1898
+ w = writer.target.width,
1899
+ h = writer.target.height
1900
+ } = opts;
1901
+ writer.accumulator.storeRegionBeforeState(x, y, w, h);
1902
+ applyMaskToPixelData(target, mask, opts);
1903
+ }
1904
+ };
1708
1905
  }
1709
1906
 
1710
- // src/IndexedImage/IndexedImage.ts
1711
- var IndexedImage = class _IndexedImage {
1712
- /** The width of the image in pixels. */
1713
- width;
1714
- /** The height of the image in pixels. */
1715
- height;
1716
- /** Flat array of palette indices. Index = x + (y * width). */
1717
- data;
1718
- /** The palette of unique 32-bit colors (ABGR/RGBA packed) found in the image. */
1719
- palette;
1720
- /** The specific index in the palette reserved for fully transparent pixels. */
1721
- transparentPalletIndex;
1722
- /**
1723
- * @param width - Image width.
1724
- * @param height - Image height.
1725
- * @param data - The indexed pixel data.
1726
- * @param palette - The array of packed colors.
1727
- * @param transparentPalletIndex - The index representing alpha 0.
1728
- */
1729
- constructor(width, height, data, palette, transparentPalletIndex) {
1730
- this.width = width;
1731
- this.height = height;
1732
- this.data = data;
1733
- this.palette = palette;
1734
- this.transparentPalletIndex = transparentPalletIndex;
1907
+ // src/PixelData/blendColorPixelData.ts
1908
+ function blendColorPixelData(dst, color, opts = {}) {
1909
+ const {
1910
+ x: targetX = 0,
1911
+ y: targetY = 0,
1912
+ w: width = dst.width,
1913
+ h: height = dst.height,
1914
+ alpha: globalAlpha = 255,
1915
+ blendFn = sourceOverFast,
1916
+ mask,
1917
+ maskType = 0 /* ALPHA */,
1918
+ mw,
1919
+ mx = 0,
1920
+ my = 0,
1921
+ invertMask = false
1922
+ } = opts;
1923
+ if (globalAlpha === 0) return;
1924
+ const baseSrcAlpha = color >>> 24;
1925
+ const isOverwrite = blendFn.isOverwrite;
1926
+ if (baseSrcAlpha === 0 && !isOverwrite) return;
1927
+ let x = targetX;
1928
+ let y = targetY;
1929
+ let w = width;
1930
+ let h = height;
1931
+ if (x < 0) {
1932
+ w += x;
1933
+ x = 0;
1735
1934
  }
1736
- /**
1737
- * Creates an IndexedImage from standard browser ImageData.
1738
- * @param imageData - The source ImageData to convert.
1739
- * @returns A new IndexedImage instance.
1740
- */
1741
- static fromImageData(imageData) {
1742
- return _IndexedImage.fromRaw(imageData.data, imageData.width, imageData.height);
1935
+ if (y < 0) {
1936
+ h += y;
1937
+ y = 0;
1743
1938
  }
1744
- /**
1745
- * Creates an IndexedImage from a raw byte buffer and dimensions.
1746
- * Any pixel with an alpha channel of 0 is normalized to the transparent palette index.
1747
- * @param data - Raw RGBA byte data.
1748
- * @param width - Image width.
1749
- * @param height - Image height.
1750
- * @returns A new IndexedImage instance.
1751
- */
1752
- static fromRaw(data, width, height) {
1753
- const buffer = data.buffer;
1754
- const rawData = new Uint32Array(buffer);
1755
- const indexedData = new Int32Array(rawData.length);
1756
- const colorMap = /* @__PURE__ */ new Map();
1757
- const transparentColor = 0;
1758
- const transparentPalletIndex = 0;
1759
- colorMap.set(transparentColor, transparentPalletIndex);
1760
- for (let i = 0; i < rawData.length; i++) {
1761
- const pixel = rawData[i];
1762
- const alpha = pixel >>> 24 & 255;
1763
- const isTransparent = alpha === 0;
1764
- const colorKey = isTransparent ? transparentColor : pixel >>> 0;
1765
- let id = colorMap.get(colorKey);
1766
- if (id === void 0) {
1767
- id = colorMap.size;
1768
- colorMap.set(colorKey, id);
1939
+ const actualW = Math.min(w, dst.width - x);
1940
+ const actualH = Math.min(h, dst.height - y);
1941
+ if (actualW <= 0 || actualH <= 0) return;
1942
+ const dst32 = dst.data32;
1943
+ const dw = dst.width;
1944
+ const mPitch = mw ?? width;
1945
+ const isAlphaMask = maskType === 0 /* ALPHA */;
1946
+ const dx = x - targetX;
1947
+ const dy = y - targetY;
1948
+ let dIdx = y * dw + x;
1949
+ let mIdx = (my + dy) * mPitch + (mx + dx);
1950
+ const dStride = dw - actualW;
1951
+ const mStride = mPitch - actualW;
1952
+ for (let iy = 0; iy < actualH; iy++) {
1953
+ for (let ix = 0; ix < actualW; ix++) {
1954
+ let weight = globalAlpha;
1955
+ if (mask) {
1956
+ const mVal = mask[mIdx];
1957
+ if (isAlphaMask) {
1958
+ const effectiveM = invertMask ? 255 - mVal : mVal;
1959
+ if (effectiveM === 0) {
1960
+ dIdx++;
1961
+ mIdx++;
1962
+ continue;
1963
+ }
1964
+ if (globalAlpha === 255) {
1965
+ weight = effectiveM;
1966
+ } else if (effectiveM === 255) {
1967
+ weight = globalAlpha;
1968
+ } else {
1969
+ weight = effectiveM * globalAlpha + 128 >> 8;
1970
+ }
1971
+ } else {
1972
+ const isHit = invertMask ? mVal === 0 : mVal === 1;
1973
+ if (!isHit) {
1974
+ dIdx++;
1975
+ mIdx++;
1976
+ continue;
1977
+ }
1978
+ weight = globalAlpha;
1979
+ }
1980
+ if (weight === 0) {
1981
+ dIdx++;
1982
+ mIdx++;
1983
+ continue;
1984
+ }
1769
1985
  }
1770
- indexedData[i] = id;
1986
+ let currentSrcColor = color;
1987
+ if (weight < 255) {
1988
+ let currentSrcAlpha = baseSrcAlpha;
1989
+ if (baseSrcAlpha === 255) {
1990
+ currentSrcAlpha = weight;
1991
+ } else {
1992
+ currentSrcAlpha = baseSrcAlpha * weight + 128 >> 8;
1993
+ }
1994
+ if (!isOverwrite && currentSrcAlpha === 0) {
1995
+ dIdx++;
1996
+ mIdx++;
1997
+ continue;
1998
+ }
1999
+ currentSrcColor = (color & 16777215 | currentSrcAlpha << 24) >>> 0;
2000
+ }
2001
+ dst32[dIdx] = blendFn(currentSrcColor, dst32[dIdx]);
2002
+ dIdx++;
2003
+ mIdx++;
1771
2004
  }
1772
- const palette = Uint32Array.from(colorMap.keys());
1773
- return new _IndexedImage(
1774
- width,
1775
- height,
1776
- indexedData,
1777
- palette,
1778
- transparentPalletIndex
1779
- );
1780
- }
1781
- /**
1782
- * Retrieves the 32-bit packed color value at the given coordinates.
1783
- * @param x - X coordinate.
1784
- * @param y - Y coordinate.
1785
- * @returns The packed color from the palette.
1786
- */
1787
- getColorAt(x, y) {
1788
- const index = x + y * this.width;
1789
- const paletteIndex = this.data[index];
1790
- return this.palette[paletteIndex];
1791
- }
1792
- };
1793
-
1794
- // src/IndexedImage/getIndexedImageColorCounts.ts
1795
- function getIndexedImageColorCounts(indexedImage) {
1796
- const data = indexedImage.data;
1797
- const palette = indexedImage.palette;
1798
- const frequencies = new Int32Array(palette.length);
1799
- for (let i = 0; i < data.length; i++) {
1800
- const colorIndex = data[i];
1801
- frequencies[colorIndex]++;
2005
+ dIdx += dStride;
2006
+ mIdx += mStride;
1802
2007
  }
1803
- return frequencies;
1804
2008
  }
1805
2009
 
1806
- // src/IndexedImage/indexedImageToAverageColor.ts
1807
- function indexedImageToAverageColor(indexedImage, includeTransparent = false) {
1808
- const { data, palette, transparentPalletIndex } = indexedImage;
1809
- const counts = new Uint32Array(palette.length);
1810
- for (let i = 0; i < data.length; i++) {
1811
- const id = data[i];
1812
- counts[id]++;
1813
- }
1814
- let rSum = 0;
1815
- let gSum = 0;
1816
- let bSum = 0;
1817
- let aSum = 0;
1818
- let totalWeight = 0;
1819
- for (let id = 0; id < counts.length; id++) {
1820
- const weight = counts[id];
1821
- if (weight === 0) {
1822
- continue;
1823
- }
1824
- if (!includeTransparent && id === transparentPalletIndex) {
1825
- continue;
2010
+ // src/History/PixelMutator/mutatorBlendColor.ts
2011
+ function mutatorBlendColor(writer) {
2012
+ return {
2013
+ blendColor(color, opts = {}) {
2014
+ const {
2015
+ x = 0,
2016
+ y = 0,
2017
+ w = writer.target.width,
2018
+ h = writer.target.height
2019
+ } = opts;
2020
+ writer.accumulator.storeRegionBeforeState(x, y, w, h);
2021
+ blendColorPixelData(writer.target, color, opts);
1826
2022
  }
1827
- const color = palette[id] >>> 0;
1828
- const r2 = color & 255;
1829
- const g2 = color >> 8 & 255;
1830
- const b2 = color >> 16 & 255;
1831
- const a2 = color >> 24 & 255;
1832
- rSum += r2 * weight;
1833
- gSum += g2 * weight;
1834
- bSum += b2 * weight;
1835
- aSum += a2 * weight;
1836
- totalWeight += weight;
1837
- }
1838
- if (totalWeight === 0) {
1839
- return packColor(0, 0, 0, 0);
1840
- }
1841
- const r = rSum / totalWeight | 0;
1842
- const g = gSum / totalWeight | 0;
1843
- const b = bSum / totalWeight | 0;
1844
- const a = aSum / totalWeight | 0;
1845
- return packColor(r, g, b, a);
2023
+ };
1846
2024
  }
1847
2025
 
1848
- // src/IndexedImage/resampleIndexedImage.ts
1849
- function resampleIndexedImage(source, factor) {
1850
- const { data, width, height } = resample32(
1851
- source.data,
1852
- source.width,
1853
- source.height,
1854
- factor
1855
- );
1856
- return new IndexedImage(
1857
- width,
1858
- height,
1859
- data,
1860
- source.palette,
1861
- source.transparentPalletIndex
1862
- );
2026
+ // src/History/PixelMutator/mutatorBlendPixel.ts
2027
+ function mutatorBlendPixel(writer) {
2028
+ return {
2029
+ blendPixel(x, y, color, alpha = 255, blendFn = overwriteFast) {
2030
+ let target = writer.target;
2031
+ let width = target.width;
2032
+ let height = target.height;
2033
+ if (x < 0 || x >= width || y < 0 || y >= height) return;
2034
+ writer.accumulator.storeTileBeforeState(x, y);
2035
+ let index = y * width + x;
2036
+ let bg = target.data32[index];
2037
+ let finalColor = color;
2038
+ if (alpha < 255) {
2039
+ let baseSrcAlpha = color >>> 24;
2040
+ let finalAlpha = baseSrcAlpha * alpha + 128 >> 8;
2041
+ finalColor = (color & 16777215 | finalAlpha << 24) >>> 0;
2042
+ }
2043
+ target.data32[index] = blendFn(finalColor, bg);
2044
+ }
2045
+ };
1863
2046
  }
1864
2047
 
1865
- // src/IndexedImage/indexedImageToImageData.ts
1866
- function indexedImageToImageData(indexedImage) {
1867
- const { width, height, data, palette } = indexedImage;
1868
- const result = new ImageData(width, height);
1869
- const data32 = new Uint32Array(result.data.buffer);
1870
- for (let i = 0; i < data.length; i++) {
1871
- const paletteIndex = data[i];
1872
- const color = palette[paletteIndex];
1873
- data32[i] = color;
2048
+ // src/PixelData/blendPixelData.ts
2049
+ function blendPixelData(dst, src, opts) {
2050
+ const {
2051
+ x: targetX = 0,
2052
+ y: targetY = 0,
2053
+ sx: sourceX = 0,
2054
+ sy: sourceY = 0,
2055
+ w: width = src.width,
2056
+ h: height = src.height,
2057
+ alpha: globalAlpha = 255,
2058
+ blendFn = sourceOverFast,
2059
+ mask,
2060
+ maskType = 0 /* ALPHA */,
2061
+ mw,
2062
+ mx = 0,
2063
+ my = 0,
2064
+ invertMask = false
2065
+ } = opts;
2066
+ if (globalAlpha === 0) return;
2067
+ let x = targetX;
2068
+ let y = targetY;
2069
+ let sx = sourceX;
2070
+ let sy = sourceY;
2071
+ let w = width;
2072
+ let h = height;
2073
+ if (sx < 0) {
2074
+ x -= sx;
2075
+ w += sx;
2076
+ sx = 0;
1874
2077
  }
1875
- return result;
1876
- }
1877
-
1878
- // src/Input/fileInputChangeToImageData.ts
1879
- async function fileInputChangeToImageData(event) {
1880
- const target = event.target;
1881
- const file = target.files?.[0];
1882
- if (!file) return null;
1883
- return await fileToImageData(file);
1884
- }
1885
-
1886
- // src/Input/fileToImageData.ts
1887
- var UnsupportedFormatError = class extends Error {
1888
- constructor(mimeType) {
1889
- super(`File type ${mimeType} is not a supported image format.`);
1890
- this.name = "UnsupportedFormatError";
2078
+ if (sy < 0) {
2079
+ y -= sy;
2080
+ h += sy;
2081
+ sy = 0;
1891
2082
  }
1892
- };
1893
- async function fileToImageData(file) {
1894
- if (!file) return null;
1895
- if (!file.type.startsWith("image/")) {
1896
- throw new UnsupportedFormatError(file.type);
1897
- }
1898
- let bitmap = null;
1899
- try {
1900
- bitmap = await createImageBitmap(file);
1901
- const canvas = new OffscreenCanvas(
1902
- bitmap.width,
1903
- bitmap.height
1904
- );
1905
- const ctx = canvas.getContext("2d");
1906
- if (!ctx) throw new Error(OFFSCREEN_CANVAS_CTX_FAILED);
1907
- ctx.drawImage(
1908
- bitmap,
1909
- 0,
1910
- 0
1911
- );
1912
- return ctx.getImageData(
1913
- 0,
1914
- 0,
1915
- bitmap.width,
1916
- bitmap.height
1917
- );
1918
- } finally {
1919
- bitmap?.close();
1920
- }
1921
- }
1922
-
1923
- // src/Input/getSupportedRasterFormats.ts
1924
- var formatsPromise = null;
1925
- var defaultRasterMimes = [
1926
- "image/png",
1927
- "image/jpeg",
1928
- "image/webp",
1929
- "image/avif",
1930
- "image/gif",
1931
- "image/bmp"
1932
- ];
1933
- async function getSupportedPixelFormats(rasterMimes = defaultRasterMimes) {
1934
- if (formatsPromise) {
1935
- return formatsPromise;
1936
- }
1937
- const probeCanvas = async () => {
1938
- const canvas = new OffscreenCanvas(1, 1);
1939
- const results = await Promise.all(
1940
- rasterMimes.map(async (mime) => {
1941
- try {
1942
- const blob = await canvas.convertToBlob({
1943
- type: mime
1944
- });
1945
- return blob.type === mime ? mime : null;
1946
- } catch {
1947
- return null;
1948
- }
1949
- })
1950
- );
1951
- return results.filter((type) => {
1952
- return type !== null;
1953
- });
1954
- };
1955
- formatsPromise = probeCanvas().catch((error) => {
1956
- formatsPromise = null;
1957
- throw error;
1958
- });
1959
- return formatsPromise;
1960
- }
1961
-
1962
- // src/Mask/copyMask.ts
1963
- function copyMask(src) {
1964
- return src.slice();
1965
- }
1966
-
1967
- // src/Mask/invertMask.ts
1968
- function invertBinaryMask(dst) {
1969
- const len = dst.length;
1970
- for (let i = 0; i < len; i++) {
1971
- dst[i] = dst[i] === 0 ? 1 : 0;
1972
- }
1973
- }
1974
- function invertAlphaMask(dst) {
1975
- const len = dst.length;
1976
- for (let i = 0; i < len; i++) {
1977
- dst[i] = 255 - dst[i];
1978
- }
1979
- }
1980
-
1981
- // src/Mask/mergeMasks.ts
1982
- function mergeMasks(dst, dstWidth, src, opts) {
1983
- const {
1984
- x: targetX = 0,
1985
- y: targetY = 0,
1986
- w: width = 0,
1987
- h: height = 0,
1988
- alpha: globalAlpha = 255,
1989
- maskType = 0 /* ALPHA */,
1990
- mw,
1991
- mx = 0,
1992
- my = 0,
1993
- invertMask = false
1994
- } = opts;
1995
- if (width <= 0 || height <= 0 || globalAlpha === 0) {
1996
- return;
1997
- }
1998
- const sPitch = mw ?? width;
1999
- const isAlpha = maskType === 0 /* ALPHA */;
2000
- for (let iy = 0; iy < height; iy++) {
2001
- const dy = targetY + iy;
2002
- const sy = my + iy;
2003
- if (dy < 0 || sy < 0) {
2004
- continue;
2005
- }
2006
- for (let ix = 0; ix < width; ix++) {
2007
- const dx = targetX + ix;
2008
- const sx = mx + ix;
2009
- if (dx < 0 || dx >= dstWidth || sx < 0 || sx >= sPitch) {
2010
- continue;
2011
- }
2012
- const dIdx = dy * dstWidth + dx;
2013
- const sIdx = sy * sPitch + sx;
2014
- const mVal = src[sIdx];
2015
- let weight = globalAlpha;
2016
- if (isAlpha) {
2017
- const effectiveM = invertMask ? 255 - mVal : mVal;
2018
- if (effectiveM === 0) {
2019
- dst[dIdx] = 0;
2020
- continue;
2021
- }
2022
- weight = globalAlpha === 255 ? effectiveM : effectiveM * globalAlpha + 128 >> 8;
2023
- } else {
2024
- const isHit = invertMask ? mVal === 0 : mVal === 1;
2025
- if (!isHit) {
2026
- dst[dIdx] = 0;
2027
- continue;
2028
- }
2029
- weight = globalAlpha;
2030
- }
2031
- if (weight === 0) {
2032
- dst[dIdx] = 0;
2033
- continue;
2034
- }
2035
- const da = dst[dIdx];
2036
- if (da === 0) {
2037
- } else if (weight === 255) {
2038
- } else if (da === 255) {
2039
- dst[dIdx] = weight;
2040
- } else {
2041
- dst[dIdx] = da * weight + 128 >> 8;
2042
- }
2043
- }
2044
- }
2045
- }
2046
-
2047
- // src/PixelData/PixelData.ts
2048
- var PixelData = class _PixelData {
2049
- data32;
2050
- imageData;
2051
- get width() {
2052
- return this.imageData.width;
2053
- }
2054
- get height() {
2055
- return this.imageData.height;
2056
- }
2057
- constructor(imageData) {
2058
- this.data32 = imageDataToUInt32Array(imageData);
2059
- this.imageData = imageData;
2060
- }
2061
- set(imageData) {
2062
- this.imageData = imageData;
2063
- this.data32 = imageDataToUInt32Array(imageData);
2064
- }
2065
- /**
2066
- * Creates a deep copy of the PixelData using the environment's ImageData constructor.
2067
- */
2068
- copy() {
2069
- const buffer = new Uint8ClampedArray(this.imageData.data);
2070
- const ImageConstructor = typeof ImageData !== "undefined" ? ImageData : this.imageData.constructor;
2071
- const newImageData = new ImageConstructor(
2072
- buffer,
2073
- this.width,
2074
- this.height
2075
- );
2076
- return new _PixelData(newImageData);
2077
- }
2078
- };
2079
-
2080
- // src/PixelData/applyMaskToPixelData.ts
2081
- function applyMaskToPixelData(dst, mask, opts) {
2082
- const {
2083
- x: targetX = 0,
2084
- y: targetY = 0,
2085
- w: width = dst.width,
2086
- h: height = dst.height,
2087
- alpha: globalAlpha = 255,
2088
- maskType = 0 /* ALPHA */,
2089
- mw,
2090
- mx = 0,
2091
- my = 0,
2092
- invertMask = false
2093
- } = opts;
2094
- let x = targetX;
2095
- let y = targetY;
2096
- let w = width;
2097
- let h = height;
2098
- if (x < 0) {
2099
- w += x;
2100
- x = 0;
2083
+ w = Math.min(w, src.width - sx);
2084
+ h = Math.min(h, src.height - sy);
2085
+ if (x < 0) {
2086
+ sx -= x;
2087
+ w += x;
2088
+ x = 0;
2101
2089
  }
2102
2090
  if (y < 0) {
2091
+ sy -= y;
2103
2092
  h += y;
2104
2093
  y = 0;
2105
2094
  }
2106
2095
  const actualW = Math.min(w, dst.width - x);
2107
2096
  const actualH = Math.min(h, dst.height - y);
2108
- if (actualW <= 0 || actualH <= 0 || globalAlpha === 0) {
2109
- return;
2110
- }
2097
+ if (actualW <= 0 || actualH <= 0) return;
2111
2098
  const dst32 = dst.data32;
2099
+ const src32 = src.data32;
2112
2100
  const dw = dst.width;
2101
+ const sw = src.width;
2113
2102
  const mPitch = mw ?? width;
2114
- const isAlpha = maskType === 0 /* ALPHA */;
2103
+ const isAlphaMask = maskType === 0 /* ALPHA */;
2115
2104
  const dx = x - targetX;
2116
2105
  const dy = y - targetY;
2117
2106
  let dIdx = y * dw + x;
2107
+ let sIdx = sy * sw + sx;
2118
2108
  let mIdx = (my + dy) * mPitch + (mx + dx);
2119
2109
  const dStride = dw - actualW;
2110
+ const sStride = sw - actualW;
2120
2111
  const mStride = mPitch - actualW;
2112
+ const isOverwrite = blendFn.isOverwrite;
2121
2113
  for (let iy = 0; iy < actualH; iy++) {
2122
2114
  for (let ix = 0; ix < actualW; ix++) {
2123
- const mVal = mask[mIdx];
2115
+ const baseSrcColor = src32[sIdx];
2116
+ const baseSrcAlpha = baseSrcColor >>> 24;
2117
+ if (baseSrcAlpha === 0 && !isOverwrite) {
2118
+ dIdx++;
2119
+ sIdx++;
2120
+ mIdx++;
2121
+ continue;
2122
+ }
2124
2123
  let weight = globalAlpha;
2125
- if (isAlpha) {
2126
- const effectiveM = invertMask ? 255 - mVal : mVal;
2127
- if (effectiveM === 0) {
2128
- dst32[dIdx] = (dst32[dIdx] & 16777215) >>> 0;
2124
+ if (mask) {
2125
+ const mVal = mask[mIdx];
2126
+ if (isAlphaMask) {
2127
+ const effectiveM = invertMask ? 255 - mVal : mVal;
2128
+ if (effectiveM === 0) {
2129
+ dIdx++;
2130
+ sIdx++;
2131
+ mIdx++;
2132
+ continue;
2133
+ }
2134
+ if (globalAlpha === 255) {
2135
+ weight = effectiveM;
2136
+ } else if (effectiveM === 255) {
2137
+ weight = globalAlpha;
2138
+ } else {
2139
+ weight = effectiveM * globalAlpha + 128 >> 8;
2140
+ }
2141
+ } else {
2142
+ const isHit = invertMask ? mVal === 0 : mVal === 1;
2143
+ if (!isHit) {
2144
+ dIdx++;
2145
+ sIdx++;
2146
+ mIdx++;
2147
+ continue;
2148
+ }
2149
+ weight = globalAlpha;
2150
+ }
2151
+ if (weight === 0) {
2129
2152
  dIdx++;
2153
+ sIdx++;
2130
2154
  mIdx++;
2131
2155
  continue;
2132
2156
  }
2133
- weight = globalAlpha === 255 ? effectiveM : effectiveM * globalAlpha + 128 >> 8;
2134
- } else {
2135
- const isHit = invertMask ? mVal === 0 : mVal === 1;
2136
- if (!isHit) {
2137
- dst32[dIdx] = (dst32[dIdx] & 16777215) >>> 0;
2157
+ }
2158
+ let currentSrcColor = baseSrcColor;
2159
+ if (weight < 255) {
2160
+ let currentSrcAlpha = baseSrcAlpha;
2161
+ if (baseSrcAlpha === 255) {
2162
+ currentSrcAlpha = weight;
2163
+ } else {
2164
+ currentSrcAlpha = baseSrcAlpha * weight + 128 >> 8;
2165
+ }
2166
+ if (!isOverwrite && currentSrcAlpha === 0) {
2138
2167
  dIdx++;
2168
+ sIdx++;
2139
2169
  mIdx++;
2140
2170
  continue;
2141
2171
  }
2142
- weight = globalAlpha;
2143
- }
2144
- if (weight === 0) {
2145
- dst32[dIdx] = (dst32[dIdx] & 16777215) >>> 0;
2146
- } else {
2147
- const d = dst32[dIdx];
2148
- const da = d >>> 24;
2149
- let finalAlpha = da;
2150
- if (da === 0) {
2151
- } else if (weight === 255) {
2152
- } else if (da === 255) {
2153
- finalAlpha = weight;
2154
- } else {
2155
- finalAlpha = da * weight + 128 >> 8;
2156
- }
2157
- dst32[dIdx] = (d & 16777215 | finalAlpha << 24) >>> 0;
2172
+ currentSrcColor = (baseSrcColor & 16777215 | currentSrcAlpha << 24) >>> 0;
2158
2173
  }
2174
+ dst32[dIdx] = blendFn(currentSrcColor, dst32[dIdx]);
2159
2175
  dIdx++;
2176
+ sIdx++;
2160
2177
  mIdx++;
2161
2178
  }
2162
2179
  dIdx += dStride;
2180
+ sIdx += sStride;
2163
2181
  mIdx += mStride;
2164
2182
  }
2165
2183
  }
2166
2184
 
2167
- // src/PixelData/blendColorPixelData.ts
2168
- function blendColorPixelData(dst, color, opts = {}) {
2185
+ // src/History/PixelMutator/mutatorBlendPixelData.ts
2186
+ function mutatorBlendPixelData(writer) {
2187
+ return {
2188
+ blendPixelData(src, opts) {
2189
+ const {
2190
+ x = 0,
2191
+ y = 0,
2192
+ w = src.width,
2193
+ h = src.height
2194
+ } = opts;
2195
+ writer.accumulator.storeRegionBeforeState(x, y, w, h);
2196
+ blendPixelData(writer.target, src, opts);
2197
+ }
2198
+ };
2199
+ }
2200
+
2201
+ // src/PixelData/fillPixelData.ts
2202
+ function fillPixelData(dst, color, _x, _y, _w, _h) {
2203
+ let x;
2204
+ let y;
2205
+ let w;
2206
+ let h;
2207
+ if (typeof _x === "object") {
2208
+ x = _x.x ?? 0;
2209
+ y = _x.y ?? 0;
2210
+ w = _x.w ?? dst.width;
2211
+ h = _x.h ?? dst.height;
2212
+ } else if (typeof _x === "number") {
2213
+ x = _x;
2214
+ y = _y;
2215
+ w = _w;
2216
+ h = _h;
2217
+ } else {
2218
+ x = 0;
2219
+ y = 0;
2220
+ w = dst.width;
2221
+ h = dst.height;
2222
+ }
2223
+ if (x < 0) {
2224
+ w += x;
2225
+ x = 0;
2226
+ }
2227
+ if (y < 0) {
2228
+ h += y;
2229
+ y = 0;
2230
+ }
2231
+ const actualW = Math.min(w, dst.width - x);
2232
+ const actualH = Math.min(h, dst.height - y);
2233
+ if (actualW <= 0 || actualH <= 0) {
2234
+ return;
2235
+ }
2236
+ const dst32 = dst.data32;
2237
+ const dw = dst.width;
2238
+ if (actualW === dw && actualH === dst.height && x === 0 && y === 0) {
2239
+ dst32.fill(color);
2240
+ return;
2241
+ }
2242
+ for (let iy = 0; iy < actualH; iy++) {
2243
+ const start = (y + iy) * dw + x;
2244
+ const end = start + actualW;
2245
+ dst32.fill(color, start, end);
2246
+ }
2247
+ }
2248
+
2249
+ // src/History/PixelMutator/mutatorFillPixelData.ts
2250
+ function mutatorFill(writer) {
2251
+ return {
2252
+ fill(color, rect = {}) {
2253
+ const {
2254
+ x = 0,
2255
+ y = 0,
2256
+ w = writer.target.width,
2257
+ h = writer.target.height
2258
+ } = rect;
2259
+ writer.accumulator.storeRegionBeforeState(x, y, w, h);
2260
+ fillPixelData(writer.target, color, x, y, w, h);
2261
+ }
2262
+ };
2263
+ }
2264
+
2265
+ // src/PixelData/invertPixelData.ts
2266
+ function invertPixelData(pixelData, opts = {}) {
2267
+ const dst = pixelData;
2169
2268
  const {
2170
2269
  x: targetX = 0,
2171
2270
  y: targetY = 0,
2172
- w: width = dst.width,
2173
- h: height = dst.height,
2174
- alpha: globalAlpha = 255,
2175
- blendFn = sourceOverFast,
2271
+ w: width = pixelData.width,
2272
+ h: height = pixelData.height,
2176
2273
  mask,
2177
- maskType = 0 /* ALPHA */,
2178
2274
  mw,
2179
2275
  mx = 0,
2180
2276
  my = 0,
2181
2277
  invertMask = false
2182
2278
  } = opts;
2183
- if (globalAlpha === 0) return;
2184
- const baseSrcAlpha = color >>> 24;
2185
- const isOverwrite = blendFn.isOverwrite;
2186
- if (baseSrcAlpha === 0 && !isOverwrite) return;
2187
2279
  let x = targetX;
2188
2280
  let y = targetY;
2189
2281
  let w = width;
@@ -2202,255 +2294,798 @@ function blendColorPixelData(dst, color, opts = {}) {
2202
2294
  const dst32 = dst.data32;
2203
2295
  const dw = dst.width;
2204
2296
  const mPitch = mw ?? width;
2205
- const isAlphaMask = maskType === 0 /* ALPHA */;
2206
2297
  const dx = x - targetX;
2207
2298
  const dy = y - targetY;
2208
2299
  let dIdx = y * dw + x;
2209
2300
  let mIdx = (my + dy) * mPitch + (mx + dx);
2210
2301
  const dStride = dw - actualW;
2211
2302
  const mStride = mPitch - actualW;
2212
- for (let iy = 0; iy < actualH; iy++) {
2213
- for (let ix = 0; ix < actualW; ix++) {
2214
- let weight = globalAlpha;
2215
- if (mask) {
2303
+ if (mask) {
2304
+ for (let iy = 0; iy < actualH; iy++) {
2305
+ for (let ix = 0; ix < actualW; ix++) {
2216
2306
  const mVal = mask[mIdx];
2217
- if (isAlphaMask) {
2218
- const effectiveM = invertMask ? 255 - mVal : mVal;
2219
- if (effectiveM === 0) {
2220
- dIdx++;
2221
- mIdx++;
2222
- continue;
2223
- }
2224
- if (globalAlpha === 255) {
2225
- weight = effectiveM;
2226
- } else if (effectiveM === 255) {
2227
- weight = globalAlpha;
2228
- } else {
2229
- weight = effectiveM * globalAlpha + 128 >> 8;
2230
- }
2231
- } else {
2232
- const isHit = invertMask ? mVal === 0 : mVal === 1;
2233
- if (!isHit) {
2234
- dIdx++;
2235
- mIdx++;
2236
- continue;
2237
- }
2238
- weight = globalAlpha;
2239
- }
2240
- if (weight === 0) {
2241
- dIdx++;
2242
- mIdx++;
2243
- continue;
2307
+ const isHit = invertMask ? mVal === 0 : mVal === 1;
2308
+ if (isHit) {
2309
+ dst32[dIdx] = dst32[dIdx] ^ 16777215;
2244
2310
  }
2311
+ dIdx++;
2312
+ mIdx++;
2245
2313
  }
2246
- let currentSrcColor = color;
2247
- if (weight < 255) {
2248
- let currentSrcAlpha = baseSrcAlpha;
2249
- if (baseSrcAlpha === 255) {
2250
- currentSrcAlpha = weight;
2251
- } else {
2252
- currentSrcAlpha = baseSrcAlpha * weight + 128 >> 8;
2253
- }
2254
- if (!isOverwrite && currentSrcAlpha === 0) {
2255
- dIdx++;
2256
- mIdx++;
2257
- continue;
2258
- }
2259
- currentSrcColor = (color & 16777215 | currentSrcAlpha << 24) >>> 0;
2314
+ dIdx += dStride;
2315
+ mIdx += mStride;
2316
+ }
2317
+ } else {
2318
+ for (let iy = 0; iy < actualH; iy++) {
2319
+ for (let ix = 0; ix < actualW; ix++) {
2320
+ dst32[dIdx] = dst32[dIdx] ^ 16777215;
2321
+ dIdx++;
2260
2322
  }
2261
- dst32[dIdx] = blendFn(currentSrcColor, dst32[dIdx]);
2262
- dIdx++;
2263
- mIdx++;
2323
+ dIdx += dStride;
2264
2324
  }
2265
- dIdx += dStride;
2266
- mIdx += mStride;
2267
2325
  }
2268
2326
  }
2269
2327
 
2270
- // src/PixelData/blendPixelData.ts
2271
- function blendPixelData(dst, src, opts) {
2328
+ // src/History/PixelMutator/mutatorInvert.ts
2329
+ function mutatorInvert(writer) {
2330
+ return {
2331
+ invert(opts = {}) {
2332
+ const {
2333
+ x = 0,
2334
+ y = 0,
2335
+ w = writer.target.width,
2336
+ h = writer.target.height
2337
+ } = opts;
2338
+ writer.accumulator.storeRegionBeforeState(x, y, w, h);
2339
+ invertPixelData(writer.target, opts);
2340
+ }
2341
+ };
2342
+ }
2343
+
2344
+ // src/History/PixelMutator.ts
2345
+ function makeFullPixelMutator(writer) {
2346
+ return {
2347
+ ...mutatorApplyMask(writer),
2348
+ ...mutatorBlendPixelData(writer),
2349
+ ...mutatorBlendColor(writer),
2350
+ ...mutatorBlendPixel(writer),
2351
+ ...mutatorFill(writer),
2352
+ ...mutatorInvert(writer)
2353
+ };
2354
+ }
2355
+
2356
+ // src/ImageData/ReusableImageData.ts
2357
+ function makeReusableImageData() {
2358
+ let imageData = null;
2359
+ let buffer = null;
2360
+ return function getReusableImageData(width, height) {
2361
+ const hasInstance = !!imageData;
2362
+ const widthMatches = hasInstance && imageData.width === width;
2363
+ const heightMatches = hasInstance && imageData.height === height;
2364
+ if (!widthMatches || !heightMatches) {
2365
+ const buffer2 = new Uint8ClampedArray(width * height * 4);
2366
+ imageData = new ImageData(buffer2, width, height);
2367
+ }
2368
+ return imageData;
2369
+ };
2370
+ }
2371
+
2372
+ // src/ImageData/copyImageData.ts
2373
+ function copyImageData({ data, width, height }) {
2374
+ return new ImageData(data.slice(), width, height);
2375
+ }
2376
+ function copyImageDataLike({ data, width, height }) {
2377
+ return {
2378
+ data: data.slice(),
2379
+ width,
2380
+ height
2381
+ };
2382
+ }
2383
+
2384
+ // src/PixelData/pixelDataToAlphaMask.ts
2385
+ function pixelDataToAlphaMask(pixelData) {
2272
2386
  const {
2273
- x: targetX = 0,
2274
- y: targetY = 0,
2275
- sx: sourceX = 0,
2276
- sy: sourceY = 0,
2277
- w: width = src.width,
2278
- h: height = src.height,
2387
+ data32,
2388
+ width,
2389
+ height
2390
+ } = pixelData;
2391
+ const len = data32.length;
2392
+ const mask = new Uint8Array(width * height);
2393
+ for (let i = 0; i < len; i++) {
2394
+ const val = data32[i];
2395
+ mask[i] = val >>> 24 & 255;
2396
+ }
2397
+ return mask;
2398
+ }
2399
+
2400
+ // src/ImageData/imageDataToAlphaMask.ts
2401
+ function imageDataToAlphaMask(imageData) {
2402
+ const {
2403
+ width,
2404
+ height,
2405
+ data
2406
+ } = imageData;
2407
+ const data32 = new Uint32Array(
2408
+ data.buffer,
2409
+ data.byteOffset,
2410
+ data.byteLength >> 2
2411
+ );
2412
+ const len = data32.length;
2413
+ const mask = new Uint8Array(width * height);
2414
+ for (let i = 0; i < len; i++) {
2415
+ const val = data32[i];
2416
+ mask[i] = val >>> 24 & 255;
2417
+ }
2418
+ return mask;
2419
+ }
2420
+
2421
+ // src/ImageData/imageDataToDataUrl.ts
2422
+ var get = makeReusableCanvas();
2423
+ function imageDataToDataUrl(imageData) {
2424
+ const { canvas, ctx } = get(imageData.width, imageData.height);
2425
+ ctx.putImageData(imageData, 0, 0);
2426
+ return canvas.toDataURL();
2427
+ }
2428
+ imageDataToDataUrl.reset = get.reset;
2429
+
2430
+ // src/ImageData/imageDataToUInt32Array.ts
2431
+ function imageDataToUInt32Array(imageData) {
2432
+ return new Uint32Array(
2433
+ imageData.data.buffer,
2434
+ imageData.data.byteOffset,
2435
+ // Shift right by 2 is a fast bitwise division by 4.
2436
+ imageData.data.byteLength >> 2
2437
+ );
2438
+ }
2439
+
2440
+ // src/ImageData/invertImageData.ts
2441
+ function invertImageData(imageData) {
2442
+ const data = imageData.data;
2443
+ let length = data.length;
2444
+ for (let i = 0; i < length; i += 4) {
2445
+ data[i] = 255 - data[i];
2446
+ data[i + 1] = 255 - data[i + 1];
2447
+ data[i + 2] = 255 - data[i + 2];
2448
+ }
2449
+ return imageData;
2450
+ }
2451
+
2452
+ // src/Internal/resample32.ts
2453
+ var resample32Scratch = {
2454
+ data: null,
2455
+ width: 0,
2456
+ height: 0
2457
+ };
2458
+ function resample32(srcData32, srcW, srcH, factor) {
2459
+ const dstW = Math.max(1, srcW * factor | 0);
2460
+ const dstH = Math.max(1, srcH * factor | 0);
2461
+ const dstData = new Int32Array(dstW * dstH);
2462
+ const scaleX = srcW / dstW;
2463
+ const scaleY = srcH / dstH;
2464
+ for (let y = 0; y < dstH; y++) {
2465
+ const srcY = Math.min(srcH - 1, y * scaleY | 0);
2466
+ const srcRowOffset = srcY * srcW;
2467
+ const dstRowOffset = y * dstW;
2468
+ for (let x = 0; x < dstW; x++) {
2469
+ const srcX = Math.min(srcW - 1, x * scaleX | 0);
2470
+ dstData[dstRowOffset + x] = srcData32[srcRowOffset + srcX];
2471
+ }
2472
+ }
2473
+ resample32Scratch.data = dstData;
2474
+ resample32Scratch.width = dstW;
2475
+ resample32Scratch.height = dstH;
2476
+ return resample32Scratch;
2477
+ }
2478
+
2479
+ // src/ImageData/resampleImageData.ts
2480
+ function resampleImageData(source, factor) {
2481
+ const src32 = new Uint32Array(source.data.buffer);
2482
+ const { data, width, height } = resample32(src32, source.width, source.height, factor);
2483
+ const uint8ClampedArray = new Uint8ClampedArray(data.buffer);
2484
+ return new ImageData(uint8ClampedArray, width, height);
2485
+ }
2486
+
2487
+ // src/ImageData/resizeImageData.ts
2488
+ function resizeImageData(current, newWidth, newHeight, offsetX = 0, offsetY = 0) {
2489
+ const result = new ImageData(newWidth, newHeight);
2490
+ const {
2491
+ width: oldW,
2492
+ height: oldH,
2493
+ data: oldData
2494
+ } = current;
2495
+ const newData = result.data;
2496
+ const x0 = Math.max(0, offsetX);
2497
+ const y0 = Math.max(0, offsetY);
2498
+ const x1 = Math.min(newWidth, offsetX + oldW);
2499
+ const y1 = Math.min(newHeight, offsetY + oldH);
2500
+ if (x1 <= x0 || y1 <= y0) {
2501
+ return result;
2502
+ }
2503
+ const rowCount = y1 - y0;
2504
+ const rowLen = (x1 - x0) * 4;
2505
+ for (let row = 0; row < rowCount; row++) {
2506
+ const dstY = y0 + row;
2507
+ const srcY = dstY - offsetY;
2508
+ const srcX = x0 - offsetX;
2509
+ const dstStart = (dstY * newWidth + x0) * 4;
2510
+ const srcStart = (srcY * oldW + srcX) * 4;
2511
+ newData.set(
2512
+ oldData.subarray(srcStart, srcStart + rowLen),
2513
+ dstStart
2514
+ );
2515
+ }
2516
+ return result;
2517
+ }
2518
+
2519
+ // src/ImageData/serialization.ts
2520
+ function base64EncodeArrayBuffer(buffer) {
2521
+ const uint8 = new Uint8Array(buffer);
2522
+ const decoder = new TextDecoder("latin1");
2523
+ const binary = decoder.decode(uint8);
2524
+ return btoa(binary);
2525
+ }
2526
+ function base64DecodeArrayBuffer(encoded) {
2527
+ const binary = atob(encoded);
2528
+ const bytes = new Uint8ClampedArray(binary.length);
2529
+ for (let i = 0; i < binary.length; i++) {
2530
+ bytes[i] = binary.charCodeAt(i);
2531
+ }
2532
+ return bytes;
2533
+ }
2534
+ function serializeImageData(imageData) {
2535
+ return {
2536
+ width: imageData.width,
2537
+ height: imageData.height,
2538
+ data: base64EncodeArrayBuffer(imageData.data.buffer)
2539
+ };
2540
+ }
2541
+ function serializeNullableImageData(imageData) {
2542
+ if (!imageData) return null;
2543
+ return serializeImageData(imageData);
2544
+ }
2545
+ function deserializeRawImageData(serialized) {
2546
+ return {
2547
+ width: serialized.width,
2548
+ height: serialized.height,
2549
+ data: base64DecodeArrayBuffer(serialized.data)
2550
+ };
2551
+ }
2552
+ function deserializeImageData(serialized) {
2553
+ const data = base64DecodeArrayBuffer(serialized.data);
2554
+ return new ImageData(data, serialized.width, serialized.height);
2555
+ }
2556
+ function deserializeNullableImageData(serialized) {
2557
+ if (!serialized) return null;
2558
+ return deserializeImageData(serialized);
2559
+ }
2560
+
2561
+ // src/ImageData/writeImageData.ts
2562
+ function writeImageData(target, source, x, y, sx = 0, sy = 0, sw = source.width, sh = source.height, mask = null, maskType = 1 /* BINARY */) {
2563
+ const dstW = target.width;
2564
+ const dstH = target.height;
2565
+ const dstData = target.data;
2566
+ const srcW = source.width;
2567
+ const srcData = source.data;
2568
+ const x0 = Math.max(0, x);
2569
+ const y0 = Math.max(0, y);
2570
+ const x1 = Math.min(dstW, x + sw);
2571
+ const y1 = Math.min(dstH, y + sh);
2572
+ if (x1 <= x0 || y1 <= y0) {
2573
+ return;
2574
+ }
2575
+ const useMask = !!mask;
2576
+ const rowCount = y1 - y0;
2577
+ const rowLenPixels = x1 - x0;
2578
+ for (let row = 0; row < rowCount; row++) {
2579
+ const dstY = y0 + row;
2580
+ const srcY = sy + (dstY - y);
2581
+ const srcXBase = sx + (x0 - x);
2582
+ const dstStart = (dstY * dstW + x0) * 4;
2583
+ const srcStart = (srcY * srcW + srcXBase) * 4;
2584
+ if (useMask && mask) {
2585
+ for (let ix = 0; ix < rowLenPixels; ix++) {
2586
+ const mi = srcY * srcW + (srcXBase + ix);
2587
+ const alpha = mask[mi];
2588
+ if (alpha === 0) {
2589
+ continue;
2590
+ }
2591
+ const di = dstStart + ix * 4;
2592
+ const si = srcStart + ix * 4;
2593
+ if (maskType === 1 /* BINARY */ || alpha === 255) {
2594
+ dstData[di] = srcData[si];
2595
+ dstData[di + 1] = srcData[si + 1];
2596
+ dstData[di + 2] = srcData[si + 2];
2597
+ dstData[di + 3] = srcData[si + 3];
2598
+ } else {
2599
+ const a = alpha / 255;
2600
+ const invA = 1 - a;
2601
+ dstData[di] = srcData[si] * a + dstData[di] * invA;
2602
+ dstData[di + 1] = srcData[si + 1] * a + dstData[di + 1] * invA;
2603
+ dstData[di + 2] = srcData[si + 2] * a + dstData[di + 2] * invA;
2604
+ dstData[di + 3] = srcData[si + 3] * a + dstData[di + 3] * invA;
2605
+ }
2606
+ }
2607
+ } else {
2608
+ const byteLen = rowLenPixels * 4;
2609
+ const sub = srcData.subarray(srcStart, srcStart + byteLen);
2610
+ dstData.set(sub, dstStart);
2611
+ }
2612
+ }
2613
+ }
2614
+
2615
+ // src/ImageData/writeImageDataBuffer.ts
2616
+ function writeImageDataBuffer(imageData, data, _x, _y, _w, _h) {
2617
+ const { x, y, w, h } = typeof _x === "object" ? _x : { x: _x, y: _y, w: _w, h: _h };
2618
+ const { width: dstW, height: dstH, data: dst } = imageData;
2619
+ const x0 = Math.max(0, x);
2620
+ const y0 = Math.max(0, y);
2621
+ const x1 = Math.min(dstW, x + w);
2622
+ const y1 = Math.min(dstH, y + h);
2623
+ if (x1 <= x0 || y1 <= y0) return;
2624
+ const rowLen = (x1 - x0) * 4;
2625
+ const srcCol = x0 - x;
2626
+ const srcYOffset = y0 - y;
2627
+ const actualH = y1 - y0;
2628
+ for (let row = 0; row < actualH; row++) {
2629
+ const dstStart = ((y0 + row) * dstW + x0) * 4;
2630
+ const srcRow = srcYOffset + row;
2631
+ const o = (srcRow * w + srcCol) * 4;
2632
+ dst.set(data.subarray(o, o + rowLen), dstStart);
2633
+ }
2634
+ }
2635
+
2636
+ // src/IndexedImage/IndexedImage.ts
2637
+ var IndexedImage = class _IndexedImage {
2638
+ /** The width of the image in pixels. */
2639
+ width;
2640
+ /** The height of the image in pixels. */
2641
+ height;
2642
+ /** Flat array of palette indices. Index = x + (y * width). */
2643
+ data;
2644
+ /** The palette of unique 32-bit colors (ABGR/RGBA packed) found in the image. */
2645
+ palette;
2646
+ /** The specific index in the palette reserved for fully transparent pixels. */
2647
+ transparentPalletIndex;
2648
+ /**
2649
+ * @param width - Image width.
2650
+ * @param height - Image height.
2651
+ * @param data - The indexed pixel data.
2652
+ * @param palette - The array of packed colors.
2653
+ * @param transparentPalletIndex - The index representing alpha 0.
2654
+ */
2655
+ constructor(width, height, data, palette, transparentPalletIndex) {
2656
+ this.width = width;
2657
+ this.height = height;
2658
+ this.data = data;
2659
+ this.palette = palette;
2660
+ this.transparentPalletIndex = transparentPalletIndex;
2661
+ }
2662
+ /**
2663
+ * Creates an IndexedImage from standard browser ImageData.
2664
+ * @param imageData - The source ImageData to convert.
2665
+ * @returns A new IndexedImage instance.
2666
+ */
2667
+ static fromImageData(imageData) {
2668
+ return _IndexedImage.fromRaw(imageData.data, imageData.width, imageData.height);
2669
+ }
2670
+ /**
2671
+ * Creates an IndexedImage from a raw byte buffer and dimensions.
2672
+ * Any pixel with an alpha channel of 0 is normalized to the transparent palette index.
2673
+ * @param data - Raw RGBA byte data.
2674
+ * @param width - Image width.
2675
+ * @param height - Image height.
2676
+ * @returns A new IndexedImage instance.
2677
+ */
2678
+ static fromRaw(data, width, height) {
2679
+ const buffer = data.buffer;
2680
+ const rawData = new Uint32Array(buffer);
2681
+ const indexedData = new Int32Array(rawData.length);
2682
+ const colorMap = /* @__PURE__ */ new Map();
2683
+ const transparentColor = 0;
2684
+ const transparentPalletIndex = 0;
2685
+ colorMap.set(transparentColor, transparentPalletIndex);
2686
+ for (let i = 0; i < rawData.length; i++) {
2687
+ const pixel = rawData[i];
2688
+ const alpha = pixel >>> 24 & 255;
2689
+ const isTransparent = alpha === 0;
2690
+ const colorKey = isTransparent ? transparentColor : pixel >>> 0;
2691
+ let id = colorMap.get(colorKey);
2692
+ if (id === void 0) {
2693
+ id = colorMap.size;
2694
+ colorMap.set(colorKey, id);
2695
+ }
2696
+ indexedData[i] = id;
2697
+ }
2698
+ const palette = Uint32Array.from(colorMap.keys());
2699
+ return new _IndexedImage(
2700
+ width,
2701
+ height,
2702
+ indexedData,
2703
+ palette,
2704
+ transparentPalletIndex
2705
+ );
2706
+ }
2707
+ /**
2708
+ * Retrieves the 32-bit packed color value at the given coordinates.
2709
+ * @param x - X coordinate.
2710
+ * @param y - Y coordinate.
2711
+ * @returns The packed color from the palette.
2712
+ */
2713
+ getColorAt(x, y) {
2714
+ const index = x + y * this.width;
2715
+ const paletteIndex = this.data[index];
2716
+ return this.palette[paletteIndex];
2717
+ }
2718
+ };
2719
+
2720
+ // src/IndexedImage/getIndexedImageColorCounts.ts
2721
+ function getIndexedImageColorCounts(indexedImage) {
2722
+ const data = indexedImage.data;
2723
+ const palette = indexedImage.palette;
2724
+ const frequencies = new Int32Array(palette.length);
2725
+ for (let i = 0; i < data.length; i++) {
2726
+ const colorIndex = data[i];
2727
+ frequencies[colorIndex]++;
2728
+ }
2729
+ return frequencies;
2730
+ }
2731
+
2732
+ // src/IndexedImage/indexedImageToAverageColor.ts
2733
+ function indexedImageToAverageColor(indexedImage, includeTransparent = false) {
2734
+ const { data, palette, transparentPalletIndex } = indexedImage;
2735
+ const counts = new Uint32Array(palette.length);
2736
+ for (let i = 0; i < data.length; i++) {
2737
+ const id = data[i];
2738
+ counts[id]++;
2739
+ }
2740
+ let rSum = 0;
2741
+ let gSum = 0;
2742
+ let bSum = 0;
2743
+ let aSum = 0;
2744
+ let totalWeight = 0;
2745
+ for (let id = 0; id < counts.length; id++) {
2746
+ const weight = counts[id];
2747
+ if (weight === 0) {
2748
+ continue;
2749
+ }
2750
+ if (!includeTransparent && id === transparentPalletIndex) {
2751
+ continue;
2752
+ }
2753
+ const color = palette[id] >>> 0;
2754
+ const r2 = color & 255;
2755
+ const g2 = color >> 8 & 255;
2756
+ const b2 = color >> 16 & 255;
2757
+ const a2 = color >> 24 & 255;
2758
+ rSum += r2 * weight;
2759
+ gSum += g2 * weight;
2760
+ bSum += b2 * weight;
2761
+ aSum += a2 * weight;
2762
+ totalWeight += weight;
2763
+ }
2764
+ if (totalWeight === 0) {
2765
+ return packColor(0, 0, 0, 0);
2766
+ }
2767
+ const r = rSum / totalWeight | 0;
2768
+ const g = gSum / totalWeight | 0;
2769
+ const b = bSum / totalWeight | 0;
2770
+ const a = aSum / totalWeight | 0;
2771
+ return packColor(r, g, b, a);
2772
+ }
2773
+
2774
+ // src/IndexedImage/resampleIndexedImage.ts
2775
+ function resampleIndexedImage(source, factor) {
2776
+ const { data, width, height } = resample32(
2777
+ source.data,
2778
+ source.width,
2779
+ source.height,
2780
+ factor
2781
+ );
2782
+ return new IndexedImage(
2783
+ width,
2784
+ height,
2785
+ data,
2786
+ source.palette,
2787
+ source.transparentPalletIndex
2788
+ );
2789
+ }
2790
+
2791
+ // src/IndexedImage/indexedImageToImageData.ts
2792
+ function indexedImageToImageData(indexedImage) {
2793
+ const { width, height, data, palette } = indexedImage;
2794
+ const result = new ImageData(width, height);
2795
+ const data32 = new Uint32Array(result.data.buffer);
2796
+ for (let i = 0; i < data.length; i++) {
2797
+ const paletteIndex = data[i];
2798
+ const color = palette[paletteIndex];
2799
+ data32[i] = color;
2800
+ }
2801
+ return result;
2802
+ }
2803
+
2804
+ // src/Input/fileInputChangeToImageData.ts
2805
+ async function fileInputChangeToImageData(event) {
2806
+ const target = event.target;
2807
+ const file = target.files?.[0];
2808
+ if (!file) return null;
2809
+ return await fileToImageData(file);
2810
+ }
2811
+
2812
+ // src/Input/fileToImageData.ts
2813
+ var UnsupportedFormatError = class extends Error {
2814
+ constructor(mimeType) {
2815
+ super(`File type ${mimeType} is not a supported image format.`);
2816
+ this.name = "UnsupportedFormatError";
2817
+ }
2818
+ };
2819
+ async function fileToImageData(file) {
2820
+ if (!file) return null;
2821
+ if (!file.type.startsWith("image/")) {
2822
+ throw new UnsupportedFormatError(file.type);
2823
+ }
2824
+ let bitmap = null;
2825
+ try {
2826
+ bitmap = await createImageBitmap(file);
2827
+ const canvas = new OffscreenCanvas(
2828
+ bitmap.width,
2829
+ bitmap.height
2830
+ );
2831
+ const ctx = canvas.getContext("2d");
2832
+ if (!ctx) throw new Error(OFFSCREEN_CANVAS_CTX_FAILED);
2833
+ ctx.drawImage(
2834
+ bitmap,
2835
+ 0,
2836
+ 0
2837
+ );
2838
+ return ctx.getImageData(
2839
+ 0,
2840
+ 0,
2841
+ bitmap.width,
2842
+ bitmap.height
2843
+ );
2844
+ } finally {
2845
+ bitmap?.close();
2846
+ }
2847
+ }
2848
+
2849
+ // src/Input/getSupportedRasterFormats.ts
2850
+ var formatsPromise = null;
2851
+ var defaultRasterMimes = [
2852
+ "image/png",
2853
+ "image/jpeg",
2854
+ "image/webp",
2855
+ "image/avif",
2856
+ "image/gif",
2857
+ "image/bmp"
2858
+ ];
2859
+ async function getSupportedPixelFormats(rasterMimes = defaultRasterMimes) {
2860
+ if (formatsPromise) {
2861
+ return formatsPromise;
2862
+ }
2863
+ const probeCanvas = async () => {
2864
+ const canvas = new OffscreenCanvas(1, 1);
2865
+ const results = await Promise.all(
2866
+ rasterMimes.map(async (mime) => {
2867
+ try {
2868
+ const blob = await canvas.convertToBlob({
2869
+ type: mime
2870
+ });
2871
+ return blob.type === mime ? mime : null;
2872
+ } catch {
2873
+ return null;
2874
+ }
2875
+ })
2876
+ );
2877
+ return results.filter((type) => {
2878
+ return type !== null;
2879
+ });
2880
+ };
2881
+ formatsPromise = probeCanvas().catch((error) => {
2882
+ formatsPromise = null;
2883
+ throw error;
2884
+ });
2885
+ return formatsPromise;
2886
+ }
2887
+
2888
+ // src/Mask/copyMask.ts
2889
+ function copyMask(src) {
2890
+ return src.slice();
2891
+ }
2892
+
2893
+ // src/Mask/invertMask.ts
2894
+ function invertBinaryMask(dst) {
2895
+ const len = dst.length;
2896
+ for (let i = 0; i < len; i++) {
2897
+ dst[i] = dst[i] === 0 ? 1 : 0;
2898
+ }
2899
+ }
2900
+ function invertAlphaMask(dst) {
2901
+ const len = dst.length;
2902
+ for (let i = 0; i < len; i++) {
2903
+ dst[i] = 255 - dst[i];
2904
+ }
2905
+ }
2906
+
2907
+ // src/Mask/mergeMasks.ts
2908
+ function mergeMasks(dst, dstWidth, src, opts) {
2909
+ const {
2910
+ x: targetX = 0,
2911
+ y: targetY = 0,
2912
+ w: width = 0,
2913
+ h: height = 0,
2279
2914
  alpha: globalAlpha = 255,
2280
- blendFn = FAST_BLEND_MODES[1 /* sourceOver */],
2281
- mask,
2282
2915
  maskType = 0 /* ALPHA */,
2283
2916
  mw,
2284
2917
  mx = 0,
2285
2918
  my = 0,
2286
2919
  invertMask = false
2287
2920
  } = opts;
2288
- if (globalAlpha === 0) return;
2289
- let x = targetX;
2290
- let y = targetY;
2291
- let sx = sourceX;
2292
- let sy = sourceY;
2293
- let w = width;
2294
- let h = height;
2295
- if (sx < 0) {
2296
- x -= sx;
2297
- w += sx;
2298
- sx = 0;
2299
- }
2300
- if (sy < 0) {
2301
- y -= sy;
2302
- h += sy;
2303
- sy = 0;
2304
- }
2305
- w = Math.min(w, src.width - sx);
2306
- h = Math.min(h, src.height - sy);
2307
- if (x < 0) {
2308
- sx -= x;
2309
- w += x;
2310
- x = 0;
2311
- }
2312
- if (y < 0) {
2313
- sy -= y;
2314
- h += y;
2315
- y = 0;
2921
+ if (width <= 0 || height <= 0 || globalAlpha === 0) {
2922
+ return;
2316
2923
  }
2317
- const actualW = Math.min(w, dst.width - x);
2318
- const actualH = Math.min(h, dst.height - y);
2319
- if (actualW <= 0 || actualH <= 0) return;
2320
- const dst32 = dst.data32;
2321
- const src32 = src.data32;
2322
- const dw = dst.width;
2323
- const sw = src.width;
2324
- const mPitch = mw ?? width;
2325
- const isAlphaMask = maskType === 0 /* ALPHA */;
2326
- const dx = x - targetX;
2327
- const dy = y - targetY;
2328
- let dIdx = y * dw + x;
2329
- let sIdx = sy * sw + sx;
2330
- let mIdx = (my + dy) * mPitch + (mx + dx);
2331
- const dStride = dw - actualW;
2332
- const sStride = sw - actualW;
2333
- const mStride = mPitch - actualW;
2334
- const isOverwrite = blendFn.isOverwrite;
2335
- for (let iy = 0; iy < actualH; iy++) {
2336
- for (let ix = 0; ix < actualW; ix++) {
2337
- const baseSrcColor = src32[sIdx];
2338
- const baseSrcAlpha = baseSrcColor >>> 24;
2339
- if (baseSrcAlpha === 0 && !isOverwrite) {
2340
- dIdx++;
2341
- sIdx++;
2342
- mIdx++;
2924
+ const sPitch = mw ?? width;
2925
+ const isAlpha = maskType === 0 /* ALPHA */;
2926
+ for (let iy = 0; iy < height; iy++) {
2927
+ const dy = targetY + iy;
2928
+ const sy = my + iy;
2929
+ if (dy < 0 || sy < 0) {
2930
+ continue;
2931
+ }
2932
+ for (let ix = 0; ix < width; ix++) {
2933
+ const dx = targetX + ix;
2934
+ const sx = mx + ix;
2935
+ if (dx < 0 || dx >= dstWidth || sx < 0 || sx >= sPitch) {
2343
2936
  continue;
2344
2937
  }
2938
+ const dIdx = dy * dstWidth + dx;
2939
+ const sIdx = sy * sPitch + sx;
2940
+ const mVal = src[sIdx];
2345
2941
  let weight = globalAlpha;
2346
- if (mask) {
2347
- const mVal = mask[mIdx];
2348
- if (isAlphaMask) {
2349
- const effectiveM = invertMask ? 255 - mVal : mVal;
2350
- if (effectiveM === 0) {
2351
- dIdx++;
2352
- sIdx++;
2353
- mIdx++;
2354
- continue;
2355
- }
2356
- if (globalAlpha === 255) {
2357
- weight = effectiveM;
2358
- } else if (effectiveM === 255) {
2359
- weight = globalAlpha;
2360
- } else {
2361
- weight = effectiveM * globalAlpha + 128 >> 8;
2362
- }
2363
- } else {
2364
- const isHit = invertMask ? mVal === 0 : mVal === 1;
2365
- if (!isHit) {
2366
- dIdx++;
2367
- sIdx++;
2368
- mIdx++;
2369
- continue;
2370
- }
2371
- weight = globalAlpha;
2372
- }
2373
- if (weight === 0) {
2374
- dIdx++;
2375
- sIdx++;
2376
- mIdx++;
2942
+ if (isAlpha) {
2943
+ const effectiveM = invertMask ? 255 - mVal : mVal;
2944
+ if (effectiveM === 0) {
2945
+ dst[dIdx] = 0;
2377
2946
  continue;
2378
2947
  }
2379
- }
2380
- let currentSrcColor = baseSrcColor;
2381
- if (weight < 255) {
2382
- let currentSrcAlpha = baseSrcAlpha;
2383
- if (baseSrcAlpha === 255) {
2384
- currentSrcAlpha = weight;
2385
- } else {
2386
- currentSrcAlpha = baseSrcAlpha * weight + 128 >> 8;
2387
- }
2388
- if (!isOverwrite && currentSrcAlpha === 0) {
2389
- dIdx++;
2390
- sIdx++;
2391
- mIdx++;
2948
+ weight = globalAlpha === 255 ? effectiveM : effectiveM * globalAlpha + 128 >> 8;
2949
+ } else {
2950
+ const isHit = invertMask ? mVal === 0 : mVal === 1;
2951
+ if (!isHit) {
2952
+ dst[dIdx] = 0;
2392
2953
  continue;
2393
2954
  }
2394
- currentSrcColor = (baseSrcColor & 16777215 | currentSrcAlpha << 24) >>> 0;
2955
+ weight = globalAlpha;
2956
+ }
2957
+ if (weight === 0) {
2958
+ dst[dIdx] = 0;
2959
+ continue;
2960
+ }
2961
+ const da = dst[dIdx];
2962
+ if (da === 0) {
2963
+ } else if (weight === 255) {
2964
+ } else if (da === 255) {
2965
+ dst[dIdx] = weight;
2966
+ } else {
2967
+ dst[dIdx] = da * weight + 128 >> 8;
2395
2968
  }
2396
- dst32[dIdx] = blendFn(currentSrcColor, dst32[dIdx]);
2397
- dIdx++;
2398
- sIdx++;
2399
- mIdx++;
2400
2969
  }
2401
- dIdx += dStride;
2402
- sIdx += sStride;
2403
- mIdx += mStride;
2404
2970
  }
2405
2971
  }
2406
2972
 
2407
- // src/PixelData/fillPixelData.ts
2408
- function fillPixelData(dst, color, _x, _y, _w, _h) {
2409
- let x;
2410
- let y;
2411
- let w;
2412
- let h;
2413
- if (typeof _x === "object") {
2414
- x = _x.x ?? 0;
2415
- y = _x.y ?? 0;
2416
- w = _x.w ?? dst.width;
2417
- h = _x.h ?? dst.height;
2418
- } else if (typeof _x === "number") {
2419
- x = _x;
2420
- y = _y;
2421
- w = _w;
2422
- h = _h;
2423
- } else {
2424
- x = 0;
2425
- y = 0;
2426
- w = dst.width;
2427
- h = dst.height;
2973
+ // src/PixelData/PixelData.ts
2974
+ var PixelData = class _PixelData {
2975
+ data32;
2976
+ imageData;
2977
+ get width() {
2978
+ return this.imageData.width;
2428
2979
  }
2429
- if (x < 0) {
2430
- w += x;
2431
- x = 0;
2980
+ get height() {
2981
+ return this.imageData.height;
2432
2982
  }
2433
- if (y < 0) {
2434
- h += y;
2435
- y = 0;
2983
+ constructor(imageData) {
2984
+ this.data32 = imageDataToUInt32Array(imageData);
2985
+ this.imageData = imageData;
2436
2986
  }
2437
- const actualW = Math.min(w, dst.width - x);
2438
- const actualH = Math.min(h, dst.height - y);
2439
- if (actualW <= 0 || actualH <= 0) {
2440
- return;
2987
+ set(imageData) {
2988
+ this.imageData = imageData;
2989
+ this.data32 = imageDataToUInt32Array(imageData);
2441
2990
  }
2442
- const dst32 = dst.data32;
2443
- const dw = dst.width;
2444
- if (actualW === dw && actualH === dst.height && x === 0 && y === 0) {
2445
- dst32.fill(color);
2446
- return;
2991
+ /**
2992
+ * Creates a deep copy of the PixelData using the environment's ImageData constructor.
2993
+ */
2994
+ copy() {
2995
+ const buffer = new Uint8ClampedArray(this.imageData.data);
2996
+ const ImageConstructor = typeof ImageData !== "undefined" ? ImageData : this.imageData.constructor;
2997
+ const newImageData = new ImageConstructor(
2998
+ buffer,
2999
+ this.width,
3000
+ this.height
3001
+ );
3002
+ return new _PixelData(newImageData);
2447
3003
  }
2448
- for (let iy = 0; iy < actualH; iy++) {
2449
- const start = (y + iy) * dw + x;
2450
- const end = start + actualW;
2451
- dst32.fill(color, start, end);
3004
+ };
3005
+
3006
+ // src/PixelData/applyCircleBrushToPixelData.ts
3007
+ function applyCircleBrushToPixelData(target, color, centerX, centerY, brushSize, alpha = 255, fallOff, blendFn = sourceOverPerfect) {
3008
+ const r = brushSize / 2;
3009
+ const rSqr = r * r;
3010
+ const centerOffset = brushSize % 2 === 0 ? 0.5 : 0;
3011
+ const xStart = Math.max(0, Math.ceil(centerX - r));
3012
+ const xEnd = Math.min(target.width - 1, Math.floor(centerX + r));
3013
+ const yStart = Math.max(0, Math.ceil(centerY - r));
3014
+ const yEnd = Math.min(target.height - 1, Math.floor(centerY + r));
3015
+ const data32 = target.data32;
3016
+ const targetWidth = target.width;
3017
+ const baseColor = color & 16777215;
3018
+ const invR = 1 / r;
3019
+ const constantSrc = (alpha << 24 | baseColor) >>> 0;
3020
+ for (let cy = yStart; cy <= yEnd; cy++) {
3021
+ const dy = cy - centerY + centerOffset;
3022
+ const dySqr = dy * dy;
3023
+ const rowOffset = cy * targetWidth;
3024
+ for (let cx = xStart; cx <= xEnd; cx++) {
3025
+ const dx = cx - centerX + centerOffset;
3026
+ const dSqr = dx * dx + dySqr;
3027
+ if (dSqr <= rSqr) {
3028
+ const idx = rowOffset + cx;
3029
+ if (fallOff) {
3030
+ const strength = fallOff(Math.sqrt(dSqr) * invR);
3031
+ const fAlpha = alpha * strength & 255;
3032
+ const src = (fAlpha << 24 | baseColor) >>> 0;
3033
+ data32[idx] = blendFn(src, data32[idx]);
3034
+ } else {
3035
+ data32[idx] = blendFn(constantSrc, data32[idx]);
3036
+ }
3037
+ }
3038
+ }
3039
+ }
3040
+ }
3041
+
3042
+ // src/PixelData/applyRectBrushToPixelData.ts
3043
+ function applyRectBrushToPixelData(target, color, centerX, centerY, brushWidth, brushHeight, alpha = 255, fallOff, blendFn = sourceOverPerfect) {
3044
+ const targetWidth = target.width;
3045
+ const targetHeight = target.height;
3046
+ const data32 = target.data32;
3047
+ const rawStartX = Math.floor(centerX - brushWidth / 2);
3048
+ const rawStartY = Math.floor(centerY - brushHeight / 2);
3049
+ const endX = Math.min(targetWidth, rawStartX + brushWidth);
3050
+ const endY = Math.min(targetHeight, rawStartY + brushHeight);
3051
+ const startX = Math.max(0, rawStartX);
3052
+ const startY = Math.max(0, rawStartY);
3053
+ const baseColor = color & 16777215;
3054
+ const constantSrc = (alpha << 24 | baseColor) >>> 0;
3055
+ const invHalfW = 1 / (brushWidth / 2);
3056
+ const invHalfH = 1 / (brushHeight / 2);
3057
+ for (let py = startY; py < endY; py++) {
3058
+ const rowOffset = py * targetWidth;
3059
+ const dy = Math.abs(py + 0.5 - centerY) * invHalfH;
3060
+ for (let px = startX; px < endX; px++) {
3061
+ if (fallOff) {
3062
+ const dx = Math.abs(px + 0.5 - centerX) * invHalfW;
3063
+ const dist = dx > dy ? dx : dy;
3064
+ const fAlpha = alpha * fallOff(dist) | 0;
3065
+ const src = (fAlpha << 24 | baseColor) >>> 0;
3066
+ data32[rowOffset + px] = blendFn(src, data32[rowOffset + px]);
3067
+ } else {
3068
+ data32[rowOffset + px] = blendFn(constantSrc, data32[rowOffset + px]);
3069
+ }
3070
+ }
2452
3071
  }
2453
3072
  }
3073
+ function getRectBrushBounds(centerX, centerY, brushWidth, brushHeight, targetWidth, targetHeight) {
3074
+ const rawStartX = Math.floor(centerX - brushWidth / 2);
3075
+ const rawStartY = Math.floor(centerY - brushHeight / 2);
3076
+ const rawEndX = rawStartX + brushWidth;
3077
+ const rawEndY = rawStartY + brushHeight;
3078
+ const startX = targetWidth !== void 0 ? Math.max(0, rawStartX) : rawStartX;
3079
+ const startY = targetHeight !== void 0 ? Math.max(0, rawStartY) : rawStartY;
3080
+ const endX = targetWidth !== void 0 ? Math.min(targetWidth, rawEndX) : rawEndX;
3081
+ const endY = targetHeight !== void 0 ? Math.min(targetHeight, rawEndY) : rawEndY;
3082
+ return {
3083
+ x: startX,
3084
+ y: startY,
3085
+ w: endX - startX,
3086
+ h: endY - startY
3087
+ };
3088
+ }
2454
3089
 
2455
3090
  // src/PixelData/clearPixelData.ts
2456
3091
  function clearPixelData(dst, rect) {
@@ -2498,16 +3133,6 @@ function extractPixelData(source, _x, _y, _w, _h) {
2498
3133
  return result;
2499
3134
  }
2500
3135
 
2501
- // src/PixelData/invertPixelData.ts
2502
- function invertPixelData(pixelData) {
2503
- const data32 = pixelData.data32;
2504
- const len = data32.length;
2505
- for (let i = 0; i < len; i++) {
2506
- data32[i] = data32[i] ^ 16777215;
2507
- }
2508
- return pixelData;
2509
- }
2510
-
2511
3136
  // src/PixelData/reflectPixelData.ts
2512
3137
  function reflectPixelDataHorizontal(pixelData) {
2513
3138
  const width = pixelData.width;
@@ -2598,23 +3223,53 @@ function rotateSquareInPlace(pixelData) {
2598
3223
  }
2599
3224
  }
2600
3225
  }
3226
+
3227
+ // src/PixelData/writePixelDataBuffer.ts
3228
+ function writePixelDataBuffer(target, data, _x, _y, _w, _h) {
3229
+ const { x, y, w, h } = typeof _x === "object" ? _x : {
3230
+ x: _x,
3231
+ y: _y,
3232
+ w: _w,
3233
+ h: _h
3234
+ };
3235
+ const dstW = target.width;
3236
+ const dstH = target.height;
3237
+ const dstData = target.data32;
3238
+ const x0 = Math.max(0, x);
3239
+ const y0 = Math.max(0, y);
3240
+ const x1 = Math.min(dstW, x + w);
3241
+ const y1 = Math.min(dstH, y + h);
3242
+ if (x1 <= x0 || y1 <= y0) {
3243
+ return;
3244
+ }
3245
+ const rowLen = x1 - x0;
3246
+ const srcCol = x0 - x;
3247
+ const srcYOffset = y0 - y;
3248
+ const actualH = y1 - y0;
3249
+ for (let row = 0; row < actualH; row++) {
3250
+ const dstStart = (y0 + row) * dstW + x0;
3251
+ const srcRow = srcYOffset + row;
3252
+ const srcStart = srcRow * w + srcCol;
3253
+ dstData.set(data.subarray(srcStart, srcStart + rowLen), dstStart);
3254
+ }
3255
+ }
2601
3256
  export {
2602
- BlendMode,
2603
- FAST_BLENDER_REGISTRY,
2604
- FAST_BLEND_MODES,
2605
- FAST_BLEND_MODE_BY_NAME,
2606
- FAST_BLEND_TO_INDEX,
2607
- INDEX_TO_FAST_BLEND,
2608
- INDEX_TO_PERFECT_BLEND,
3257
+ BASE_FAST_BLEND_MODE_FUNCTIONS,
3258
+ BASE_PERFECT_BLEND_MODE_FUNCTIONS,
3259
+ BaseBlendMode,
3260
+ HistoryManager,
2609
3261
  IndexedImage,
2610
3262
  MaskType,
2611
- PERFECT_BLENDER_REGISTRY,
2612
- PERFECT_BLEND_MODES,
2613
- PERFECT_BLEND_MODE_BY_NAME,
2614
- PERFECT_BLEND_TO_INDEX,
3263
+ PixelAccumulator,
2615
3264
  PixelData,
3265
+ PixelEngineConfig,
3266
+ PixelTile,
3267
+ PixelWriter,
2616
3268
  UnsupportedFormatError,
3269
+ applyCircleBrushToPixelData,
2617
3270
  applyMaskToPixelData,
3271
+ applyPatchTiles,
3272
+ applyRectBrushToPixelData,
2618
3273
  base64DecodeArrayBuffer,
2619
3274
  base64EncodeArrayBuffer,
2620
3275
  blendColorPixelData,
@@ -2653,6 +3308,7 @@ export {
2653
3308
  floodFillSelection,
2654
3309
  getImageDataFromClipboard,
2655
3310
  getIndexedImageColorCounts,
3311
+ getRectBrushBounds,
2656
3312
  getSupportedPixelFormats,
2657
3313
  hardLightFast,
2658
3314
  hardLightPerfect,
@@ -2681,14 +3337,25 @@ export {
2681
3337
  linearDodgePerfect,
2682
3338
  linearLightFast,
2683
3339
  linearLightPerfect,
3340
+ makeBlendModeRegistry,
3341
+ makeFastBlendModeRegistry,
3342
+ makeFullPixelMutator,
3343
+ makePerfectBlendModeRegistry,
2684
3344
  makePixelCanvas,
2685
3345
  makeReusableCanvas,
2686
3346
  makeReusableImageData,
2687
3347
  mergeMasks,
2688
3348
  multiplyFast,
2689
3349
  multiplyPerfect,
3350
+ mutatorApplyMask,
3351
+ mutatorBlendColor,
3352
+ mutatorBlendPixel,
3353
+ mutatorBlendPixelData,
3354
+ mutatorFill,
3355
+ mutatorInvert,
2690
3356
  overlayFast,
2691
3357
  overlayPerfect,
3358
+ overwriteBase,
2692
3359
  overwriteFast,
2693
3360
  overwritePerfect,
2694
3361
  packColor,
@@ -2723,8 +3390,9 @@ export {
2723
3390
  vividLightFast,
2724
3391
  vividLightPerfect,
2725
3392
  writeImageData,
2726
- writeImageDataPixels,
3393
+ writeImageDataBuffer,
2727
3394
  writeImageDataToClipboard,
2728
- writeImgBlobToClipboard
3395
+ writeImgBlobToClipboard,
3396
+ writePixelDataBuffer
2729
3397
  };
2730
3398
  //# sourceMappingURL=index.dev.js.map