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
@@ -20,22 +20,22 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/index.ts
21
21
  var src_exports = {};
22
22
  __export(src_exports, {
23
- BlendMode: () => BlendMode,
24
- FAST_BLENDER_REGISTRY: () => FAST_BLENDER_REGISTRY,
25
- FAST_BLEND_MODES: () => FAST_BLEND_MODES,
26
- FAST_BLEND_MODE_BY_NAME: () => FAST_BLEND_MODE_BY_NAME,
27
- FAST_BLEND_TO_INDEX: () => FAST_BLEND_TO_INDEX,
28
- INDEX_TO_FAST_BLEND: () => INDEX_TO_FAST_BLEND,
29
- INDEX_TO_PERFECT_BLEND: () => INDEX_TO_PERFECT_BLEND,
23
+ BASE_FAST_BLEND_MODE_FUNCTIONS: () => BASE_FAST_BLEND_MODE_FUNCTIONS,
24
+ BASE_PERFECT_BLEND_MODE_FUNCTIONS: () => BASE_PERFECT_BLEND_MODE_FUNCTIONS,
25
+ BaseBlendMode: () => BaseBlendMode,
26
+ HistoryManager: () => HistoryManager,
30
27
  IndexedImage: () => IndexedImage,
31
28
  MaskType: () => MaskType,
32
- PERFECT_BLENDER_REGISTRY: () => PERFECT_BLENDER_REGISTRY,
33
- PERFECT_BLEND_MODES: () => PERFECT_BLEND_MODES,
34
- PERFECT_BLEND_MODE_BY_NAME: () => PERFECT_BLEND_MODE_BY_NAME,
35
- PERFECT_BLEND_TO_INDEX: () => PERFECT_BLEND_TO_INDEX,
29
+ PixelAccumulator: () => PixelAccumulator,
36
30
  PixelData: () => PixelData,
31
+ PixelEngineConfig: () => PixelEngineConfig,
32
+ PixelTile: () => PixelTile,
33
+ PixelWriter: () => PixelWriter,
37
34
  UnsupportedFormatError: () => UnsupportedFormatError,
35
+ applyCircleBrushToPixelData: () => applyCircleBrushToPixelData,
38
36
  applyMaskToPixelData: () => applyMaskToPixelData,
37
+ applyPatchTiles: () => applyPatchTiles,
38
+ applyRectBrushToPixelData: () => applyRectBrushToPixelData,
39
39
  base64DecodeArrayBuffer: () => base64DecodeArrayBuffer,
40
40
  base64EncodeArrayBuffer: () => base64EncodeArrayBuffer,
41
41
  blendColorPixelData: () => blendColorPixelData,
@@ -74,6 +74,7 @@ __export(src_exports, {
74
74
  floodFillSelection: () => floodFillSelection,
75
75
  getImageDataFromClipboard: () => getImageDataFromClipboard,
76
76
  getIndexedImageColorCounts: () => getIndexedImageColorCounts,
77
+ getRectBrushBounds: () => getRectBrushBounds,
77
78
  getSupportedPixelFormats: () => getSupportedPixelFormats,
78
79
  hardLightFast: () => hardLightFast,
79
80
  hardLightPerfect: () => hardLightPerfect,
@@ -102,14 +103,25 @@ __export(src_exports, {
102
103
  linearDodgePerfect: () => linearDodgePerfect,
103
104
  linearLightFast: () => linearLightFast,
104
105
  linearLightPerfect: () => linearLightPerfect,
106
+ makeBlendModeRegistry: () => makeBlendModeRegistry,
107
+ makeFastBlendModeRegistry: () => makeFastBlendModeRegistry,
108
+ makeFullPixelMutator: () => makeFullPixelMutator,
109
+ makePerfectBlendModeRegistry: () => makePerfectBlendModeRegistry,
105
110
  makePixelCanvas: () => makePixelCanvas,
106
111
  makeReusableCanvas: () => makeReusableCanvas,
107
112
  makeReusableImageData: () => makeReusableImageData,
108
113
  mergeMasks: () => mergeMasks,
109
114
  multiplyFast: () => multiplyFast,
110
115
  multiplyPerfect: () => multiplyPerfect,
116
+ mutatorApplyMask: () => mutatorApplyMask,
117
+ mutatorBlendColor: () => mutatorBlendColor,
118
+ mutatorBlendPixel: () => mutatorBlendPixel,
119
+ mutatorBlendPixelData: () => mutatorBlendPixelData,
120
+ mutatorFill: () => mutatorFill,
121
+ mutatorInvert: () => mutatorInvert,
111
122
  overlayFast: () => overlayFast,
112
123
  overlayPerfect: () => overlayPerfect,
124
+ overwriteBase: () => overwriteBase,
113
125
  overwriteFast: () => overwriteFast,
114
126
  overwritePerfect: () => overwritePerfect,
115
127
  packColor: () => packColor,
@@ -144,42 +156,80 @@ __export(src_exports, {
144
156
  vividLightFast: () => vividLightFast,
145
157
  vividLightPerfect: () => vividLightPerfect,
146
158
  writeImageData: () => writeImageData,
147
- writeImageDataPixels: () => writeImageDataPixels,
159
+ writeImageDataBuffer: () => writeImageDataBuffer,
148
160
  writeImageDataToClipboard: () => writeImageDataToClipboard,
149
- writeImgBlobToClipboard: () => writeImgBlobToClipboard
161
+ writeImgBlobToClipboard: () => writeImgBlobToClipboard,
162
+ writePixelDataBuffer: () => writePixelDataBuffer
150
163
  });
151
164
  module.exports = __toCommonJS(src_exports);
152
165
 
153
166
  // src/BlendModes/blend-modes.ts
154
- var BlendMode = /* @__PURE__ */ ((BlendMode2) => {
155
- BlendMode2[BlendMode2["overwrite"] = 0] = "overwrite";
156
- BlendMode2[BlendMode2["sourceOver"] = 1] = "sourceOver";
157
- BlendMode2[BlendMode2["darken"] = 2] = "darken";
158
- BlendMode2[BlendMode2["multiply"] = 3] = "multiply";
159
- BlendMode2[BlendMode2["colorBurn"] = 4] = "colorBurn";
160
- BlendMode2[BlendMode2["linearBurn"] = 5] = "linearBurn";
161
- BlendMode2[BlendMode2["darkerColor"] = 6] = "darkerColor";
162
- BlendMode2[BlendMode2["lighten"] = 7] = "lighten";
163
- BlendMode2[BlendMode2["screen"] = 8] = "screen";
164
- BlendMode2[BlendMode2["colorDodge"] = 9] = "colorDodge";
165
- BlendMode2[BlendMode2["linearDodge"] = 10] = "linearDodge";
166
- BlendMode2[BlendMode2["lighterColor"] = 11] = "lighterColor";
167
- BlendMode2[BlendMode2["overlay"] = 12] = "overlay";
168
- BlendMode2[BlendMode2["softLight"] = 13] = "softLight";
169
- BlendMode2[BlendMode2["hardLight"] = 14] = "hardLight";
170
- BlendMode2[BlendMode2["vividLight"] = 15] = "vividLight";
171
- BlendMode2[BlendMode2["linearLight"] = 16] = "linearLight";
172
- BlendMode2[BlendMode2["pinLight"] = 17] = "pinLight";
173
- BlendMode2[BlendMode2["hardMix"] = 18] = "hardMix";
174
- BlendMode2[BlendMode2["difference"] = 19] = "difference";
175
- BlendMode2[BlendMode2["exclusion"] = 20] = "exclusion";
176
- BlendMode2[BlendMode2["subtract"] = 21] = "subtract";
177
- BlendMode2[BlendMode2["divide"] = 22] = "divide";
178
- return BlendMode2;
179
- })(BlendMode || {});
167
+ var BaseBlendMode = {
168
+ overwrite: 0,
169
+ sourceOver: 1,
170
+ darken: 2,
171
+ multiply: 3,
172
+ colorBurn: 4,
173
+ linearBurn: 5,
174
+ darkerColor: 6,
175
+ lighten: 7,
176
+ screen: 8,
177
+ colorDodge: 9,
178
+ linearDodge: 10,
179
+ lighterColor: 11,
180
+ overlay: 12,
181
+ softLight: 13,
182
+ hardLight: 14,
183
+ vividLight: 15,
184
+ linearLight: 16,
185
+ pinLight: 17,
186
+ hardMix: 18,
187
+ difference: 19,
188
+ exclusion: 20,
189
+ subtract: 21,
190
+ divide: 22
191
+ };
180
192
  var overwriteBase = (src, _dst) => src;
181
193
  overwriteBase.isOverwrite = true;
182
194
 
195
+ // src/BlendModes/BlendModeRegistry.ts
196
+ function makeBlendModeRegistry(blendModes, initialEntries) {
197
+ const blendToName = /* @__PURE__ */ new Map();
198
+ const blendToIndex = /* @__PURE__ */ new Map();
199
+ const indexToName = [];
200
+ const indexToBlend = [];
201
+ const nameToBlend = {};
202
+ const nameToIndex = {};
203
+ const add = (name, index, blendFn) => {
204
+ if (!Number.isFinite(index)) {
205
+ throw new Error(`Index "${index}" is not a number. Attempting to add name: "${name}", index: "${index}"`);
206
+ }
207
+ if (indexToBlend[index]) {
208
+ throw new Error(`Blend Mode index: ${index} is already used. Attempting to add name: "${name}", index: "${index}"`);
209
+ }
210
+ indexToName[index] = name;
211
+ indexToBlend[index] = blendFn;
212
+ blendToIndex.set(blendFn, index);
213
+ blendToName.set(blendFn, name);
214
+ nameToBlend[name] = blendFn;
215
+ nameToIndex[name] = index;
216
+ };
217
+ for (const [name, index] of Object.entries(blendModes)) {
218
+ const blend = initialEntries[index];
219
+ add(name, index, blend);
220
+ }
221
+ return {
222
+ nameToBlend,
223
+ nameToIndex,
224
+ blendToIndex,
225
+ blendToName,
226
+ indexToBlend,
227
+ indexToName,
228
+ indexType: null,
229
+ nameType: null
230
+ };
231
+ }
232
+
183
233
  // src/BlendModes/blend-modes-fast.ts
184
234
  var overwriteFast = overwriteBase;
185
235
  var sourceOverFast = (src, dst) => {
@@ -565,76 +615,34 @@ var divideFast = (src, dst) => {
565
615
  const a = 255 * sa + (dst >>> 24 & 255) * invA >> 8;
566
616
  return (a << 24 | b << 16 | g << 8 | r) >>> 0;
567
617
  };
568
- var FAST_BLENDER_REGISTRY = [
569
- [0 /* overwrite */, overwriteFast],
570
- [1 /* sourceOver */, sourceOverFast],
571
- [2 /* darken */, darkenFast],
572
- [3 /* multiply */, multiplyFast],
573
- [4 /* colorBurn */, colorBurnFast],
574
- [5 /* linearBurn */, linearBurnFast],
575
- [6 /* darkerColor */, darkerFast],
576
- [7 /* lighten */, lightenFast],
577
- [8 /* screen */, screenFast],
578
- [9 /* colorDodge */, colorDodgeFast],
579
- [10 /* linearDodge */, linearDodgeFast],
580
- [11 /* lighterColor */, lighterFast],
581
- [12 /* overlay */, overlayFast],
582
- [13 /* softLight */, softLightFast],
583
- [14 /* hardLight */, hardLightFast],
584
- [15 /* vividLight */, vividLightFast],
585
- [16 /* linearLight */, linearLightFast],
586
- [17 /* pinLight */, pinLightFast],
587
- [18 /* hardMix */, hardMixFast],
588
- [19 /* difference */, differenceFast],
589
- [20 /* exclusion */, exclusionFast],
590
- [21 /* subtract */, subtractFast],
591
- [22 /* divide */, divideFast]
592
- ];
593
- var FAST_BLEND_MODES = [];
594
- for (const [index, blend] of FAST_BLENDER_REGISTRY) {
595
- FAST_BLEND_MODES[index] = blend;
596
- }
597
- var FAST_BLEND_TO_INDEX = new Map(
598
- FAST_BLENDER_REGISTRY.map((entry, index) => {
599
- return [
600
- entry[1],
601
- index
602
- ];
603
- })
604
- );
605
- var INDEX_TO_FAST_BLEND = new Map(
606
- FAST_BLENDER_REGISTRY.map((entry, index) => {
607
- return [
608
- index,
609
- entry[1]
610
- ];
611
- })
612
- );
613
- var FAST_BLEND_MODE_BY_NAME = {
614
- overwrite: overwriteFast,
615
- sourceOver: sourceOverFast,
616
- darken: darkenFast,
617
- multiply: multiplyFast,
618
- colorBurn: colorBurnFast,
619
- linearBurn: linearBurnFast,
620
- darkerColor: darkerFast,
621
- lighten: lightenFast,
622
- screen: screenFast,
623
- colorDodge: colorDodgeFast,
624
- linearDodge: linearDodgeFast,
625
- lighterColor: lighterFast,
626
- overlay: overlayFast,
627
- softLight: softLightFast,
628
- hardLight: hardLightFast,
629
- vividLight: vividLightFast,
630
- linearLight: linearLightFast,
631
- pinLight: pinLightFast,
632
- hardMix: hardMixFast,
633
- difference: differenceFast,
634
- exclusion: exclusionFast,
635
- subtract: subtractFast,
636
- divide: divideFast
618
+ var BASE_FAST_BLEND_MODE_FUNCTIONS = {
619
+ [BaseBlendMode.overwrite]: overwriteFast,
620
+ [BaseBlendMode.sourceOver]: sourceOverFast,
621
+ [BaseBlendMode.darken]: darkenFast,
622
+ [BaseBlendMode.multiply]: multiplyFast,
623
+ [BaseBlendMode.colorBurn]: colorBurnFast,
624
+ [BaseBlendMode.linearBurn]: linearBurnFast,
625
+ [BaseBlendMode.darkerColor]: darkerFast,
626
+ [BaseBlendMode.lighten]: lightenFast,
627
+ [BaseBlendMode.screen]: screenFast,
628
+ [BaseBlendMode.colorDodge]: colorDodgeFast,
629
+ [BaseBlendMode.linearDodge]: linearDodgeFast,
630
+ [BaseBlendMode.lighterColor]: lighterFast,
631
+ [BaseBlendMode.overlay]: overlayFast,
632
+ [BaseBlendMode.softLight]: softLightFast,
633
+ [BaseBlendMode.hardLight]: hardLightFast,
634
+ [BaseBlendMode.vividLight]: vividLightFast,
635
+ [BaseBlendMode.linearLight]: linearLightFast,
636
+ [BaseBlendMode.pinLight]: pinLightFast,
637
+ [BaseBlendMode.hardMix]: hardMixFast,
638
+ [BaseBlendMode.difference]: differenceFast,
639
+ [BaseBlendMode.exclusion]: exclusionFast,
640
+ [BaseBlendMode.subtract]: subtractFast,
641
+ [BaseBlendMode.divide]: divideFast
637
642
  };
643
+ function makeFastBlendModeRegistry() {
644
+ return makeBlendModeRegistry(BaseBlendMode, BASE_FAST_BLEND_MODE_FUNCTIONS);
645
+ }
638
646
 
639
647
  // src/_types.ts
640
648
  var MaskType = /* @__PURE__ */ ((MaskType2) => {
@@ -1001,14 +1009,18 @@ var sourceOverPerfect = (src, dst) => {
1001
1009
  const sa = src >>> 24 & 255;
1002
1010
  if (sa === 255) return src;
1003
1011
  if (sa === 0) return dst;
1012
+ const invA = 255 - sa;
1004
1013
  const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
1005
1014
  const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
1006
1015
  const da = dst >>> 24 & 255;
1007
- const invA = 255 - sa;
1008
- const r = (sr * sa + dr * invA) / 255 | 0;
1009
- const g = (sg * sa + dg * invA) / 255 | 0;
1010
- const b = (sb * sa + db * invA) / 255 | 0;
1011
- const a = (255 * sa + da * invA) / 255 | 0;
1016
+ const tR = sr * sa + dr * invA;
1017
+ const r = tR + 1 + (tR >> 8) >> 8;
1018
+ const tG = sg * sa + dg * invA;
1019
+ const g = tG + 1 + (tG >> 8) >> 8;
1020
+ const tB = sb * sa + db * invA;
1021
+ const b = tB + 1 + (tB >> 8) >> 8;
1022
+ const tA = 255 * sa + da * invA;
1023
+ const a = tA + 1 + (tA >> 8) >> 8;
1012
1024
  return (a << 24 | b << 16 | g << 8 | r) >>> 0;
1013
1025
  };
1014
1026
  var darkenPerfect = (src, dst) => {
@@ -1021,44 +1033,71 @@ var darkenPerfect = (src, dst) => {
1021
1033
  const bb = sb < db ? sb : db;
1022
1034
  if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
1023
1035
  const invA = 255 - sa;
1024
- const r = (br * sa + dr * invA) / 255 | 0;
1025
- const g = (bg * sa + dg * invA) / 255 | 0;
1026
- const b = (bb * sa + db * invA) / 255 | 0;
1027
- const a = (255 * sa + (dst >>> 24 & 255) * invA) / 255 | 0;
1036
+ const da = dst >>> 24 & 255;
1037
+ const tR = br * sa + dr * invA;
1038
+ const r = tR + 1 + (tR >> 8) >> 8;
1039
+ const tG = bg * sa + dg * invA;
1040
+ const g = tG + 1 + (tG >> 8) >> 8;
1041
+ const tB = bb * sa + db * invA;
1042
+ const b = tB + 1 + (tB >> 8) >> 8;
1043
+ const tA = 255 * sa + da * invA;
1044
+ const a = tA + 1 + (tA >> 8) >> 8;
1028
1045
  return (a << 24 | b << 16 | g << 8 | r) >>> 0;
1029
1046
  };
1030
1047
  var multiplyPerfect = (src, dst) => {
1031
1048
  const sa = src >>> 24 & 255;
1032
1049
  if (sa === 0) return dst;
1033
- const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
1034
- const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
1035
- const br = sr * dr / 255 | 0;
1036
- const bg = sg * dg / 255 | 0;
1037
- const bb = sb * db / 255 | 0;
1050
+ const dr = dst & 255;
1051
+ const dg = dst >>> 8 & 255;
1052
+ const db = dst >>> 16 & 255;
1053
+ const da = dst >>> 24 & 255;
1054
+ const sr = src & 255;
1055
+ const sg = src >>> 8 & 255;
1056
+ const sb = src >>> 16 & 255;
1057
+ const mR = sr * dr;
1058
+ const br = mR + 1 + (mR >> 8) >> 8;
1059
+ const mG = sg * dg;
1060
+ const bg = mG + 1 + (mG >> 8) >> 8;
1061
+ const mB = sb * db;
1062
+ const bb = mB + 1 + (mB >> 8) >> 8;
1038
1063
  if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
1039
1064
  const invA = 255 - sa;
1040
- const da = dst >>> 24 & 255;
1041
- const r = (br * sa + dr * invA) / 255 | 0;
1042
- const g = (bg * sa + dg * invA) / 255 | 0;
1043
- const b = (bb * sa + db * invA) / 255 | 0;
1044
- const a = (255 * sa + da * invA) / 255 | 0;
1065
+ const tR = br * sa + dr * invA;
1066
+ const r = tR + 1 + (tR >> 8) >> 8;
1067
+ const tG = bg * sa + dg * invA;
1068
+ const g = tG + 1 + (tG >> 8) >> 8;
1069
+ const tB = bb * sa + db * invA;
1070
+ const b = tB + 1 + (tB >> 8) >> 8;
1071
+ const tA = 255 * sa + da * invA;
1072
+ const a = tA + 1 + (tA >> 8) >> 8;
1045
1073
  return (a << 24 | b << 16 | g << 8 | r) >>> 0;
1046
1074
  };
1047
1075
  var colorBurnPerfect = (src, dst) => {
1048
1076
  const sa = src >>> 24 & 255;
1049
1077
  if (sa === 0) return dst;
1050
- const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
1051
- const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
1052
- const br = dr === 255 ? 255 : sr === 0 ? 0 : Math.max(0, 255 - ((255 - dr) * 255 / sr | 0));
1053
- const bg = dg === 255 ? 255 : sg === 0 ? 0 : Math.max(0, 255 - ((255 - dg) * 255 / sg | 0));
1054
- const bb = db === 255 ? 255 : sb === 0 ? 0 : Math.max(0, 255 - ((255 - db) * 255 / sb | 0));
1078
+ const dr = dst & 255;
1079
+ const dg = dst >>> 8 & 255;
1080
+ const db = dst >>> 16 & 255;
1081
+ const sr = src & 255;
1082
+ const sg = src >>> 8 & 255;
1083
+ const sb = src >>> 16 & 255;
1084
+ const resR = dr === 255 ? 255 : sr === 0 ? 0 : 255 - ((255 - dr) * 255 / sr | 0);
1085
+ const br = resR < 0 ? 0 : resR;
1086
+ const resG = dg === 255 ? 255 : sg === 0 ? 0 : 255 - ((255 - dg) * 255 / sg | 0);
1087
+ const bg = resG < 0 ? 0 : resG;
1088
+ const resB = db === 255 ? 255 : sb === 0 ? 0 : 255 - ((255 - db) * 255 / sb | 0);
1089
+ const bb = resB < 0 ? 0 : resB;
1055
1090
  if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
1056
1091
  const invA = 255 - sa;
1057
1092
  const da = dst >>> 24 & 255;
1058
- const r = (br * sa + dr * invA) / 255 | 0;
1059
- const g = (bg * sa + dg * invA) / 255 | 0;
1060
- const b = (bb * sa + db * invA) / 255 | 0;
1061
- const a = (255 * sa + da * invA) / 255 | 0;
1093
+ const tR = br * sa + dr * invA;
1094
+ const r = tR + 1 + (tR >> 8) >> 8;
1095
+ const tG = bg * sa + dg * invA;
1096
+ const g = tG + 1 + (tG >> 8) >> 8;
1097
+ const tB = bb * sa + db * invA;
1098
+ const b = tB + 1 + (tB >> 8) >> 8;
1099
+ const tA = 255 * sa + da * invA;
1100
+ const a = tA + 1 + (tA >> 8) >> 8;
1062
1101
  return (a << 24 | b << 16 | g << 8 | r) >>> 0;
1063
1102
  };
1064
1103
  var linearBurnPerfect = (src, dst) => {
@@ -1074,10 +1113,15 @@ var linearBurnPerfect = (src, dst) => {
1074
1113
  const bb = bbU < 0 ? 0 : bbU;
1075
1114
  if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
1076
1115
  const invA = 255 - sa;
1077
- const r = (br * sa + dr * invA) / 255 | 0;
1078
- const g = (bg * sa + dg * invA) / 255 | 0;
1079
- const b = (bb * sa + db * invA) / 255 | 0;
1080
- const a = (255 * sa + (dst >>> 24 & 255) * invA) / 255 | 0;
1116
+ const da = dst >>> 24 & 255;
1117
+ const tR = br * sa + dr * invA;
1118
+ const r = tR + 1 + (tR >> 8) >> 8;
1119
+ const tG = bg * sa + dg * invA;
1120
+ const g = tG + 1 + (tG >> 8) >> 8;
1121
+ const tB = bb * sa + db * invA;
1122
+ const b = tB + 1 + (tB >> 8) >> 8;
1123
+ const tA = 255 * sa + da * invA;
1124
+ const a = tA + 1 + (tA >> 8) >> 8;
1081
1125
  return (a << 24 | b << 16 | g << 8 | r) >>> 0;
1082
1126
  };
1083
1127
  var darkerPerfect = (src, dst) => {
@@ -1099,10 +1143,15 @@ var darkerPerfect = (src, dst) => {
1099
1143
  }
1100
1144
  if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
1101
1145
  const invA = 255 - sa;
1102
- const r = (br * sa + dr * invA) / 255 | 0;
1103
- const g = (bg * sa + dg * invA) / 255 | 0;
1104
- const b = (bb * sa + db * invA) / 255 | 0;
1105
- const a = (255 * sa + (dst >>> 24 & 255) * invA) / 255 | 0;
1146
+ const da = dst >>> 24 & 255;
1147
+ const tR = br * sa + dr * invA;
1148
+ const r = tR + 1 + (tR >> 8) >> 8;
1149
+ const tG = bg * sa + dg * invA;
1150
+ const g = tG + 1 + (tG >> 8) >> 8;
1151
+ const tB = bb * sa + db * invA;
1152
+ const b = tB + 1 + (tB >> 8) >> 8;
1153
+ const tA = 255 * sa + da * invA;
1154
+ const a = tA + 1 + (tA >> 8) >> 8;
1106
1155
  return (a << 24 | b << 16 | g << 8 | r) >>> 0;
1107
1156
  };
1108
1157
  var lightenPerfect = (src, dst) => {
@@ -1114,10 +1163,15 @@ var lightenPerfect = (src, dst) => {
1114
1163
  const bb = (src >>> 16 & 255) > db ? src >>> 16 & 255 : db;
1115
1164
  if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
1116
1165
  const invA = 255 - sa;
1117
- const r = (br * sa + dr * invA) / 255 | 0;
1118
- const g = (bg * sa + dg * invA) / 255 | 0;
1119
- const b = (bb * sa + db * invA) / 255 | 0;
1120
- const a = (255 * sa + (dst >>> 24 & 255) * invA) / 255 | 0;
1166
+ const da = dst >>> 24 & 255;
1167
+ const tR = br * sa + dr * invA;
1168
+ const r = tR + 1 + (tR >> 8) >> 8;
1169
+ const tG = bg * sa + dg * invA;
1170
+ const g = tG + 1 + (tG >> 8) >> 8;
1171
+ const tB = bb * sa + db * invA;
1172
+ const b = tB + 1 + (tB >> 8) >> 8;
1173
+ const tA = 255 * sa + da * invA;
1174
+ const a = tA + 1 + (tA >> 8) >> 8;
1121
1175
  return (a << 24 | b << 16 | g << 8 | r) >>> 0;
1122
1176
  };
1123
1177
  var screenPerfect = (src, dst) => {
@@ -1129,26 +1183,43 @@ var screenPerfect = (src, dst) => {
1129
1183
  const bb = 255 - ((255 - (src >>> 16 & 255)) * (255 - db) / 255 | 0);
1130
1184
  if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
1131
1185
  const invA = 255 - sa;
1132
- const r = (br * sa + dr * invA) / 255 | 0;
1133
- const g = (bg * sa + dg * invA) / 255 | 0;
1134
- const b = (bb * sa + db * invA) / 255 | 0;
1135
- const a = (255 * sa + (dst >>> 24 & 255) * invA) / 255 | 0;
1186
+ const da = dst >>> 24 & 255;
1187
+ const tR = br * sa + dr * invA;
1188
+ const r = tR + 1 + (tR >> 8) >> 8;
1189
+ const tG = bg * sa + dg * invA;
1190
+ const g = tG + 1 + (tG >> 8) >> 8;
1191
+ const tB = bb * sa + db * invA;
1192
+ const b = tB + 1 + (tB >> 8) >> 8;
1193
+ const tA = 255 * sa + da * invA;
1194
+ const a = tA + 1 + (tA >> 8) >> 8;
1136
1195
  return (a << 24 | b << 16 | g << 8 | r) >>> 0;
1137
1196
  };
1138
1197
  var colorDodgePerfect = (src, dst) => {
1139
1198
  const sa = src >>> 24 & 255;
1140
1199
  if (sa === 0) return dst;
1141
- const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
1142
- const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
1143
- const br = sr === 255 ? 255 : Math.min(255, dr * 255 / (255 - sr) | 0);
1144
- const bg = sg === 255 ? 255 : Math.min(255, dg * 255 / (255 - sg) | 0);
1145
- const bb = sb === 255 ? 255 : Math.min(255, db * 255 / (255 - sb) | 0);
1200
+ const dr = dst & 255;
1201
+ const dg = dst >>> 8 & 255;
1202
+ const db = dst >>> 16 & 255;
1203
+ const sr = src & 255;
1204
+ const sg = src >>> 8 & 255;
1205
+ const sb = src >>> 16 & 255;
1206
+ const resR = sr === 255 ? 255 : dr * 255 / (255 - sr) | 0;
1207
+ const br = resR > 255 ? 255 : resR;
1208
+ const resG = sg === 255 ? 255 : dg * 255 / (255 - sg) | 0;
1209
+ const bg = resG > 255 ? 255 : resG;
1210
+ const resB = sb === 255 ? 255 : db * 255 / (255 - sb) | 0;
1211
+ const bb = resB > 255 ? 255 : resB;
1146
1212
  if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
1147
1213
  const invA = 255 - sa;
1148
- const r = (br * sa + dr * invA) / 255 | 0;
1149
- const g = (bg * sa + dg * invA) / 255 | 0;
1150
- const b = (bb * sa + db * invA) / 255 | 0;
1151
- const a = (255 * sa + (dst >>> 24 & 255) * invA) / 255 | 0;
1214
+ const da = dst >>> 24 & 255;
1215
+ const tR = br * sa + dr * invA;
1216
+ const r = tR + 1 + (tR >> 8) >> 8;
1217
+ const tG = bg * sa + dg * invA;
1218
+ const g = tG + 1 + (tG >> 8) >> 8;
1219
+ const tB = bb * sa + db * invA;
1220
+ const b = tB + 1 + (tB >> 8) >> 8;
1221
+ const tA = 255 * sa + da * invA;
1222
+ const a = tA + 1 + (tA >> 8) >> 8;
1152
1223
  return (a << 24 | b << 16 | g << 8 | r) >>> 0;
1153
1224
  };
1154
1225
  var linearDodgePerfect = (src, dst) => {
@@ -1163,10 +1234,15 @@ var linearDodgePerfect = (src, dst) => {
1163
1234
  const bb = bbU > 255 ? 255 : bbU;
1164
1235
  if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
1165
1236
  const invA = 255 - sa;
1166
- const r = (br * sa + dr * invA) / 255 | 0;
1167
- const g = (bg * sa + dg * invA) / 255 | 0;
1168
- const b = (bb * sa + db * invA) / 255 | 0;
1169
- const a = (255 * sa + (dst >>> 24 & 255) * invA) / 255 | 0;
1237
+ const da = dst >>> 24 & 255;
1238
+ const tR = br * sa + dr * invA;
1239
+ const r = tR + 1 + (tR >> 8) >> 8;
1240
+ const tG = bg * sa + dg * invA;
1241
+ const g = tG + 1 + (tG >> 8) >> 8;
1242
+ const tB = bb * sa + db * invA;
1243
+ const b = tB + 1 + (tB >> 8) >> 8;
1244
+ const tA = 255 * sa + da * invA;
1245
+ const a = tA + 1 + (tA >> 8) >> 8;
1170
1246
  return (a << 24 | b << 16 | g << 8 | r) >>> 0;
1171
1247
  };
1172
1248
  var lighterPerfect = (src, dst) => {
@@ -1188,10 +1264,15 @@ var lighterPerfect = (src, dst) => {
1188
1264
  }
1189
1265
  if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
1190
1266
  const invA = 255 - sa;
1191
- const r = (br * sa + dr * invA) / 255 | 0;
1192
- const g = (bg * sa + dg * invA) / 255 | 0;
1193
- const b = (bb * sa + db * invA) / 255 | 0;
1194
- const a = (255 * sa + (dst >>> 24 & 255) * invA) / 255 | 0;
1267
+ const da = dst >>> 24 & 255;
1268
+ const tR = br * sa + dr * invA;
1269
+ const r = tR + 1 + (tR >> 8) >> 8;
1270
+ const tG = bg * sa + dg * invA;
1271
+ const g = tG + 1 + (tG >> 8) >> 8;
1272
+ const tB = bb * sa + db * invA;
1273
+ const b = tB + 1 + (tB >> 8) >> 8;
1274
+ const tA = 255 * sa + da * invA;
1275
+ const a = tA + 1 + (tA >> 8) >> 8;
1195
1276
  return (a << 24 | b << 16 | g << 8 | r) >>> 0;
1196
1277
  };
1197
1278
  var overlayPerfect = (src, dst) => {
@@ -1204,10 +1285,15 @@ var overlayPerfect = (src, dst) => {
1204
1285
  const bb = db < 128 ? 2 * sb * db / 255 | 0 : 255 - (2 * (255 - sb) * (255 - db) / 255 | 0);
1205
1286
  if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
1206
1287
  const invA = 255 - sa;
1207
- const r = (br * sa + dr * invA) / 255 | 0;
1208
- const g = (bg * sa + dg * invA) / 255 | 0;
1209
- const b = (bb * sa + db * invA) / 255 | 0;
1210
- const a = (255 * sa + (dst >>> 24 & 255) * invA) / 255 | 0;
1288
+ const da = dst >>> 24 & 255;
1289
+ const tR = br * sa + dr * invA;
1290
+ const r = tR + 1 + (tR >> 8) >> 8;
1291
+ const tG = bg * sa + dg * invA;
1292
+ const g = tG + 1 + (tG >> 8) >> 8;
1293
+ const tB = bb * sa + db * invA;
1294
+ const b = tB + 1 + (tB >> 8) >> 8;
1295
+ const tA = 255 * sa + da * invA;
1296
+ const a = tA + 1 + (tA >> 8) >> 8;
1211
1297
  return (a << 24 | b << 16 | g << 8 | r) >>> 0;
1212
1298
  };
1213
1299
  var softLightPerfect = (src, dst) => {
@@ -1215,15 +1301,26 @@ var softLightPerfect = (src, dst) => {
1215
1301
  if (sa === 0) return dst;
1216
1302
  const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
1217
1303
  const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
1218
- const br = ((255 - dr) * (sr * dr / 255 | 0) + dr * (255 - ((255 - sr) * (255 - dr) / 255 | 0))) / 255 | 0;
1219
- const bg = ((255 - dg) * (sg * dg / 255 | 0) + dg * (255 - ((255 - sg) * (255 - dg) / 255 | 0))) / 255 | 0;
1220
- const bb = ((255 - db) * (sb * db / 255 | 0) + db * (255 - ((255 - sb) * (255 - db) / 255 | 0))) / 255 | 0;
1304
+ const mR = sr * dr;
1305
+ const scR = (255 - sr) * (255 - dr);
1306
+ 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;
1307
+ const mG = sg * dg;
1308
+ const scG = (255 - sg) * (255 - dg);
1309
+ 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;
1310
+ const mB = sb * db;
1311
+ const scB = (255 - sb) * (255 - db);
1312
+ 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;
1221
1313
  if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
1222
1314
  const invA = 255 - sa;
1223
- const r = (br * sa + dr * invA) / 255 | 0;
1224
- const g = (bg * sa + dg * invA) / 255 | 0;
1225
- const b = (bb * sa + db * invA) / 255 | 0;
1226
- const a = (255 * sa + (dst >>> 24 & 255) * invA) / 255 | 0;
1315
+ const da = dst >>> 24 & 255;
1316
+ const tR = br * sa + dr * invA;
1317
+ const r = tR + 1 + (tR >> 8) >> 8;
1318
+ const tG = bg * sa + dg * invA;
1319
+ const g = tG + 1 + (tG >> 8) >> 8;
1320
+ const tB = bb * sa + db * invA;
1321
+ const b = tB + 1 + (tB >> 8) >> 8;
1322
+ const tA = 255 * sa + da * invA;
1323
+ const a = tA + 1 + (tA >> 8) >> 8;
1227
1324
  return (a << 24 | b << 16 | g << 8 | r) >>> 0;
1228
1325
  };
1229
1326
  var hardLightPerfect = (src, dst) => {
@@ -1236,10 +1333,15 @@ var hardLightPerfect = (src, dst) => {
1236
1333
  const bb = sb < 128 ? 2 * sb * db / 255 | 0 : 255 - (2 * (255 - sb) * (255 - db) / 255 | 0);
1237
1334
  if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
1238
1335
  const invA = 255 - sa;
1239
- const r = (br * sa + dr * invA) / 255 | 0;
1240
- const g = (bg * sa + dg * invA) / 255 | 0;
1241
- const b = (bb * sa + db * invA) / 255 | 0;
1242
- const a = (255 * sa + (dst >>> 24 & 255) * invA) / 255 | 0;
1336
+ const da = dst >>> 24 & 255;
1337
+ const tR = br * sa + dr * invA;
1338
+ const r = tR + 1 + (tR >> 8) >> 8;
1339
+ const tG = bg * sa + dg * invA;
1340
+ const g = tG + 1 + (tG >> 8) >> 8;
1341
+ const tB = bb * sa + db * invA;
1342
+ const b = tB + 1 + (tB >> 8) >> 8;
1343
+ const tA = 255 * sa + da * invA;
1344
+ const a = tA + 1 + (tA >> 8) >> 8;
1243
1345
  return (a << 24 | b << 16 | g << 8 | r) >>> 0;
1244
1346
  };
1245
1347
  var vividLightPerfect = (src, dst) => {
@@ -1252,10 +1354,15 @@ var vividLightPerfect = (src, dst) => {
1252
1354
  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);
1253
1355
  if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
1254
1356
  const invA = 255 - sa;
1255
- const r = (br * sa + dr * invA) / 255 | 0;
1256
- const g = (bg * sa + dg * invA) / 255 | 0;
1257
- const b = (bb * sa + db * invA) / 255 | 0;
1258
- const a = (255 * sa + (dst >>> 24 & 255) * invA) / 255 | 0;
1357
+ const da = dst >>> 24 & 255;
1358
+ const tR = br * sa + dr * invA;
1359
+ const r = tR + 1 + (tR >> 8) >> 8;
1360
+ const tG = bg * sa + dg * invA;
1361
+ const g = tG + 1 + (tG >> 8) >> 8;
1362
+ const tB = bb * sa + db * invA;
1363
+ const b = tB + 1 + (tB >> 8) >> 8;
1364
+ const tA = 255 * sa + da * invA;
1365
+ const a = tA + 1 + (tA >> 8) >> 8;
1259
1366
  return (a << 24 | b << 16 | g << 8 | r) >>> 0;
1260
1367
  };
1261
1368
  var linearLightPerfect = (src, dst) => {
@@ -1271,10 +1378,15 @@ var linearLightPerfect = (src, dst) => {
1271
1378
  const bb = bbU < 0 ? 0 : bbU > 255 ? 255 : bbU;
1272
1379
  if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
1273
1380
  const invA = 255 - sa;
1274
- const r = (br * sa + dr * invA) / 255 | 0;
1275
- const g = (bg * sa + dg * invA) / 255 | 0;
1276
- const b = (bb * sa + db * invA) / 255 | 0;
1277
- const a = (255 * sa + (dst >>> 24 & 255) * invA) / 255 | 0;
1381
+ const da = dst >>> 24 & 255;
1382
+ const tR = br * sa + dr * invA;
1383
+ const r = tR + 1 + (tR >> 8) >> 8;
1384
+ const tG = bg * sa + dg * invA;
1385
+ const g = tG + 1 + (tG >> 8) >> 8;
1386
+ const tB = bb * sa + db * invA;
1387
+ const b = tB + 1 + (tB >> 8) >> 8;
1388
+ const tA = 255 * sa + da * invA;
1389
+ const a = tA + 1 + (tA >> 8) >> 8;
1278
1390
  return (a << 24 | b << 16 | g << 8 | r) >>> 0;
1279
1391
  };
1280
1392
  var pinLightPerfect = (src, dst) => {
@@ -1292,10 +1404,14 @@ var pinLightPerfect = (src, dst) => {
1292
1404
  if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
1293
1405
  const invA = 255 - sa;
1294
1406
  const da = dst >>> 24 & 255;
1295
- const r = (br * sa + dr * invA + 128) / 255 | 0;
1296
- const g = (bg * sa + dg * invA + 128) / 255 | 0;
1297
- const b = (bb * sa + db * invA + 128) / 255 | 0;
1298
- const a = (255 * sa + da * invA + 128) / 255 | 0;
1407
+ const tR = br * sa + dr * invA;
1408
+ const r = tR + 1 + (tR >> 8) >> 8;
1409
+ const tG = bg * sa + dg * invA;
1410
+ const g = tG + 1 + (tG >> 8) >> 8;
1411
+ const tB = bb * sa + db * invA;
1412
+ const b = tB + 1 + (tB >> 8) >> 8;
1413
+ const tA = 255 * sa + da * invA;
1414
+ const a = tA + 1 + (tA >> 8) >> 8;
1299
1415
  return (a << 24 | b << 16 | g << 8 | r) >>> 0;
1300
1416
  };
1301
1417
  var hardMixPerfect = (src, dst) => {
@@ -1308,33 +1424,36 @@ var hardMixPerfect = (src, dst) => {
1308
1424
  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;
1309
1425
  if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
1310
1426
  const invA = 255 - sa;
1311
- const r = (br * sa + dr * invA) / 255 | 0;
1312
- const g = (bg * sa + dg * invA) / 255 | 0;
1313
- const b = (bb * sa + db * invA) / 255 | 0;
1314
- const a = (255 * sa + (dst >>> 24 & 255) * invA) / 255 | 0;
1427
+ const da = dst >>> 24 & 255;
1428
+ const tR = br * sa + dr * invA;
1429
+ const r = tR + 1 + (tR >> 8) >> 8;
1430
+ const tG = bg * sa + dg * invA;
1431
+ const g = tG + 1 + (tG >> 8) >> 8;
1432
+ const tB = bb * sa + db * invA;
1433
+ const b = tB + 1 + (tB >> 8) >> 8;
1434
+ const tA = 255 * sa + da * invA;
1435
+ const a = tA + 1 + (tA >> 8) >> 8;
1315
1436
  return (a << 24 | b << 16 | g << 8 | r) >>> 0;
1316
1437
  };
1317
1438
  var differencePerfect = (src, dst) => {
1318
1439
  const sa = src >>> 24 & 255;
1319
1440
  if (sa === 0) return dst;
1320
- const dr = dst & 255;
1321
- const dg = dst >>> 8 & 255;
1322
- const db = dst >>> 16 & 255;
1323
- const sr = src & 255;
1324
- const sg = src >>> 8 & 255;
1325
- const sb = src >>> 16 & 255;
1326
- const br = Math.abs(dr - sr);
1327
- const bg = Math.abs(dg - sg);
1328
- const bb = Math.abs(db - sb);
1329
- if (sa === 255) {
1330
- return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
1331
- }
1441
+ const dr = dst & 255, dg = dst >>> 8 & 255, db = dst >>> 16 & 255;
1442
+ const sr = src & 255, sg = src >>> 8 & 255, sb = src >>> 16 & 255;
1443
+ const br = dr > sr ? dr - sr : sr - dr;
1444
+ const bg = dg > sg ? dg - sg : sg - dg;
1445
+ const bb = db > sb ? db - sb : sb - db;
1446
+ if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
1332
1447
  const invA = 255 - sa;
1333
1448
  const da = dst >>> 24 & 255;
1334
- const r = (br * sa + dr * invA + 128) / 255 | 0;
1335
- const g = (bg * sa + dg * invA + 128) / 255 | 0;
1336
- const b = (bb * sa + db * invA + 128) / 255 | 0;
1337
- const a = (255 * sa + da * invA + 128) / 255 | 0;
1449
+ const tR = br * sa + dr * invA;
1450
+ const r = tR + 1 + (tR >> 8) >> 8;
1451
+ const tG = bg * sa + dg * invA;
1452
+ const g = tG + 1 + (tG >> 8) >> 8;
1453
+ const tB = bb * sa + db * invA;
1454
+ const b = tB + 1 + (tB >> 8) >> 8;
1455
+ const tA = 255 * sa + da * invA;
1456
+ const a = tA + 1 + (tA >> 8) >> 8;
1338
1457
  return (a << 24 | b << 16 | g << 8 | r) >>> 0;
1339
1458
  };
1340
1459
  var exclusionPerfect = (src, dst) => {
@@ -1346,18 +1465,23 @@ var exclusionPerfect = (src, dst) => {
1346
1465
  const sr = src & 255;
1347
1466
  const sg = src >>> 8 & 255;
1348
1467
  const sb = src >>> 16 & 255;
1349
- const br = dr + sr - (dr * sr >> 7);
1350
- const bg = dg + sg - (dg * sg >> 7);
1351
- const bb = db + sb - (db * sb >> 7);
1352
- if (sa === 255) {
1353
- return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
1354
- }
1468
+ const r2 = dr * sr;
1469
+ const br = dr + sr - (r2 + r2 + 1 + (r2 + r2 >> 8) >> 8);
1470
+ const g2 = dg * sg;
1471
+ const bg = dg + sg - (g2 + g2 + 1 + (g2 + g2 >> 8) >> 8);
1472
+ const b2 = db * sb;
1473
+ const bb = db + sb - (b2 + b2 + 1 + (b2 + b2 >> 8) >> 8);
1474
+ if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
1355
1475
  const invA = 255 - sa;
1356
1476
  const da = dst >>> 24 & 255;
1357
- const r = (br * sa + dr * invA) / 255 | 0;
1358
- const g = (bg * sa + dg * invA) / 255 | 0;
1359
- const b = (bb * sa + db * invA) / 255 | 0;
1360
- const a = (255 * sa + da * invA) / 255 | 0;
1477
+ const tR = br * sa + dr * invA;
1478
+ const r = tR + 1 + (tR >> 8) >> 8;
1479
+ const tG = bg * sa + dg * invA;
1480
+ const g = tG + 1 + (tG >> 8) >> 8;
1481
+ const tB = bb * sa + db * invA;
1482
+ const b = tB + 1 + (tB >> 8) >> 8;
1483
+ const tA = 255 * sa + da * invA;
1484
+ const a = tA + 1 + (tA >> 8) >> 8;
1361
1485
  return (a << 24 | b << 16 | g << 8 | r) >>> 0;
1362
1486
  };
1363
1487
  var subtractPerfect = (src, dst) => {
@@ -1373,10 +1497,15 @@ var subtractPerfect = (src, dst) => {
1373
1497
  const bb = bbU < 0 ? 0 : bbU;
1374
1498
  if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
1375
1499
  const invA = 255 - sa;
1376
- const r = (br * sa + dr * invA) / 255 | 0;
1377
- const g = (bg * sa + dg * invA) / 255 | 0;
1378
- const b = (bb * sa + db * invA) / 255 | 0;
1379
- const a = (255 * sa + (dst >>> 24 & 255) * invA) / 255 | 0;
1500
+ const da = dst >>> 24 & 255;
1501
+ const tR = br * sa + dr * invA;
1502
+ const r = tR + 1 + (tR >> 8) >> 8;
1503
+ const tG = bg * sa + dg * invA;
1504
+ const g = tG + 1 + (tG >> 8) >> 8;
1505
+ const tB = bb * sa + db * invA;
1506
+ const b = tB + 1 + (tB >> 8) >> 8;
1507
+ const tA = 255 * sa + da * invA;
1508
+ const a = tA + 1 + (tA >> 8) >> 8;
1380
1509
  return (a << 24 | b << 16 | g << 8 | r) >>> 0;
1381
1510
  };
1382
1511
  var dividePerfect = (src, dst) => {
@@ -1389,82 +1518,45 @@ var dividePerfect = (src, dst) => {
1389
1518
  const bb = sb === 0 ? 255 : Math.min(255, db * 255 / sb | 0);
1390
1519
  if (sa === 255) return (4278190080 | bb << 16 | bg << 8 | br) >>> 0;
1391
1520
  const invA = 255 - sa;
1392
- const r = (br * sa + dr * invA) / 255 | 0;
1393
- const g = (bg * sa + dg * invA) / 255 | 0;
1394
- const b = (bb * sa + db * invA) / 255 | 0;
1395
- const a = (255 * sa + (dst >>> 24 & 255) * invA) / 255 | 0;
1521
+ const da = dst >>> 24 & 255;
1522
+ const tR = br * sa + dr * invA;
1523
+ const r = tR + 1 + (tR >> 8) >> 8;
1524
+ const tG = bg * sa + dg * invA;
1525
+ const g = tG + 1 + (tG >> 8) >> 8;
1526
+ const tB = bb * sa + db * invA;
1527
+ const b = tB + 1 + (tB >> 8) >> 8;
1528
+ const tA = 255 * sa + da * invA;
1529
+ const a = tA + 1 + (tA >> 8) >> 8;
1396
1530
  return (a << 24 | b << 16 | g << 8 | r) >>> 0;
1397
1531
  };
1398
- var PERFECT_BLENDER_REGISTRY = [
1399
- [0 /* overwrite */, overwritePerfect],
1400
- [1 /* sourceOver */, sourceOverPerfect],
1401
- [2 /* darken */, darkenPerfect],
1402
- [3 /* multiply */, multiplyPerfect],
1403
- [4 /* colorBurn */, colorBurnPerfect],
1404
- [5 /* linearBurn */, linearBurnPerfect],
1405
- [6 /* darkerColor */, darkerPerfect],
1406
- [7 /* lighten */, lightenPerfect],
1407
- [8 /* screen */, screenPerfect],
1408
- [9 /* colorDodge */, colorDodgePerfect],
1409
- [10 /* linearDodge */, linearDodgePerfect],
1410
- [11 /* lighterColor */, lighterPerfect],
1411
- [12 /* overlay */, overlayPerfect],
1412
- [13 /* softLight */, softLightPerfect],
1413
- [14 /* hardLight */, hardLightPerfect],
1414
- [15 /* vividLight */, vividLightPerfect],
1415
- [16 /* linearLight */, linearLightPerfect],
1416
- [17 /* pinLight */, pinLightPerfect],
1417
- [18 /* hardMix */, hardMixPerfect],
1418
- [19 /* difference */, differencePerfect],
1419
- [20 /* exclusion */, exclusionPerfect],
1420
- [21 /* subtract */, subtractPerfect],
1421
- [22 /* divide */, dividePerfect]
1422
- ];
1423
- var PERFECT_BLEND_MODES = [];
1424
- for (const [index, blend] of PERFECT_BLENDER_REGISTRY) {
1425
- PERFECT_BLEND_MODES[index] = blend;
1426
- }
1427
- var PERFECT_BLEND_TO_INDEX = new Map(
1428
- PERFECT_BLENDER_REGISTRY.map((entry, index) => {
1429
- return [
1430
- entry[1],
1431
- index
1432
- ];
1433
- })
1434
- );
1435
- var INDEX_TO_PERFECT_BLEND = new Map(
1436
- PERFECT_BLENDER_REGISTRY.map((entry, index) => {
1437
- return [
1438
- index,
1439
- entry[1]
1440
- ];
1441
- })
1442
- );
1443
- var PERFECT_BLEND_MODE_BY_NAME = {
1444
- overwrite: overwritePerfect,
1445
- sourceOver: sourceOverPerfect,
1446
- darken: darkenPerfect,
1447
- multiply: multiplyPerfect,
1448
- colorBurn: colorBurnPerfect,
1449
- linearBurn: linearBurnPerfect,
1450
- darkerColor: darkerPerfect,
1451
- lighten: lightenPerfect,
1452
- screen: screenPerfect,
1453
- colorDodge: colorDodgePerfect,
1454
- linearDodge: linearDodgePerfect,
1455
- lighterColor: lighterPerfect,
1456
- overlay: overlayPerfect,
1457
- softLight: softLightPerfect,
1458
- hardLight: hardLightPerfect,
1459
- vividLight: vividLightPerfect,
1460
- linearLight: linearLightPerfect,
1461
- pinLight: pinLightPerfect,
1462
- hardMix: hardMixPerfect,
1463
- difference: differencePerfect,
1464
- exclusion: exclusionPerfect,
1465
- subtract: subtractPerfect,
1466
- divide: dividePerfect
1532
+ var BASE_PERFECT_BLEND_MODE_FUNCTIONS = {
1533
+ [BaseBlendMode.overwrite]: overwritePerfect,
1534
+ [BaseBlendMode.sourceOver]: sourceOverPerfect,
1535
+ [BaseBlendMode.darken]: darkenPerfect,
1536
+ [BaseBlendMode.multiply]: multiplyPerfect,
1537
+ [BaseBlendMode.colorBurn]: colorBurnPerfect,
1538
+ [BaseBlendMode.linearBurn]: linearBurnPerfect,
1539
+ [BaseBlendMode.darkerColor]: darkerPerfect,
1540
+ [BaseBlendMode.lighten]: lightenPerfect,
1541
+ [BaseBlendMode.screen]: screenPerfect,
1542
+ [BaseBlendMode.colorDodge]: colorDodgePerfect,
1543
+ [BaseBlendMode.linearDodge]: linearDodgePerfect,
1544
+ [BaseBlendMode.lighterColor]: lighterPerfect,
1545
+ [BaseBlendMode.overlay]: overlayPerfect,
1546
+ [BaseBlendMode.softLight]: softLightPerfect,
1547
+ [BaseBlendMode.hardLight]: hardLightPerfect,
1548
+ [BaseBlendMode.vividLight]: vividLightPerfect,
1549
+ [BaseBlendMode.linearLight]: linearLightPerfect,
1550
+ [BaseBlendMode.pinLight]: pinLightPerfect,
1551
+ [BaseBlendMode.hardMix]: hardMixPerfect,
1552
+ [BaseBlendMode.difference]: differencePerfect,
1553
+ [BaseBlendMode.exclusion]: exclusionPerfect,
1554
+ [BaseBlendMode.subtract]: subtractPerfect,
1555
+ [BaseBlendMode.divide]: dividePerfect
1467
1556
  };
1557
+ function makePerfectBlendModeRegistry() {
1558
+ return makeBlendModeRegistry(BaseBlendMode, BASE_PERFECT_BLEND_MODE_FUNCTIONS);
1559
+ }
1468
1560
 
1469
1561
  // src/Canvas/_constants.ts
1470
1562
  var OFFSCREEN_CANVAS_CTX_FAILED = "Failed to create OffscreenCanvas context";
@@ -1579,763 +1671,776 @@ async function writeImageDataToClipboard(imageData) {
1579
1671
  return writeImgBlobToClipboard(blob);
1580
1672
  }
1581
1673
 
1582
- // src/ImageData/ReusableImageData.ts
1583
- function makeReusableImageData() {
1584
- let imageData = null;
1585
- let buffer = null;
1586
- return function getReusableImageData(width, height) {
1587
- const hasInstance = !!imageData;
1588
- const widthMatches = hasInstance && imageData.width === width;
1589
- const heightMatches = hasInstance && imageData.height === height;
1590
- if (!widthMatches || !heightMatches) {
1591
- const buffer2 = new Uint8ClampedArray(width * height * 4);
1592
- imageData = new ImageData(buffer2, width, height);
1674
+ // src/History/PixelPatchTiles.ts
1675
+ var PixelTile = class {
1676
+ constructor(id, tx, ty, tileArea) {
1677
+ this.id = id;
1678
+ this.tx = tx;
1679
+ this.ty = ty;
1680
+ this.data32 = new Uint32Array(tileArea);
1681
+ }
1682
+ data32;
1683
+ };
1684
+ function applyPatchTiles(target, tiles, tileSize = 256) {
1685
+ for (let i = 0; i < tiles.length; i++) {
1686
+ const tile = tiles[i];
1687
+ if (!tile) continue;
1688
+ const dst = target.data32;
1689
+ const src = tile.data32;
1690
+ const dstWidth = target.width;
1691
+ const dstHeight = target.height;
1692
+ const startX = tile.tx * tileSize;
1693
+ const startY = tile.ty * tileSize;
1694
+ const copyWidth = Math.max(0, Math.min(tileSize, dstWidth - startX));
1695
+ if (copyWidth <= 0) return;
1696
+ for (let ly = 0; ly < tileSize; ly++) {
1697
+ const globalY = startY + ly;
1698
+ if (globalY >= dstHeight) break;
1699
+ const dstIndex = globalY * dstWidth + startX;
1700
+ const srcIndex = ly * tileSize;
1701
+ const rowData = src.subarray(srcIndex, srcIndex + copyWidth);
1702
+ dst.set(rowData, dstIndex);
1593
1703
  }
1594
- return imageData;
1595
- };
1596
- }
1597
-
1598
- // src/ImageData/copyImageData.ts
1599
- function copyImageData({ data, width, height }) {
1600
- return new ImageData(data.slice(), width, height);
1601
- }
1602
- function copyImageDataLike({ data, width, height }) {
1603
- return {
1604
- data: data.slice(),
1605
- width,
1606
- height
1607
- };
1608
- }
1609
-
1610
- // src/PixelData/pixelDataToAlphaMask.ts
1611
- function pixelDataToAlphaMask(pixelData) {
1612
- const {
1613
- data32,
1614
- width,
1615
- height
1616
- } = pixelData;
1617
- const len = data32.length;
1618
- const mask = new Uint8Array(width * height);
1619
- for (let i = 0; i < len; i++) {
1620
- const val = data32[i];
1621
- mask[i] = val >>> 24 & 255;
1622
1704
  }
1623
- return mask;
1624
1705
  }
1625
1706
 
1626
- // src/ImageData/imageDataToAlphaMask.ts
1627
- function imageDataToAlphaMask(imageData) {
1628
- const {
1629
- width,
1630
- height,
1631
- data
1632
- } = imageData;
1633
- const data32 = new Uint32Array(
1634
- data.buffer,
1635
- data.byteOffset,
1636
- data.byteLength >> 2
1637
- );
1638
- const len = data32.length;
1639
- const mask = new Uint8Array(width * height);
1640
- for (let i = 0; i < len; i++) {
1641
- const val = data32[i];
1642
- mask[i] = val >>> 24 & 255;
1707
+ // src/History/PixelAccumulator.ts
1708
+ var PixelAccumulator = class {
1709
+ constructor(target, config) {
1710
+ this.target = target;
1711
+ this.config = config;
1712
+ this.lookup = [];
1713
+ this.beforeTiles = [];
1714
+ this.pool = [];
1715
+ }
1716
+ lookup;
1717
+ beforeTiles;
1718
+ pool;
1719
+ getTile(id, tx, ty) {
1720
+ let tile = this.pool.pop();
1721
+ if (tile) {
1722
+ tile.id = id;
1723
+ tile.tx = tx;
1724
+ tile.ty = ty;
1725
+ return tile;
1726
+ }
1727
+ return new PixelTile(
1728
+ id,
1729
+ tx,
1730
+ ty,
1731
+ this.config.tileArea
1732
+ );
1643
1733
  }
1644
- return mask;
1645
- }
1646
-
1647
- // src/ImageData/imageDataToDataUrl.ts
1648
- var get = makeReusableCanvas();
1649
- function imageDataToDataUrl(imageData) {
1650
- const { canvas, ctx } = get(imageData.width, imageData.height);
1651
- ctx.putImageData(imageData, 0, 0);
1652
- return canvas.toDataURL();
1653
- }
1654
- imageDataToDataUrl.reset = get.reset;
1655
-
1656
- // src/ImageData/imageDataToUInt32Array.ts
1657
- function imageDataToUInt32Array(imageData) {
1658
- return new Uint32Array(
1659
- imageData.data.buffer,
1660
- imageData.data.byteOffset,
1661
- // Shift right by 2 is a fast bitwise division by 4.
1662
- imageData.data.byteLength >> 2
1663
- );
1664
- }
1665
-
1666
- // src/ImageData/invertImageData.ts
1667
- function invertImageData(imageData) {
1668
- const data = imageData.data;
1669
- let length = data.length;
1670
- for (let i = 0; i < length; i += 4) {
1671
- data[i] = 255 - data[i];
1672
- data[i + 1] = 255 - data[i + 1];
1673
- data[i + 2] = 255 - data[i + 2];
1734
+ recyclePatch(patch) {
1735
+ const before = patch.beforeTiles;
1736
+ for (let i = 0; i < before.length; i++) {
1737
+ let tile = before[i];
1738
+ if (tile) {
1739
+ this.pool.push(tile);
1740
+ }
1741
+ }
1742
+ const after = patch.afterTiles;
1743
+ for (let i = 0; i < after.length; i++) {
1744
+ let tile = after[i];
1745
+ if (tile) {
1746
+ this.pool.push(tile);
1747
+ }
1748
+ }
1749
+ }
1750
+ /**
1751
+ * @param x pixel x coordinate
1752
+ * @param y pixel y coordinate
1753
+ */
1754
+ storeTileBeforeState(x, y) {
1755
+ let target = this.target;
1756
+ let shift = this.config.tileShift;
1757
+ let columns = target.width + this.config.tileMask >> shift;
1758
+ let tx = x >> shift;
1759
+ let ty = y >> shift;
1760
+ let id = ty * columns + tx;
1761
+ let tile = this.lookup[id];
1762
+ if (!tile) {
1763
+ tile = this.getTile(
1764
+ id,
1765
+ tx,
1766
+ ty
1767
+ );
1768
+ this.extractState(tile);
1769
+ this.lookup[id] = tile;
1770
+ this.beforeTiles.push(tile);
1771
+ }
1772
+ }
1773
+ /**
1774
+ *
1775
+ * @param x pixel x coordinate
1776
+ * @param y pixel y coordinate
1777
+ * @param w pixel width
1778
+ * @param h pixel height
1779
+ */
1780
+ storeRegionBeforeState(x, y, w, h) {
1781
+ let target = this.target;
1782
+ let shift = this.config.tileShift;
1783
+ let columns = target.width + this.config.tileMask >> shift;
1784
+ let startX = x >> shift;
1785
+ let startY = y >> shift;
1786
+ let endX = x + w - 1 >> shift;
1787
+ let endY = y + h - 1 >> shift;
1788
+ for (let ty = startY; ty <= endY; ty++) {
1789
+ for (let tx = startX; tx <= endX; tx++) {
1790
+ let id = ty * columns + tx;
1791
+ let tile = this.lookup[id];
1792
+ if (!tile) {
1793
+ tile = this.getTile(
1794
+ id,
1795
+ tx,
1796
+ ty
1797
+ );
1798
+ this.extractState(tile);
1799
+ this.lookup[id] = tile;
1800
+ this.beforeTiles.push(tile);
1801
+ }
1802
+ }
1803
+ }
1804
+ }
1805
+ extractState(tile) {
1806
+ let target = this.target;
1807
+ let TILE_SIZE = this.config.tileSize;
1808
+ let dst = tile.data32;
1809
+ let src = target.data32;
1810
+ let startX = tile.tx * TILE_SIZE;
1811
+ let startY = tile.ty * TILE_SIZE;
1812
+ let targetWidth = target.width;
1813
+ let targetHeight = target.height;
1814
+ let copyWidth = Math.max(0, Math.min(TILE_SIZE, targetWidth - startX));
1815
+ for (let ly = 0; ly < TILE_SIZE; ly++) {
1816
+ let globalY = startY + ly;
1817
+ let dstIndex = ly * TILE_SIZE;
1818
+ if (globalY < 0 || globalY >= targetHeight || copyWidth === 0) {
1819
+ dst.fill(0, dstIndex, dstIndex + TILE_SIZE);
1820
+ continue;
1821
+ }
1822
+ let srcIndex = globalY * targetWidth + startX;
1823
+ let rowData = src.subarray(srcIndex, srcIndex + copyWidth);
1824
+ dst.set(rowData, dstIndex);
1825
+ if (copyWidth < TILE_SIZE) {
1826
+ dst.fill(0, dstIndex + copyWidth, dstIndex + TILE_SIZE);
1827
+ }
1828
+ }
1829
+ }
1830
+ extractAfterTiles() {
1831
+ let afterTiles = [];
1832
+ let length = this.beforeTiles.length;
1833
+ for (let i = 0; i < length; i++) {
1834
+ let beforeTile = this.beforeTiles[i];
1835
+ if (beforeTile) {
1836
+ let afterTile = this.getTile(
1837
+ beforeTile.id,
1838
+ beforeTile.tx,
1839
+ beforeTile.ty
1840
+ );
1841
+ this.extractState(afterTile);
1842
+ afterTiles.push(afterTile);
1843
+ }
1844
+ }
1845
+ return afterTiles;
1846
+ }
1847
+ reset() {
1848
+ this.lookup = [];
1849
+ this.beforeTiles = [];
1674
1850
  }
1675
- return imageData;
1676
- }
1677
-
1678
- // src/Internal/resample32.ts
1679
- var resample32Scratch = {
1680
- data: null,
1681
- width: 0,
1682
- height: 0
1683
1851
  };
1684
- function resample32(srcData32, srcW, srcH, factor) {
1685
- const dstW = Math.max(1, srcW * factor | 0);
1686
- const dstH = Math.max(1, srcH * factor | 0);
1687
- const dstData = new Int32Array(dstW * dstH);
1688
- const scaleX = srcW / dstW;
1689
- const scaleY = srcH / dstH;
1690
- for (let y = 0; y < dstH; y++) {
1691
- const srcY = Math.min(srcH - 1, y * scaleY | 0);
1692
- const srcRowOffset = srcY * srcW;
1693
- const dstRowOffset = y * dstW;
1694
- for (let x = 0; x < dstW; x++) {
1695
- const srcX = Math.min(srcW - 1, x * scaleX | 0);
1696
- dstData[dstRowOffset + x] = srcData32[srcRowOffset + srcX];
1852
+
1853
+ // src/History/HistoryManager.ts
1854
+ var HistoryManager = class {
1855
+ constructor(maxSteps = 50) {
1856
+ this.maxSteps = maxSteps;
1857
+ this.undoStack = [];
1858
+ this.redoStack = [];
1859
+ this.listeners = /* @__PURE__ */ new Set();
1860
+ }
1861
+ undoStack;
1862
+ redoStack;
1863
+ listeners;
1864
+ get canUndo() {
1865
+ return this.undoStack.length > 0;
1866
+ }
1867
+ get canRedo() {
1868
+ return this.redoStack.length > 0;
1869
+ }
1870
+ subscribe(fn) {
1871
+ this.listeners.add(fn);
1872
+ return () => this.listeners.delete(fn);
1873
+ }
1874
+ notify() {
1875
+ this.listeners.forEach((fn) => fn());
1876
+ }
1877
+ commit(action) {
1878
+ this.undoStack.push(action);
1879
+ this.clearRedoStack();
1880
+ if (this.undoStack.length > this.maxSteps) {
1881
+ this.undoStack.shift()?.dispose?.();
1882
+ }
1883
+ this.notify();
1884
+ }
1885
+ undo() {
1886
+ let action = this.undoStack.pop();
1887
+ if (!action) return;
1888
+ this.redoStack.push(action);
1889
+ action.undo();
1890
+ this.notify();
1891
+ }
1892
+ redo() {
1893
+ let action = this.redoStack.pop();
1894
+ if (!action) return;
1895
+ this.undoStack.push(action);
1896
+ action.redo();
1897
+ this.notify();
1898
+ }
1899
+ clearRedoStack() {
1900
+ let length = this.redoStack.length;
1901
+ for (let i = 0; i < length; i++) {
1902
+ let action = this.redoStack[i];
1903
+ if (action) {
1904
+ action.dispose?.();
1905
+ }
1697
1906
  }
1907
+ this.redoStack.length = 0;
1698
1908
  }
1699
- resample32Scratch.data = dstData;
1700
- resample32Scratch.width = dstW;
1701
- resample32Scratch.height = dstH;
1702
- return resample32Scratch;
1703
- }
1909
+ };
1704
1910
 
1705
- // src/ImageData/resampleImageData.ts
1706
- function resampleImageData(source, factor) {
1707
- const src32 = new Uint32Array(source.data.buffer);
1708
- const { data, width, height } = resample32(src32, source.width, source.height, factor);
1709
- const uint8ClampedArray = new Uint8ClampedArray(data.buffer);
1710
- return new ImageData(uint8ClampedArray, width, height);
1711
- }
1911
+ // src/History/PixelEngineConfig.ts
1912
+ var PixelEngineConfig = class {
1913
+ tileSize;
1914
+ tileShift;
1915
+ tileMask;
1916
+ tileArea;
1917
+ constructor(tileSize = 256) {
1918
+ if ((tileSize & tileSize - 1) !== 0) {
1919
+ throw new Error("tileSize must be a power of 2");
1920
+ }
1921
+ this.tileSize = tileSize;
1922
+ this.tileShift = Math.log2(tileSize);
1923
+ this.tileMask = tileSize - 1;
1924
+ this.tileArea = tileSize * tileSize;
1925
+ }
1926
+ };
1712
1927
 
1713
- // src/ImageData/resizeImageData.ts
1714
- function resizeImageData(current, newWidth, newHeight, offsetX = 0, offsetY = 0) {
1715
- const result = new ImageData(newWidth, newHeight);
1928
+ // src/PixelData/applyMaskToPixelData.ts
1929
+ function applyMaskToPixelData(dst, mask, opts = {}) {
1716
1930
  const {
1717
- width: oldW,
1718
- height: oldH,
1719
- data: oldData
1720
- } = current;
1721
- const newData = result.data;
1722
- const x0 = Math.max(0, offsetX);
1723
- const y0 = Math.max(0, offsetY);
1724
- const x1 = Math.min(newWidth, offsetX + oldW);
1725
- const y1 = Math.min(newHeight, offsetY + oldH);
1726
- if (x1 <= x0 || y1 <= y0) {
1727
- return result;
1728
- }
1729
- const rowCount = y1 - y0;
1730
- const rowLen = (x1 - x0) * 4;
1731
- for (let row = 0; row < rowCount; row++) {
1732
- const dstY = y0 + row;
1733
- const srcY = dstY - offsetY;
1734
- const srcX = x0 - offsetX;
1735
- const dstStart = (dstY * newWidth + x0) * 4;
1736
- const srcStart = (srcY * oldW + srcX) * 4;
1737
- newData.set(
1738
- oldData.subarray(srcStart, srcStart + rowLen),
1739
- dstStart
1740
- );
1931
+ x: targetX = 0,
1932
+ y: targetY = 0,
1933
+ w: width = dst.width,
1934
+ h: height = dst.height,
1935
+ alpha: globalAlpha = 255,
1936
+ maskType = 0 /* ALPHA */,
1937
+ mw,
1938
+ mx = 0,
1939
+ my = 0,
1940
+ invertMask = false
1941
+ } = opts;
1942
+ let x = targetX;
1943
+ let y = targetY;
1944
+ let w = width;
1945
+ let h = height;
1946
+ if (x < 0) {
1947
+ w += x;
1948
+ x = 0;
1741
1949
  }
1742
- return result;
1743
- }
1744
-
1745
- // src/ImageData/serialization.ts
1746
- function base64EncodeArrayBuffer(buffer) {
1747
- const uint8 = new Uint8Array(buffer);
1748
- const decoder = new TextDecoder("latin1");
1749
- const binary = decoder.decode(uint8);
1750
- return btoa(binary);
1751
- }
1752
- function base64DecodeArrayBuffer(encoded) {
1753
- const binary = atob(encoded);
1754
- const bytes = new Uint8ClampedArray(binary.length);
1755
- for (let i = 0; i < binary.length; i++) {
1756
- bytes[i] = binary.charCodeAt(i);
1950
+ if (y < 0) {
1951
+ h += y;
1952
+ y = 0;
1757
1953
  }
1758
- return bytes;
1759
- }
1760
- function serializeImageData(imageData) {
1761
- return {
1762
- width: imageData.width,
1763
- height: imageData.height,
1764
- data: base64EncodeArrayBuffer(imageData.data.buffer)
1765
- };
1766
- }
1767
- function serializeNullableImageData(imageData) {
1768
- if (!imageData) return null;
1769
- return serializeImageData(imageData);
1770
- }
1771
- function deserializeRawImageData(serialized) {
1772
- return {
1773
- width: serialized.width,
1774
- height: serialized.height,
1775
- data: base64DecodeArrayBuffer(serialized.data)
1776
- };
1777
- }
1778
- function deserializeImageData(serialized) {
1779
- const data = base64DecodeArrayBuffer(serialized.data);
1780
- return new ImageData(data, serialized.width, serialized.height);
1781
- }
1782
- function deserializeNullableImageData(serialized) {
1783
- if (!serialized) return null;
1784
- return deserializeImageData(serialized);
1785
- }
1786
-
1787
- // src/ImageData/writeImageData.ts
1788
- function writeImageData(target, source, x, y, sx = 0, sy = 0, sw = source.width, sh = source.height, mask = null, maskType = 1 /* BINARY */) {
1789
- const dstW = target.width;
1790
- const dstH = target.height;
1791
- const dstData = target.data;
1792
- const srcW = source.width;
1793
- const srcData = source.data;
1794
- const x0 = Math.max(0, x);
1795
- const y0 = Math.max(0, y);
1796
- const x1 = Math.min(dstW, x + sw);
1797
- const y1 = Math.min(dstH, y + sh);
1798
- if (x1 <= x0 || y1 <= y0) {
1954
+ const actualW = Math.min(w, dst.width - x);
1955
+ const actualH = Math.min(h, dst.height - y);
1956
+ if (actualW <= 0 || actualH <= 0 || globalAlpha === 0) {
1799
1957
  return;
1800
1958
  }
1801
- const useMask = !!mask;
1802
- const rowCount = y1 - y0;
1803
- const rowLenPixels = x1 - x0;
1804
- for (let row = 0; row < rowCount; row++) {
1805
- const dstY = y0 + row;
1806
- const srcY = sy + (dstY - y);
1807
- const srcXBase = sx + (x0 - x);
1808
- const dstStart = (dstY * dstW + x0) * 4;
1809
- const srcStart = (srcY * srcW + srcXBase) * 4;
1810
- if (useMask && mask) {
1811
- for (let ix = 0; ix < rowLenPixels; ix++) {
1812
- const mi = srcY * srcW + (srcXBase + ix);
1813
- const alpha = mask[mi];
1814
- if (alpha === 0) {
1959
+ const dst32 = dst.data32;
1960
+ const dw = dst.width;
1961
+ const mPitch = mw ?? width;
1962
+ const isAlpha = maskType === 0 /* ALPHA */;
1963
+ const dx = x - targetX;
1964
+ const dy = y - targetY;
1965
+ let dIdx = y * dw + x;
1966
+ let mIdx = (my + dy) * mPitch + (mx + dx);
1967
+ const dStride = dw - actualW;
1968
+ const mStride = mPitch - actualW;
1969
+ for (let iy = 0; iy < actualH; iy++) {
1970
+ for (let ix = 0; ix < actualW; ix++) {
1971
+ const mVal = mask[mIdx];
1972
+ let weight = globalAlpha;
1973
+ if (isAlpha) {
1974
+ const effectiveM = invertMask ? 255 - mVal : mVal;
1975
+ if (effectiveM === 0) {
1976
+ dst32[dIdx] = (dst32[dIdx] & 16777215) >>> 0;
1977
+ dIdx++;
1978
+ mIdx++;
1815
1979
  continue;
1816
1980
  }
1817
- const di = dstStart + ix * 4;
1818
- const si = srcStart + ix * 4;
1819
- if (maskType === 1 /* BINARY */ || alpha === 255) {
1820
- dstData[di] = srcData[si];
1821
- dstData[di + 1] = srcData[si + 1];
1822
- dstData[di + 2] = srcData[si + 2];
1823
- dstData[di + 3] = srcData[si + 3];
1981
+ weight = globalAlpha === 255 ? effectiveM : effectiveM * globalAlpha + 128 >> 8;
1982
+ } else {
1983
+ const isHit = invertMask ? mVal === 0 : mVal === 1;
1984
+ if (!isHit) {
1985
+ dst32[dIdx] = (dst32[dIdx] & 16777215) >>> 0;
1986
+ dIdx++;
1987
+ mIdx++;
1988
+ continue;
1989
+ }
1990
+ weight = globalAlpha;
1991
+ }
1992
+ if (weight === 0) {
1993
+ dst32[dIdx] = (dst32[dIdx] & 16777215) >>> 0;
1994
+ } else {
1995
+ const d = dst32[dIdx];
1996
+ const da = d >>> 24;
1997
+ let finalAlpha = da;
1998
+ if (da === 0) {
1999
+ } else if (weight === 255) {
2000
+ } else if (da === 255) {
2001
+ finalAlpha = weight;
1824
2002
  } else {
1825
- const a = alpha / 255;
1826
- const invA = 1 - a;
1827
- dstData[di] = srcData[si] * a + dstData[di] * invA;
1828
- dstData[di + 1] = srcData[si + 1] * a + dstData[di + 1] * invA;
1829
- dstData[di + 2] = srcData[si + 2] * a + dstData[di + 2] * invA;
1830
- dstData[di + 3] = srcData[si + 3] * a + dstData[di + 3] * invA;
2003
+ finalAlpha = da * weight + 128 >> 8;
1831
2004
  }
2005
+ dst32[dIdx] = (d & 16777215 | finalAlpha << 24) >>> 0;
1832
2006
  }
1833
- } else {
1834
- const byteLen = rowLenPixels * 4;
1835
- const sub = srcData.subarray(srcStart, srcStart + byteLen);
1836
- dstData.set(sub, dstStart);
2007
+ dIdx++;
2008
+ mIdx++;
1837
2009
  }
2010
+ dIdx += dStride;
2011
+ mIdx += mStride;
1838
2012
  }
1839
2013
  }
1840
2014
 
1841
- // src/ImageData/writeImageDataPixels.ts
1842
- function writeImageDataPixels(imageData, data, _x, _y, _w, _h) {
1843
- const { x, y, w, h } = typeof _x === "object" ? _x : { x: _x, y: _y, w: _w, h: _h };
1844
- const { width: dstW, height: dstH, data: dst } = imageData;
1845
- const x0 = Math.max(0, x);
1846
- const y0 = Math.max(0, y);
1847
- const x1 = Math.min(dstW, x + w);
1848
- const y1 = Math.min(dstH, y + h);
1849
- if (x1 <= x0 || y1 <= y0) return;
1850
- const rowLen = (x1 - x0) * 4;
1851
- const srcCol = x0 - x;
1852
- const srcYOffset = y0 - y;
1853
- const actualH = y1 - y0;
1854
- for (let row = 0; row < actualH; row++) {
1855
- const dstStart = ((y0 + row) * dstW + x0) * 4;
1856
- const srcRow = srcYOffset + row;
1857
- const o = (srcRow * w + srcCol) * 4;
1858
- dst.set(data.subarray(o, o + rowLen), dstStart);
2015
+ // src/History/PixelWriter.ts
2016
+ var PixelWriter = class {
2017
+ target;
2018
+ historyManager;
2019
+ accumulator;
2020
+ config;
2021
+ mutator;
2022
+ constructor(target, mutatorFactory, {
2023
+ tileSize = 256,
2024
+ maxHistorySteps = 50,
2025
+ historyManager = new HistoryManager(maxHistorySteps)
2026
+ } = {}) {
2027
+ this.target = target;
2028
+ this.config = new PixelEngineConfig(tileSize);
2029
+ this.historyManager = historyManager;
2030
+ this.accumulator = new PixelAccumulator(target, this.config);
2031
+ this.mutator = mutatorFactory(this);
2032
+ }
2033
+ withHistory(cb) {
2034
+ cb(this.mutator);
2035
+ const beforeTiles = this.accumulator.beforeTiles;
2036
+ if (beforeTiles.length === 0) return;
2037
+ const afterTiles = this.accumulator.extractAfterTiles();
2038
+ const patch = {
2039
+ beforeTiles,
2040
+ afterTiles
2041
+ };
2042
+ const target = this.target;
2043
+ const tileSize = this.config.tileSize;
2044
+ const accumulator = this.accumulator;
2045
+ const action = {
2046
+ undo: () => applyPatchTiles(target, patch.beforeTiles, tileSize),
2047
+ redo: () => applyPatchTiles(target, patch.afterTiles, tileSize),
2048
+ dispose: () => accumulator.recyclePatch(patch)
2049
+ };
2050
+ this.historyManager.commit(action);
2051
+ this.accumulator.reset();
1859
2052
  }
2053
+ };
2054
+
2055
+ // src/History/PixelMutator/mutatorApplyMask.ts
2056
+ function mutatorApplyMask(writer) {
2057
+ return {
2058
+ applyMask: (mask, opts = {}) => {
2059
+ let target = writer.target;
2060
+ const {
2061
+ x = 0,
2062
+ y = 0,
2063
+ w = writer.target.width,
2064
+ h = writer.target.height
2065
+ } = opts;
2066
+ writer.accumulator.storeRegionBeforeState(x, y, w, h);
2067
+ applyMaskToPixelData(target, mask, opts);
2068
+ }
2069
+ };
1860
2070
  }
1861
2071
 
1862
- // src/IndexedImage/IndexedImage.ts
1863
- var IndexedImage = class _IndexedImage {
1864
- /** The width of the image in pixels. */
1865
- width;
1866
- /** The height of the image in pixels. */
1867
- height;
1868
- /** Flat array of palette indices. Index = x + (y * width). */
1869
- data;
1870
- /** The palette of unique 32-bit colors (ABGR/RGBA packed) found in the image. */
1871
- palette;
1872
- /** The specific index in the palette reserved for fully transparent pixels. */
1873
- transparentPalletIndex;
1874
- /**
1875
- * @param width - Image width.
1876
- * @param height - Image height.
1877
- * @param data - The indexed pixel data.
1878
- * @param palette - The array of packed colors.
1879
- * @param transparentPalletIndex - The index representing alpha 0.
1880
- */
1881
- constructor(width, height, data, palette, transparentPalletIndex) {
1882
- this.width = width;
1883
- this.height = height;
1884
- this.data = data;
1885
- this.palette = palette;
1886
- this.transparentPalletIndex = transparentPalletIndex;
2072
+ // src/PixelData/blendColorPixelData.ts
2073
+ function blendColorPixelData(dst, color, opts = {}) {
2074
+ const {
2075
+ x: targetX = 0,
2076
+ y: targetY = 0,
2077
+ w: width = dst.width,
2078
+ h: height = dst.height,
2079
+ alpha: globalAlpha = 255,
2080
+ blendFn = sourceOverFast,
2081
+ mask,
2082
+ maskType = 0 /* ALPHA */,
2083
+ mw,
2084
+ mx = 0,
2085
+ my = 0,
2086
+ invertMask = false
2087
+ } = opts;
2088
+ if (globalAlpha === 0) return;
2089
+ const baseSrcAlpha = color >>> 24;
2090
+ const isOverwrite = blendFn.isOverwrite;
2091
+ if (baseSrcAlpha === 0 && !isOverwrite) return;
2092
+ let x = targetX;
2093
+ let y = targetY;
2094
+ let w = width;
2095
+ let h = height;
2096
+ if (x < 0) {
2097
+ w += x;
2098
+ x = 0;
1887
2099
  }
1888
- /**
1889
- * Creates an IndexedImage from standard browser ImageData.
1890
- * @param imageData - The source ImageData to convert.
1891
- * @returns A new IndexedImage instance.
1892
- */
1893
- static fromImageData(imageData) {
1894
- return _IndexedImage.fromRaw(imageData.data, imageData.width, imageData.height);
2100
+ if (y < 0) {
2101
+ h += y;
2102
+ y = 0;
1895
2103
  }
1896
- /**
1897
- * Creates an IndexedImage from a raw byte buffer and dimensions.
1898
- * Any pixel with an alpha channel of 0 is normalized to the transparent palette index.
1899
- * @param data - Raw RGBA byte data.
1900
- * @param width - Image width.
1901
- * @param height - Image height.
1902
- * @returns A new IndexedImage instance.
1903
- */
1904
- static fromRaw(data, width, height) {
1905
- const buffer = data.buffer;
1906
- const rawData = new Uint32Array(buffer);
1907
- const indexedData = new Int32Array(rawData.length);
1908
- const colorMap = /* @__PURE__ */ new Map();
1909
- const transparentColor = 0;
1910
- const transparentPalletIndex = 0;
1911
- colorMap.set(transparentColor, transparentPalletIndex);
1912
- for (let i = 0; i < rawData.length; i++) {
1913
- const pixel = rawData[i];
1914
- const alpha = pixel >>> 24 & 255;
1915
- const isTransparent = alpha === 0;
1916
- const colorKey = isTransparent ? transparentColor : pixel >>> 0;
1917
- let id = colorMap.get(colorKey);
1918
- if (id === void 0) {
1919
- id = colorMap.size;
1920
- colorMap.set(colorKey, id);
2104
+ const actualW = Math.min(w, dst.width - x);
2105
+ const actualH = Math.min(h, dst.height - y);
2106
+ if (actualW <= 0 || actualH <= 0) return;
2107
+ const dst32 = dst.data32;
2108
+ const dw = dst.width;
2109
+ const mPitch = mw ?? width;
2110
+ const isAlphaMask = maskType === 0 /* ALPHA */;
2111
+ const dx = x - targetX;
2112
+ const dy = y - targetY;
2113
+ let dIdx = y * dw + x;
2114
+ let mIdx = (my + dy) * mPitch + (mx + dx);
2115
+ const dStride = dw - actualW;
2116
+ const mStride = mPitch - actualW;
2117
+ for (let iy = 0; iy < actualH; iy++) {
2118
+ for (let ix = 0; ix < actualW; ix++) {
2119
+ let weight = globalAlpha;
2120
+ if (mask) {
2121
+ const mVal = mask[mIdx];
2122
+ if (isAlphaMask) {
2123
+ const effectiveM = invertMask ? 255 - mVal : mVal;
2124
+ if (effectiveM === 0) {
2125
+ dIdx++;
2126
+ mIdx++;
2127
+ continue;
2128
+ }
2129
+ if (globalAlpha === 255) {
2130
+ weight = effectiveM;
2131
+ } else if (effectiveM === 255) {
2132
+ weight = globalAlpha;
2133
+ } else {
2134
+ weight = effectiveM * globalAlpha + 128 >> 8;
2135
+ }
2136
+ } else {
2137
+ const isHit = invertMask ? mVal === 0 : mVal === 1;
2138
+ if (!isHit) {
2139
+ dIdx++;
2140
+ mIdx++;
2141
+ continue;
2142
+ }
2143
+ weight = globalAlpha;
2144
+ }
2145
+ if (weight === 0) {
2146
+ dIdx++;
2147
+ mIdx++;
2148
+ continue;
2149
+ }
1921
2150
  }
1922
- indexedData[i] = id;
2151
+ let currentSrcColor = color;
2152
+ if (weight < 255) {
2153
+ let currentSrcAlpha = baseSrcAlpha;
2154
+ if (baseSrcAlpha === 255) {
2155
+ currentSrcAlpha = weight;
2156
+ } else {
2157
+ currentSrcAlpha = baseSrcAlpha * weight + 128 >> 8;
2158
+ }
2159
+ if (!isOverwrite && currentSrcAlpha === 0) {
2160
+ dIdx++;
2161
+ mIdx++;
2162
+ continue;
2163
+ }
2164
+ currentSrcColor = (color & 16777215 | currentSrcAlpha << 24) >>> 0;
2165
+ }
2166
+ dst32[dIdx] = blendFn(currentSrcColor, dst32[dIdx]);
2167
+ dIdx++;
2168
+ mIdx++;
1923
2169
  }
1924
- const palette = Uint32Array.from(colorMap.keys());
1925
- return new _IndexedImage(
1926
- width,
1927
- height,
1928
- indexedData,
1929
- palette,
1930
- transparentPalletIndex
1931
- );
1932
- }
1933
- /**
1934
- * Retrieves the 32-bit packed color value at the given coordinates.
1935
- * @param x - X coordinate.
1936
- * @param y - Y coordinate.
1937
- * @returns The packed color from the palette.
1938
- */
1939
- getColorAt(x, y) {
1940
- const index = x + y * this.width;
1941
- const paletteIndex = this.data[index];
1942
- return this.palette[paletteIndex];
1943
- }
1944
- };
1945
-
1946
- // src/IndexedImage/getIndexedImageColorCounts.ts
1947
- function getIndexedImageColorCounts(indexedImage) {
1948
- const data = indexedImage.data;
1949
- const palette = indexedImage.palette;
1950
- const frequencies = new Int32Array(palette.length);
1951
- for (let i = 0; i < data.length; i++) {
1952
- const colorIndex = data[i];
1953
- frequencies[colorIndex]++;
2170
+ dIdx += dStride;
2171
+ mIdx += mStride;
1954
2172
  }
1955
- return frequencies;
1956
2173
  }
1957
2174
 
1958
- // src/IndexedImage/indexedImageToAverageColor.ts
1959
- function indexedImageToAverageColor(indexedImage, includeTransparent = false) {
1960
- const { data, palette, transparentPalletIndex } = indexedImage;
1961
- const counts = new Uint32Array(palette.length);
1962
- for (let i = 0; i < data.length; i++) {
1963
- const id = data[i];
1964
- counts[id]++;
1965
- }
1966
- let rSum = 0;
1967
- let gSum = 0;
1968
- let bSum = 0;
1969
- let aSum = 0;
1970
- let totalWeight = 0;
1971
- for (let id = 0; id < counts.length; id++) {
1972
- const weight = counts[id];
1973
- if (weight === 0) {
1974
- continue;
1975
- }
1976
- if (!includeTransparent && id === transparentPalletIndex) {
1977
- continue;
2175
+ // src/History/PixelMutator/mutatorBlendColor.ts
2176
+ function mutatorBlendColor(writer) {
2177
+ return {
2178
+ blendColor(color, opts = {}) {
2179
+ const {
2180
+ x = 0,
2181
+ y = 0,
2182
+ w = writer.target.width,
2183
+ h = writer.target.height
2184
+ } = opts;
2185
+ writer.accumulator.storeRegionBeforeState(x, y, w, h);
2186
+ blendColorPixelData(writer.target, color, opts);
1978
2187
  }
1979
- const color = palette[id] >>> 0;
1980
- const r2 = color & 255;
1981
- const g2 = color >> 8 & 255;
1982
- const b2 = color >> 16 & 255;
1983
- const a2 = color >> 24 & 255;
1984
- rSum += r2 * weight;
1985
- gSum += g2 * weight;
1986
- bSum += b2 * weight;
1987
- aSum += a2 * weight;
1988
- totalWeight += weight;
1989
- }
1990
- if (totalWeight === 0) {
1991
- return packColor(0, 0, 0, 0);
1992
- }
1993
- const r = rSum / totalWeight | 0;
1994
- const g = gSum / totalWeight | 0;
1995
- const b = bSum / totalWeight | 0;
1996
- const a = aSum / totalWeight | 0;
1997
- return packColor(r, g, b, a);
2188
+ };
1998
2189
  }
1999
2190
 
2000
- // src/IndexedImage/resampleIndexedImage.ts
2001
- function resampleIndexedImage(source, factor) {
2002
- const { data, width, height } = resample32(
2003
- source.data,
2004
- source.width,
2005
- source.height,
2006
- factor
2007
- );
2008
- return new IndexedImage(
2009
- width,
2010
- height,
2011
- data,
2012
- source.palette,
2013
- source.transparentPalletIndex
2014
- );
2191
+ // src/History/PixelMutator/mutatorBlendPixel.ts
2192
+ function mutatorBlendPixel(writer) {
2193
+ return {
2194
+ blendPixel(x, y, color, alpha = 255, blendFn = overwriteFast) {
2195
+ let target = writer.target;
2196
+ let width = target.width;
2197
+ let height = target.height;
2198
+ if (x < 0 || x >= width || y < 0 || y >= height) return;
2199
+ writer.accumulator.storeTileBeforeState(x, y);
2200
+ let index = y * width + x;
2201
+ let bg = target.data32[index];
2202
+ let finalColor = color;
2203
+ if (alpha < 255) {
2204
+ let baseSrcAlpha = color >>> 24;
2205
+ let finalAlpha = baseSrcAlpha * alpha + 128 >> 8;
2206
+ finalColor = (color & 16777215 | finalAlpha << 24) >>> 0;
2207
+ }
2208
+ target.data32[index] = blendFn(finalColor, bg);
2209
+ }
2210
+ };
2015
2211
  }
2016
2212
 
2017
- // src/IndexedImage/indexedImageToImageData.ts
2018
- function indexedImageToImageData(indexedImage) {
2019
- const { width, height, data, palette } = indexedImage;
2020
- const result = new ImageData(width, height);
2021
- const data32 = new Uint32Array(result.data.buffer);
2022
- for (let i = 0; i < data.length; i++) {
2023
- const paletteIndex = data[i];
2024
- const color = palette[paletteIndex];
2025
- data32[i] = color;
2213
+ // src/PixelData/blendPixelData.ts
2214
+ function blendPixelData(dst, src, opts) {
2215
+ const {
2216
+ x: targetX = 0,
2217
+ y: targetY = 0,
2218
+ sx: sourceX = 0,
2219
+ sy: sourceY = 0,
2220
+ w: width = src.width,
2221
+ h: height = src.height,
2222
+ alpha: globalAlpha = 255,
2223
+ blendFn = sourceOverFast,
2224
+ mask,
2225
+ maskType = 0 /* ALPHA */,
2226
+ mw,
2227
+ mx = 0,
2228
+ my = 0,
2229
+ invertMask = false
2230
+ } = opts;
2231
+ if (globalAlpha === 0) return;
2232
+ let x = targetX;
2233
+ let y = targetY;
2234
+ let sx = sourceX;
2235
+ let sy = sourceY;
2236
+ let w = width;
2237
+ let h = height;
2238
+ if (sx < 0) {
2239
+ x -= sx;
2240
+ w += sx;
2241
+ sx = 0;
2026
2242
  }
2027
- return result;
2028
- }
2029
-
2030
- // src/Input/fileInputChangeToImageData.ts
2031
- async function fileInputChangeToImageData(event) {
2032
- const target = event.target;
2033
- const file = target.files?.[0];
2034
- if (!file) return null;
2035
- return await fileToImageData(file);
2036
- }
2037
-
2038
- // src/Input/fileToImageData.ts
2039
- var UnsupportedFormatError = class extends Error {
2040
- constructor(mimeType) {
2041
- super(`File type ${mimeType} is not a supported image format.`);
2042
- this.name = "UnsupportedFormatError";
2243
+ if (sy < 0) {
2244
+ y -= sy;
2245
+ h += sy;
2246
+ sy = 0;
2043
2247
  }
2044
- };
2045
- async function fileToImageData(file) {
2046
- if (!file) return null;
2047
- if (!file.type.startsWith("image/")) {
2048
- throw new UnsupportedFormatError(file.type);
2049
- }
2050
- let bitmap = null;
2051
- try {
2052
- bitmap = await createImageBitmap(file);
2053
- const canvas = new OffscreenCanvas(
2054
- bitmap.width,
2055
- bitmap.height
2056
- );
2057
- const ctx = canvas.getContext("2d");
2058
- if (!ctx) throw new Error(OFFSCREEN_CANVAS_CTX_FAILED);
2059
- ctx.drawImage(
2060
- bitmap,
2061
- 0,
2062
- 0
2063
- );
2064
- return ctx.getImageData(
2065
- 0,
2066
- 0,
2067
- bitmap.width,
2068
- bitmap.height
2069
- );
2070
- } finally {
2071
- bitmap?.close();
2072
- }
2073
- }
2074
-
2075
- // src/Input/getSupportedRasterFormats.ts
2076
- var formatsPromise = null;
2077
- var defaultRasterMimes = [
2078
- "image/png",
2079
- "image/jpeg",
2080
- "image/webp",
2081
- "image/avif",
2082
- "image/gif",
2083
- "image/bmp"
2084
- ];
2085
- async function getSupportedPixelFormats(rasterMimes = defaultRasterMimes) {
2086
- if (formatsPromise) {
2087
- return formatsPromise;
2088
- }
2089
- const probeCanvas = async () => {
2090
- const canvas = new OffscreenCanvas(1, 1);
2091
- const results = await Promise.all(
2092
- rasterMimes.map(async (mime) => {
2093
- try {
2094
- const blob = await canvas.convertToBlob({
2095
- type: mime
2096
- });
2097
- return blob.type === mime ? mime : null;
2098
- } catch {
2099
- return null;
2100
- }
2101
- })
2102
- );
2103
- return results.filter((type) => {
2104
- return type !== null;
2105
- });
2106
- };
2107
- formatsPromise = probeCanvas().catch((error) => {
2108
- formatsPromise = null;
2109
- throw error;
2110
- });
2111
- return formatsPromise;
2112
- }
2113
-
2114
- // src/Mask/copyMask.ts
2115
- function copyMask(src) {
2116
- return src.slice();
2117
- }
2118
-
2119
- // src/Mask/invertMask.ts
2120
- function invertBinaryMask(dst) {
2121
- const len = dst.length;
2122
- for (let i = 0; i < len; i++) {
2123
- dst[i] = dst[i] === 0 ? 1 : 0;
2124
- }
2125
- }
2126
- function invertAlphaMask(dst) {
2127
- const len = dst.length;
2128
- for (let i = 0; i < len; i++) {
2129
- dst[i] = 255 - dst[i];
2130
- }
2131
- }
2132
-
2133
- // src/Mask/mergeMasks.ts
2134
- function mergeMasks(dst, dstWidth, src, opts) {
2135
- const {
2136
- x: targetX = 0,
2137
- y: targetY = 0,
2138
- w: width = 0,
2139
- h: height = 0,
2140
- alpha: globalAlpha = 255,
2141
- maskType = 0 /* ALPHA */,
2142
- mw,
2143
- mx = 0,
2144
- my = 0,
2145
- invertMask = false
2146
- } = opts;
2147
- if (width <= 0 || height <= 0 || globalAlpha === 0) {
2148
- return;
2149
- }
2150
- const sPitch = mw ?? width;
2151
- const isAlpha = maskType === 0 /* ALPHA */;
2152
- for (let iy = 0; iy < height; iy++) {
2153
- const dy = targetY + iy;
2154
- const sy = my + iy;
2155
- if (dy < 0 || sy < 0) {
2156
- continue;
2157
- }
2158
- for (let ix = 0; ix < width; ix++) {
2159
- const dx = targetX + ix;
2160
- const sx = mx + ix;
2161
- if (dx < 0 || dx >= dstWidth || sx < 0 || sx >= sPitch) {
2162
- continue;
2163
- }
2164
- const dIdx = dy * dstWidth + dx;
2165
- const sIdx = sy * sPitch + sx;
2166
- const mVal = src[sIdx];
2167
- let weight = globalAlpha;
2168
- if (isAlpha) {
2169
- const effectiveM = invertMask ? 255 - mVal : mVal;
2170
- if (effectiveM === 0) {
2171
- dst[dIdx] = 0;
2172
- continue;
2173
- }
2174
- weight = globalAlpha === 255 ? effectiveM : effectiveM * globalAlpha + 128 >> 8;
2175
- } else {
2176
- const isHit = invertMask ? mVal === 0 : mVal === 1;
2177
- if (!isHit) {
2178
- dst[dIdx] = 0;
2179
- continue;
2180
- }
2181
- weight = globalAlpha;
2182
- }
2183
- if (weight === 0) {
2184
- dst[dIdx] = 0;
2185
- continue;
2186
- }
2187
- const da = dst[dIdx];
2188
- if (da === 0) {
2189
- } else if (weight === 255) {
2190
- } else if (da === 255) {
2191
- dst[dIdx] = weight;
2192
- } else {
2193
- dst[dIdx] = da * weight + 128 >> 8;
2194
- }
2195
- }
2196
- }
2197
- }
2198
-
2199
- // src/PixelData/PixelData.ts
2200
- var PixelData = class _PixelData {
2201
- data32;
2202
- imageData;
2203
- get width() {
2204
- return this.imageData.width;
2205
- }
2206
- get height() {
2207
- return this.imageData.height;
2208
- }
2209
- constructor(imageData) {
2210
- this.data32 = imageDataToUInt32Array(imageData);
2211
- this.imageData = imageData;
2212
- }
2213
- set(imageData) {
2214
- this.imageData = imageData;
2215
- this.data32 = imageDataToUInt32Array(imageData);
2216
- }
2217
- /**
2218
- * Creates a deep copy of the PixelData using the environment's ImageData constructor.
2219
- */
2220
- copy() {
2221
- const buffer = new Uint8ClampedArray(this.imageData.data);
2222
- const ImageConstructor = typeof ImageData !== "undefined" ? ImageData : this.imageData.constructor;
2223
- const newImageData = new ImageConstructor(
2224
- buffer,
2225
- this.width,
2226
- this.height
2227
- );
2228
- return new _PixelData(newImageData);
2229
- }
2230
- };
2231
-
2232
- // src/PixelData/applyMaskToPixelData.ts
2233
- function applyMaskToPixelData(dst, mask, opts) {
2234
- const {
2235
- x: targetX = 0,
2236
- y: targetY = 0,
2237
- w: width = dst.width,
2238
- h: height = dst.height,
2239
- alpha: globalAlpha = 255,
2240
- maskType = 0 /* ALPHA */,
2241
- mw,
2242
- mx = 0,
2243
- my = 0,
2244
- invertMask = false
2245
- } = opts;
2246
- let x = targetX;
2247
- let y = targetY;
2248
- let w = width;
2249
- let h = height;
2250
- if (x < 0) {
2251
- w += x;
2252
- x = 0;
2248
+ w = Math.min(w, src.width - sx);
2249
+ h = Math.min(h, src.height - sy);
2250
+ if (x < 0) {
2251
+ sx -= x;
2252
+ w += x;
2253
+ x = 0;
2253
2254
  }
2254
2255
  if (y < 0) {
2256
+ sy -= y;
2255
2257
  h += y;
2256
2258
  y = 0;
2257
2259
  }
2258
2260
  const actualW = Math.min(w, dst.width - x);
2259
2261
  const actualH = Math.min(h, dst.height - y);
2260
- if (actualW <= 0 || actualH <= 0 || globalAlpha === 0) {
2261
- return;
2262
- }
2262
+ if (actualW <= 0 || actualH <= 0) return;
2263
2263
  const dst32 = dst.data32;
2264
+ const src32 = src.data32;
2264
2265
  const dw = dst.width;
2266
+ const sw = src.width;
2265
2267
  const mPitch = mw ?? width;
2266
- const isAlpha = maskType === 0 /* ALPHA */;
2268
+ const isAlphaMask = maskType === 0 /* ALPHA */;
2267
2269
  const dx = x - targetX;
2268
2270
  const dy = y - targetY;
2269
2271
  let dIdx = y * dw + x;
2272
+ let sIdx = sy * sw + sx;
2270
2273
  let mIdx = (my + dy) * mPitch + (mx + dx);
2271
2274
  const dStride = dw - actualW;
2275
+ const sStride = sw - actualW;
2272
2276
  const mStride = mPitch - actualW;
2277
+ const isOverwrite = blendFn.isOverwrite;
2273
2278
  for (let iy = 0; iy < actualH; iy++) {
2274
2279
  for (let ix = 0; ix < actualW; ix++) {
2275
- const mVal = mask[mIdx];
2280
+ const baseSrcColor = src32[sIdx];
2281
+ const baseSrcAlpha = baseSrcColor >>> 24;
2282
+ if (baseSrcAlpha === 0 && !isOverwrite) {
2283
+ dIdx++;
2284
+ sIdx++;
2285
+ mIdx++;
2286
+ continue;
2287
+ }
2276
2288
  let weight = globalAlpha;
2277
- if (isAlpha) {
2278
- const effectiveM = invertMask ? 255 - mVal : mVal;
2279
- if (effectiveM === 0) {
2280
- dst32[dIdx] = (dst32[dIdx] & 16777215) >>> 0;
2289
+ if (mask) {
2290
+ const mVal = mask[mIdx];
2291
+ if (isAlphaMask) {
2292
+ const effectiveM = invertMask ? 255 - mVal : mVal;
2293
+ if (effectiveM === 0) {
2294
+ dIdx++;
2295
+ sIdx++;
2296
+ mIdx++;
2297
+ continue;
2298
+ }
2299
+ if (globalAlpha === 255) {
2300
+ weight = effectiveM;
2301
+ } else if (effectiveM === 255) {
2302
+ weight = globalAlpha;
2303
+ } else {
2304
+ weight = effectiveM * globalAlpha + 128 >> 8;
2305
+ }
2306
+ } else {
2307
+ const isHit = invertMask ? mVal === 0 : mVal === 1;
2308
+ if (!isHit) {
2309
+ dIdx++;
2310
+ sIdx++;
2311
+ mIdx++;
2312
+ continue;
2313
+ }
2314
+ weight = globalAlpha;
2315
+ }
2316
+ if (weight === 0) {
2281
2317
  dIdx++;
2318
+ sIdx++;
2282
2319
  mIdx++;
2283
2320
  continue;
2284
2321
  }
2285
- weight = globalAlpha === 255 ? effectiveM : effectiveM * globalAlpha + 128 >> 8;
2286
- } else {
2287
- const isHit = invertMask ? mVal === 0 : mVal === 1;
2288
- if (!isHit) {
2289
- dst32[dIdx] = (dst32[dIdx] & 16777215) >>> 0;
2322
+ }
2323
+ let currentSrcColor = baseSrcColor;
2324
+ if (weight < 255) {
2325
+ let currentSrcAlpha = baseSrcAlpha;
2326
+ if (baseSrcAlpha === 255) {
2327
+ currentSrcAlpha = weight;
2328
+ } else {
2329
+ currentSrcAlpha = baseSrcAlpha * weight + 128 >> 8;
2330
+ }
2331
+ if (!isOverwrite && currentSrcAlpha === 0) {
2290
2332
  dIdx++;
2333
+ sIdx++;
2291
2334
  mIdx++;
2292
2335
  continue;
2293
2336
  }
2294
- weight = globalAlpha;
2295
- }
2296
- if (weight === 0) {
2297
- dst32[dIdx] = (dst32[dIdx] & 16777215) >>> 0;
2298
- } else {
2299
- const d = dst32[dIdx];
2300
- const da = d >>> 24;
2301
- let finalAlpha = da;
2302
- if (da === 0) {
2303
- } else if (weight === 255) {
2304
- } else if (da === 255) {
2305
- finalAlpha = weight;
2306
- } else {
2307
- finalAlpha = da * weight + 128 >> 8;
2308
- }
2309
- dst32[dIdx] = (d & 16777215 | finalAlpha << 24) >>> 0;
2337
+ currentSrcColor = (baseSrcColor & 16777215 | currentSrcAlpha << 24) >>> 0;
2310
2338
  }
2339
+ dst32[dIdx] = blendFn(currentSrcColor, dst32[dIdx]);
2311
2340
  dIdx++;
2341
+ sIdx++;
2312
2342
  mIdx++;
2313
2343
  }
2314
2344
  dIdx += dStride;
2345
+ sIdx += sStride;
2315
2346
  mIdx += mStride;
2316
2347
  }
2317
2348
  }
2318
2349
 
2319
- // src/PixelData/blendColorPixelData.ts
2320
- function blendColorPixelData(dst, color, opts = {}) {
2350
+ // src/History/PixelMutator/mutatorBlendPixelData.ts
2351
+ function mutatorBlendPixelData(writer) {
2352
+ return {
2353
+ blendPixelData(src, opts) {
2354
+ const {
2355
+ x = 0,
2356
+ y = 0,
2357
+ w = src.width,
2358
+ h = src.height
2359
+ } = opts;
2360
+ writer.accumulator.storeRegionBeforeState(x, y, w, h);
2361
+ blendPixelData(writer.target, src, opts);
2362
+ }
2363
+ };
2364
+ }
2365
+
2366
+ // src/PixelData/fillPixelData.ts
2367
+ function fillPixelData(dst, color, _x, _y, _w, _h) {
2368
+ let x;
2369
+ let y;
2370
+ let w;
2371
+ let h;
2372
+ if (typeof _x === "object") {
2373
+ x = _x.x ?? 0;
2374
+ y = _x.y ?? 0;
2375
+ w = _x.w ?? dst.width;
2376
+ h = _x.h ?? dst.height;
2377
+ } else if (typeof _x === "number") {
2378
+ x = _x;
2379
+ y = _y;
2380
+ w = _w;
2381
+ h = _h;
2382
+ } else {
2383
+ x = 0;
2384
+ y = 0;
2385
+ w = dst.width;
2386
+ h = dst.height;
2387
+ }
2388
+ if (x < 0) {
2389
+ w += x;
2390
+ x = 0;
2391
+ }
2392
+ if (y < 0) {
2393
+ h += y;
2394
+ y = 0;
2395
+ }
2396
+ const actualW = Math.min(w, dst.width - x);
2397
+ const actualH = Math.min(h, dst.height - y);
2398
+ if (actualW <= 0 || actualH <= 0) {
2399
+ return;
2400
+ }
2401
+ const dst32 = dst.data32;
2402
+ const dw = dst.width;
2403
+ if (actualW === dw && actualH === dst.height && x === 0 && y === 0) {
2404
+ dst32.fill(color);
2405
+ return;
2406
+ }
2407
+ for (let iy = 0; iy < actualH; iy++) {
2408
+ const start = (y + iy) * dw + x;
2409
+ const end = start + actualW;
2410
+ dst32.fill(color, start, end);
2411
+ }
2412
+ }
2413
+
2414
+ // src/History/PixelMutator/mutatorFillPixelData.ts
2415
+ function mutatorFill(writer) {
2416
+ return {
2417
+ fill(color, rect = {}) {
2418
+ const {
2419
+ x = 0,
2420
+ y = 0,
2421
+ w = writer.target.width,
2422
+ h = writer.target.height
2423
+ } = rect;
2424
+ writer.accumulator.storeRegionBeforeState(x, y, w, h);
2425
+ fillPixelData(writer.target, color, x, y, w, h);
2426
+ }
2427
+ };
2428
+ }
2429
+
2430
+ // src/PixelData/invertPixelData.ts
2431
+ function invertPixelData(pixelData, opts = {}) {
2432
+ const dst = pixelData;
2321
2433
  const {
2322
2434
  x: targetX = 0,
2323
2435
  y: targetY = 0,
2324
- w: width = dst.width,
2325
- h: height = dst.height,
2326
- alpha: globalAlpha = 255,
2327
- blendFn = sourceOverFast,
2436
+ w: width = pixelData.width,
2437
+ h: height = pixelData.height,
2328
2438
  mask,
2329
- maskType = 0 /* ALPHA */,
2330
2439
  mw,
2331
2440
  mx = 0,
2332
2441
  my = 0,
2333
2442
  invertMask = false
2334
2443
  } = opts;
2335
- if (globalAlpha === 0) return;
2336
- const baseSrcAlpha = color >>> 24;
2337
- const isOverwrite = blendFn.isOverwrite;
2338
- if (baseSrcAlpha === 0 && !isOverwrite) return;
2339
2444
  let x = targetX;
2340
2445
  let y = targetY;
2341
2446
  let w = width;
@@ -2354,255 +2459,798 @@ function blendColorPixelData(dst, color, opts = {}) {
2354
2459
  const dst32 = dst.data32;
2355
2460
  const dw = dst.width;
2356
2461
  const mPitch = mw ?? width;
2357
- const isAlphaMask = maskType === 0 /* ALPHA */;
2358
2462
  const dx = x - targetX;
2359
2463
  const dy = y - targetY;
2360
2464
  let dIdx = y * dw + x;
2361
2465
  let mIdx = (my + dy) * mPitch + (mx + dx);
2362
2466
  const dStride = dw - actualW;
2363
2467
  const mStride = mPitch - actualW;
2364
- for (let iy = 0; iy < actualH; iy++) {
2365
- for (let ix = 0; ix < actualW; ix++) {
2366
- let weight = globalAlpha;
2367
- if (mask) {
2468
+ if (mask) {
2469
+ for (let iy = 0; iy < actualH; iy++) {
2470
+ for (let ix = 0; ix < actualW; ix++) {
2368
2471
  const mVal = mask[mIdx];
2369
- if (isAlphaMask) {
2370
- const effectiveM = invertMask ? 255 - mVal : mVal;
2371
- if (effectiveM === 0) {
2372
- dIdx++;
2373
- mIdx++;
2374
- continue;
2375
- }
2376
- if (globalAlpha === 255) {
2377
- weight = effectiveM;
2378
- } else if (effectiveM === 255) {
2379
- weight = globalAlpha;
2380
- } else {
2381
- weight = effectiveM * globalAlpha + 128 >> 8;
2382
- }
2383
- } else {
2384
- const isHit = invertMask ? mVal === 0 : mVal === 1;
2385
- if (!isHit) {
2386
- dIdx++;
2387
- mIdx++;
2388
- continue;
2389
- }
2390
- weight = globalAlpha;
2391
- }
2392
- if (weight === 0) {
2393
- dIdx++;
2394
- mIdx++;
2395
- continue;
2472
+ const isHit = invertMask ? mVal === 0 : mVal === 1;
2473
+ if (isHit) {
2474
+ dst32[dIdx] = dst32[dIdx] ^ 16777215;
2396
2475
  }
2476
+ dIdx++;
2477
+ mIdx++;
2397
2478
  }
2398
- let currentSrcColor = color;
2399
- if (weight < 255) {
2400
- let currentSrcAlpha = baseSrcAlpha;
2401
- if (baseSrcAlpha === 255) {
2402
- currentSrcAlpha = weight;
2403
- } else {
2404
- currentSrcAlpha = baseSrcAlpha * weight + 128 >> 8;
2405
- }
2406
- if (!isOverwrite && currentSrcAlpha === 0) {
2407
- dIdx++;
2408
- mIdx++;
2409
- continue;
2410
- }
2411
- currentSrcColor = (color & 16777215 | currentSrcAlpha << 24) >>> 0;
2479
+ dIdx += dStride;
2480
+ mIdx += mStride;
2481
+ }
2482
+ } else {
2483
+ for (let iy = 0; iy < actualH; iy++) {
2484
+ for (let ix = 0; ix < actualW; ix++) {
2485
+ dst32[dIdx] = dst32[dIdx] ^ 16777215;
2486
+ dIdx++;
2412
2487
  }
2413
- dst32[dIdx] = blendFn(currentSrcColor, dst32[dIdx]);
2414
- dIdx++;
2415
- mIdx++;
2488
+ dIdx += dStride;
2416
2489
  }
2417
- dIdx += dStride;
2418
- mIdx += mStride;
2419
2490
  }
2420
2491
  }
2421
2492
 
2422
- // src/PixelData/blendPixelData.ts
2423
- function blendPixelData(dst, src, opts) {
2493
+ // src/History/PixelMutator/mutatorInvert.ts
2494
+ function mutatorInvert(writer) {
2495
+ return {
2496
+ invert(opts = {}) {
2497
+ const {
2498
+ x = 0,
2499
+ y = 0,
2500
+ w = writer.target.width,
2501
+ h = writer.target.height
2502
+ } = opts;
2503
+ writer.accumulator.storeRegionBeforeState(x, y, w, h);
2504
+ invertPixelData(writer.target, opts);
2505
+ }
2506
+ };
2507
+ }
2508
+
2509
+ // src/History/PixelMutator.ts
2510
+ function makeFullPixelMutator(writer) {
2511
+ return {
2512
+ ...mutatorApplyMask(writer),
2513
+ ...mutatorBlendPixelData(writer),
2514
+ ...mutatorBlendColor(writer),
2515
+ ...mutatorBlendPixel(writer),
2516
+ ...mutatorFill(writer),
2517
+ ...mutatorInvert(writer)
2518
+ };
2519
+ }
2520
+
2521
+ // src/ImageData/ReusableImageData.ts
2522
+ function makeReusableImageData() {
2523
+ let imageData = null;
2524
+ let buffer = null;
2525
+ return function getReusableImageData(width, height) {
2526
+ const hasInstance = !!imageData;
2527
+ const widthMatches = hasInstance && imageData.width === width;
2528
+ const heightMatches = hasInstance && imageData.height === height;
2529
+ if (!widthMatches || !heightMatches) {
2530
+ const buffer2 = new Uint8ClampedArray(width * height * 4);
2531
+ imageData = new ImageData(buffer2, width, height);
2532
+ }
2533
+ return imageData;
2534
+ };
2535
+ }
2536
+
2537
+ // src/ImageData/copyImageData.ts
2538
+ function copyImageData({ data, width, height }) {
2539
+ return new ImageData(data.slice(), width, height);
2540
+ }
2541
+ function copyImageDataLike({ data, width, height }) {
2542
+ return {
2543
+ data: data.slice(),
2544
+ width,
2545
+ height
2546
+ };
2547
+ }
2548
+
2549
+ // src/PixelData/pixelDataToAlphaMask.ts
2550
+ function pixelDataToAlphaMask(pixelData) {
2424
2551
  const {
2425
- x: targetX = 0,
2426
- y: targetY = 0,
2427
- sx: sourceX = 0,
2428
- sy: sourceY = 0,
2429
- w: width = src.width,
2430
- h: height = src.height,
2552
+ data32,
2553
+ width,
2554
+ height
2555
+ } = pixelData;
2556
+ const len = data32.length;
2557
+ const mask = new Uint8Array(width * height);
2558
+ for (let i = 0; i < len; i++) {
2559
+ const val = data32[i];
2560
+ mask[i] = val >>> 24 & 255;
2561
+ }
2562
+ return mask;
2563
+ }
2564
+
2565
+ // src/ImageData/imageDataToAlphaMask.ts
2566
+ function imageDataToAlphaMask(imageData) {
2567
+ const {
2568
+ width,
2569
+ height,
2570
+ data
2571
+ } = imageData;
2572
+ const data32 = new Uint32Array(
2573
+ data.buffer,
2574
+ data.byteOffset,
2575
+ data.byteLength >> 2
2576
+ );
2577
+ const len = data32.length;
2578
+ const mask = new Uint8Array(width * height);
2579
+ for (let i = 0; i < len; i++) {
2580
+ const val = data32[i];
2581
+ mask[i] = val >>> 24 & 255;
2582
+ }
2583
+ return mask;
2584
+ }
2585
+
2586
+ // src/ImageData/imageDataToDataUrl.ts
2587
+ var get = makeReusableCanvas();
2588
+ function imageDataToDataUrl(imageData) {
2589
+ const { canvas, ctx } = get(imageData.width, imageData.height);
2590
+ ctx.putImageData(imageData, 0, 0);
2591
+ return canvas.toDataURL();
2592
+ }
2593
+ imageDataToDataUrl.reset = get.reset;
2594
+
2595
+ // src/ImageData/imageDataToUInt32Array.ts
2596
+ function imageDataToUInt32Array(imageData) {
2597
+ return new Uint32Array(
2598
+ imageData.data.buffer,
2599
+ imageData.data.byteOffset,
2600
+ // Shift right by 2 is a fast bitwise division by 4.
2601
+ imageData.data.byteLength >> 2
2602
+ );
2603
+ }
2604
+
2605
+ // src/ImageData/invertImageData.ts
2606
+ function invertImageData(imageData) {
2607
+ const data = imageData.data;
2608
+ let length = data.length;
2609
+ for (let i = 0; i < length; i += 4) {
2610
+ data[i] = 255 - data[i];
2611
+ data[i + 1] = 255 - data[i + 1];
2612
+ data[i + 2] = 255 - data[i + 2];
2613
+ }
2614
+ return imageData;
2615
+ }
2616
+
2617
+ // src/Internal/resample32.ts
2618
+ var resample32Scratch = {
2619
+ data: null,
2620
+ width: 0,
2621
+ height: 0
2622
+ };
2623
+ function resample32(srcData32, srcW, srcH, factor) {
2624
+ const dstW = Math.max(1, srcW * factor | 0);
2625
+ const dstH = Math.max(1, srcH * factor | 0);
2626
+ const dstData = new Int32Array(dstW * dstH);
2627
+ const scaleX = srcW / dstW;
2628
+ const scaleY = srcH / dstH;
2629
+ for (let y = 0; y < dstH; y++) {
2630
+ const srcY = Math.min(srcH - 1, y * scaleY | 0);
2631
+ const srcRowOffset = srcY * srcW;
2632
+ const dstRowOffset = y * dstW;
2633
+ for (let x = 0; x < dstW; x++) {
2634
+ const srcX = Math.min(srcW - 1, x * scaleX | 0);
2635
+ dstData[dstRowOffset + x] = srcData32[srcRowOffset + srcX];
2636
+ }
2637
+ }
2638
+ resample32Scratch.data = dstData;
2639
+ resample32Scratch.width = dstW;
2640
+ resample32Scratch.height = dstH;
2641
+ return resample32Scratch;
2642
+ }
2643
+
2644
+ // src/ImageData/resampleImageData.ts
2645
+ function resampleImageData(source, factor) {
2646
+ const src32 = new Uint32Array(source.data.buffer);
2647
+ const { data, width, height } = resample32(src32, source.width, source.height, factor);
2648
+ const uint8ClampedArray = new Uint8ClampedArray(data.buffer);
2649
+ return new ImageData(uint8ClampedArray, width, height);
2650
+ }
2651
+
2652
+ // src/ImageData/resizeImageData.ts
2653
+ function resizeImageData(current, newWidth, newHeight, offsetX = 0, offsetY = 0) {
2654
+ const result = new ImageData(newWidth, newHeight);
2655
+ const {
2656
+ width: oldW,
2657
+ height: oldH,
2658
+ data: oldData
2659
+ } = current;
2660
+ const newData = result.data;
2661
+ const x0 = Math.max(0, offsetX);
2662
+ const y0 = Math.max(0, offsetY);
2663
+ const x1 = Math.min(newWidth, offsetX + oldW);
2664
+ const y1 = Math.min(newHeight, offsetY + oldH);
2665
+ if (x1 <= x0 || y1 <= y0) {
2666
+ return result;
2667
+ }
2668
+ const rowCount = y1 - y0;
2669
+ const rowLen = (x1 - x0) * 4;
2670
+ for (let row = 0; row < rowCount; row++) {
2671
+ const dstY = y0 + row;
2672
+ const srcY = dstY - offsetY;
2673
+ const srcX = x0 - offsetX;
2674
+ const dstStart = (dstY * newWidth + x0) * 4;
2675
+ const srcStart = (srcY * oldW + srcX) * 4;
2676
+ newData.set(
2677
+ oldData.subarray(srcStart, srcStart + rowLen),
2678
+ dstStart
2679
+ );
2680
+ }
2681
+ return result;
2682
+ }
2683
+
2684
+ // src/ImageData/serialization.ts
2685
+ function base64EncodeArrayBuffer(buffer) {
2686
+ const uint8 = new Uint8Array(buffer);
2687
+ const decoder = new TextDecoder("latin1");
2688
+ const binary = decoder.decode(uint8);
2689
+ return btoa(binary);
2690
+ }
2691
+ function base64DecodeArrayBuffer(encoded) {
2692
+ const binary = atob(encoded);
2693
+ const bytes = new Uint8ClampedArray(binary.length);
2694
+ for (let i = 0; i < binary.length; i++) {
2695
+ bytes[i] = binary.charCodeAt(i);
2696
+ }
2697
+ return bytes;
2698
+ }
2699
+ function serializeImageData(imageData) {
2700
+ return {
2701
+ width: imageData.width,
2702
+ height: imageData.height,
2703
+ data: base64EncodeArrayBuffer(imageData.data.buffer)
2704
+ };
2705
+ }
2706
+ function serializeNullableImageData(imageData) {
2707
+ if (!imageData) return null;
2708
+ return serializeImageData(imageData);
2709
+ }
2710
+ function deserializeRawImageData(serialized) {
2711
+ return {
2712
+ width: serialized.width,
2713
+ height: serialized.height,
2714
+ data: base64DecodeArrayBuffer(serialized.data)
2715
+ };
2716
+ }
2717
+ function deserializeImageData(serialized) {
2718
+ const data = base64DecodeArrayBuffer(serialized.data);
2719
+ return new ImageData(data, serialized.width, serialized.height);
2720
+ }
2721
+ function deserializeNullableImageData(serialized) {
2722
+ if (!serialized) return null;
2723
+ return deserializeImageData(serialized);
2724
+ }
2725
+
2726
+ // src/ImageData/writeImageData.ts
2727
+ function writeImageData(target, source, x, y, sx = 0, sy = 0, sw = source.width, sh = source.height, mask = null, maskType = 1 /* BINARY */) {
2728
+ const dstW = target.width;
2729
+ const dstH = target.height;
2730
+ const dstData = target.data;
2731
+ const srcW = source.width;
2732
+ const srcData = source.data;
2733
+ const x0 = Math.max(0, x);
2734
+ const y0 = Math.max(0, y);
2735
+ const x1 = Math.min(dstW, x + sw);
2736
+ const y1 = Math.min(dstH, y + sh);
2737
+ if (x1 <= x0 || y1 <= y0) {
2738
+ return;
2739
+ }
2740
+ const useMask = !!mask;
2741
+ const rowCount = y1 - y0;
2742
+ const rowLenPixels = x1 - x0;
2743
+ for (let row = 0; row < rowCount; row++) {
2744
+ const dstY = y0 + row;
2745
+ const srcY = sy + (dstY - y);
2746
+ const srcXBase = sx + (x0 - x);
2747
+ const dstStart = (dstY * dstW + x0) * 4;
2748
+ const srcStart = (srcY * srcW + srcXBase) * 4;
2749
+ if (useMask && mask) {
2750
+ for (let ix = 0; ix < rowLenPixels; ix++) {
2751
+ const mi = srcY * srcW + (srcXBase + ix);
2752
+ const alpha = mask[mi];
2753
+ if (alpha === 0) {
2754
+ continue;
2755
+ }
2756
+ const di = dstStart + ix * 4;
2757
+ const si = srcStart + ix * 4;
2758
+ if (maskType === 1 /* BINARY */ || alpha === 255) {
2759
+ dstData[di] = srcData[si];
2760
+ dstData[di + 1] = srcData[si + 1];
2761
+ dstData[di + 2] = srcData[si + 2];
2762
+ dstData[di + 3] = srcData[si + 3];
2763
+ } else {
2764
+ const a = alpha / 255;
2765
+ const invA = 1 - a;
2766
+ dstData[di] = srcData[si] * a + dstData[di] * invA;
2767
+ dstData[di + 1] = srcData[si + 1] * a + dstData[di + 1] * invA;
2768
+ dstData[di + 2] = srcData[si + 2] * a + dstData[di + 2] * invA;
2769
+ dstData[di + 3] = srcData[si + 3] * a + dstData[di + 3] * invA;
2770
+ }
2771
+ }
2772
+ } else {
2773
+ const byteLen = rowLenPixels * 4;
2774
+ const sub = srcData.subarray(srcStart, srcStart + byteLen);
2775
+ dstData.set(sub, dstStart);
2776
+ }
2777
+ }
2778
+ }
2779
+
2780
+ // src/ImageData/writeImageDataBuffer.ts
2781
+ function writeImageDataBuffer(imageData, data, _x, _y, _w, _h) {
2782
+ const { x, y, w, h } = typeof _x === "object" ? _x : { x: _x, y: _y, w: _w, h: _h };
2783
+ const { width: dstW, height: dstH, data: dst } = imageData;
2784
+ const x0 = Math.max(0, x);
2785
+ const y0 = Math.max(0, y);
2786
+ const x1 = Math.min(dstW, x + w);
2787
+ const y1 = Math.min(dstH, y + h);
2788
+ if (x1 <= x0 || y1 <= y0) return;
2789
+ const rowLen = (x1 - x0) * 4;
2790
+ const srcCol = x0 - x;
2791
+ const srcYOffset = y0 - y;
2792
+ const actualH = y1 - y0;
2793
+ for (let row = 0; row < actualH; row++) {
2794
+ const dstStart = ((y0 + row) * dstW + x0) * 4;
2795
+ const srcRow = srcYOffset + row;
2796
+ const o = (srcRow * w + srcCol) * 4;
2797
+ dst.set(data.subarray(o, o + rowLen), dstStart);
2798
+ }
2799
+ }
2800
+
2801
+ // src/IndexedImage/IndexedImage.ts
2802
+ var IndexedImage = class _IndexedImage {
2803
+ /** The width of the image in pixels. */
2804
+ width;
2805
+ /** The height of the image in pixels. */
2806
+ height;
2807
+ /** Flat array of palette indices. Index = x + (y * width). */
2808
+ data;
2809
+ /** The palette of unique 32-bit colors (ABGR/RGBA packed) found in the image. */
2810
+ palette;
2811
+ /** The specific index in the palette reserved for fully transparent pixels. */
2812
+ transparentPalletIndex;
2813
+ /**
2814
+ * @param width - Image width.
2815
+ * @param height - Image height.
2816
+ * @param data - The indexed pixel data.
2817
+ * @param palette - The array of packed colors.
2818
+ * @param transparentPalletIndex - The index representing alpha 0.
2819
+ */
2820
+ constructor(width, height, data, palette, transparentPalletIndex) {
2821
+ this.width = width;
2822
+ this.height = height;
2823
+ this.data = data;
2824
+ this.palette = palette;
2825
+ this.transparentPalletIndex = transparentPalletIndex;
2826
+ }
2827
+ /**
2828
+ * Creates an IndexedImage from standard browser ImageData.
2829
+ * @param imageData - The source ImageData to convert.
2830
+ * @returns A new IndexedImage instance.
2831
+ */
2832
+ static fromImageData(imageData) {
2833
+ return _IndexedImage.fromRaw(imageData.data, imageData.width, imageData.height);
2834
+ }
2835
+ /**
2836
+ * Creates an IndexedImage from a raw byte buffer and dimensions.
2837
+ * Any pixel with an alpha channel of 0 is normalized to the transparent palette index.
2838
+ * @param data - Raw RGBA byte data.
2839
+ * @param width - Image width.
2840
+ * @param height - Image height.
2841
+ * @returns A new IndexedImage instance.
2842
+ */
2843
+ static fromRaw(data, width, height) {
2844
+ const buffer = data.buffer;
2845
+ const rawData = new Uint32Array(buffer);
2846
+ const indexedData = new Int32Array(rawData.length);
2847
+ const colorMap = /* @__PURE__ */ new Map();
2848
+ const transparentColor = 0;
2849
+ const transparentPalletIndex = 0;
2850
+ colorMap.set(transparentColor, transparentPalletIndex);
2851
+ for (let i = 0; i < rawData.length; i++) {
2852
+ const pixel = rawData[i];
2853
+ const alpha = pixel >>> 24 & 255;
2854
+ const isTransparent = alpha === 0;
2855
+ const colorKey = isTransparent ? transparentColor : pixel >>> 0;
2856
+ let id = colorMap.get(colorKey);
2857
+ if (id === void 0) {
2858
+ id = colorMap.size;
2859
+ colorMap.set(colorKey, id);
2860
+ }
2861
+ indexedData[i] = id;
2862
+ }
2863
+ const palette = Uint32Array.from(colorMap.keys());
2864
+ return new _IndexedImage(
2865
+ width,
2866
+ height,
2867
+ indexedData,
2868
+ palette,
2869
+ transparentPalletIndex
2870
+ );
2871
+ }
2872
+ /**
2873
+ * Retrieves the 32-bit packed color value at the given coordinates.
2874
+ * @param x - X coordinate.
2875
+ * @param y - Y coordinate.
2876
+ * @returns The packed color from the palette.
2877
+ */
2878
+ getColorAt(x, y) {
2879
+ const index = x + y * this.width;
2880
+ const paletteIndex = this.data[index];
2881
+ return this.palette[paletteIndex];
2882
+ }
2883
+ };
2884
+
2885
+ // src/IndexedImage/getIndexedImageColorCounts.ts
2886
+ function getIndexedImageColorCounts(indexedImage) {
2887
+ const data = indexedImage.data;
2888
+ const palette = indexedImage.palette;
2889
+ const frequencies = new Int32Array(palette.length);
2890
+ for (let i = 0; i < data.length; i++) {
2891
+ const colorIndex = data[i];
2892
+ frequencies[colorIndex]++;
2893
+ }
2894
+ return frequencies;
2895
+ }
2896
+
2897
+ // src/IndexedImage/indexedImageToAverageColor.ts
2898
+ function indexedImageToAverageColor(indexedImage, includeTransparent = false) {
2899
+ const { data, palette, transparentPalletIndex } = indexedImage;
2900
+ const counts = new Uint32Array(palette.length);
2901
+ for (let i = 0; i < data.length; i++) {
2902
+ const id = data[i];
2903
+ counts[id]++;
2904
+ }
2905
+ let rSum = 0;
2906
+ let gSum = 0;
2907
+ let bSum = 0;
2908
+ let aSum = 0;
2909
+ let totalWeight = 0;
2910
+ for (let id = 0; id < counts.length; id++) {
2911
+ const weight = counts[id];
2912
+ if (weight === 0) {
2913
+ continue;
2914
+ }
2915
+ if (!includeTransparent && id === transparentPalletIndex) {
2916
+ continue;
2917
+ }
2918
+ const color = palette[id] >>> 0;
2919
+ const r2 = color & 255;
2920
+ const g2 = color >> 8 & 255;
2921
+ const b2 = color >> 16 & 255;
2922
+ const a2 = color >> 24 & 255;
2923
+ rSum += r2 * weight;
2924
+ gSum += g2 * weight;
2925
+ bSum += b2 * weight;
2926
+ aSum += a2 * weight;
2927
+ totalWeight += weight;
2928
+ }
2929
+ if (totalWeight === 0) {
2930
+ return packColor(0, 0, 0, 0);
2931
+ }
2932
+ const r = rSum / totalWeight | 0;
2933
+ const g = gSum / totalWeight | 0;
2934
+ const b = bSum / totalWeight | 0;
2935
+ const a = aSum / totalWeight | 0;
2936
+ return packColor(r, g, b, a);
2937
+ }
2938
+
2939
+ // src/IndexedImage/resampleIndexedImage.ts
2940
+ function resampleIndexedImage(source, factor) {
2941
+ const { data, width, height } = resample32(
2942
+ source.data,
2943
+ source.width,
2944
+ source.height,
2945
+ factor
2946
+ );
2947
+ return new IndexedImage(
2948
+ width,
2949
+ height,
2950
+ data,
2951
+ source.palette,
2952
+ source.transparentPalletIndex
2953
+ );
2954
+ }
2955
+
2956
+ // src/IndexedImage/indexedImageToImageData.ts
2957
+ function indexedImageToImageData(indexedImage) {
2958
+ const { width, height, data, palette } = indexedImage;
2959
+ const result = new ImageData(width, height);
2960
+ const data32 = new Uint32Array(result.data.buffer);
2961
+ for (let i = 0; i < data.length; i++) {
2962
+ const paletteIndex = data[i];
2963
+ const color = palette[paletteIndex];
2964
+ data32[i] = color;
2965
+ }
2966
+ return result;
2967
+ }
2968
+
2969
+ // src/Input/fileInputChangeToImageData.ts
2970
+ async function fileInputChangeToImageData(event) {
2971
+ const target = event.target;
2972
+ const file = target.files?.[0];
2973
+ if (!file) return null;
2974
+ return await fileToImageData(file);
2975
+ }
2976
+
2977
+ // src/Input/fileToImageData.ts
2978
+ var UnsupportedFormatError = class extends Error {
2979
+ constructor(mimeType) {
2980
+ super(`File type ${mimeType} is not a supported image format.`);
2981
+ this.name = "UnsupportedFormatError";
2982
+ }
2983
+ };
2984
+ async function fileToImageData(file) {
2985
+ if (!file) return null;
2986
+ if (!file.type.startsWith("image/")) {
2987
+ throw new UnsupportedFormatError(file.type);
2988
+ }
2989
+ let bitmap = null;
2990
+ try {
2991
+ bitmap = await createImageBitmap(file);
2992
+ const canvas = new OffscreenCanvas(
2993
+ bitmap.width,
2994
+ bitmap.height
2995
+ );
2996
+ const ctx = canvas.getContext("2d");
2997
+ if (!ctx) throw new Error(OFFSCREEN_CANVAS_CTX_FAILED);
2998
+ ctx.drawImage(
2999
+ bitmap,
3000
+ 0,
3001
+ 0
3002
+ );
3003
+ return ctx.getImageData(
3004
+ 0,
3005
+ 0,
3006
+ bitmap.width,
3007
+ bitmap.height
3008
+ );
3009
+ } finally {
3010
+ bitmap?.close();
3011
+ }
3012
+ }
3013
+
3014
+ // src/Input/getSupportedRasterFormats.ts
3015
+ var formatsPromise = null;
3016
+ var defaultRasterMimes = [
3017
+ "image/png",
3018
+ "image/jpeg",
3019
+ "image/webp",
3020
+ "image/avif",
3021
+ "image/gif",
3022
+ "image/bmp"
3023
+ ];
3024
+ async function getSupportedPixelFormats(rasterMimes = defaultRasterMimes) {
3025
+ if (formatsPromise) {
3026
+ return formatsPromise;
3027
+ }
3028
+ const probeCanvas = async () => {
3029
+ const canvas = new OffscreenCanvas(1, 1);
3030
+ const results = await Promise.all(
3031
+ rasterMimes.map(async (mime) => {
3032
+ try {
3033
+ const blob = await canvas.convertToBlob({
3034
+ type: mime
3035
+ });
3036
+ return blob.type === mime ? mime : null;
3037
+ } catch {
3038
+ return null;
3039
+ }
3040
+ })
3041
+ );
3042
+ return results.filter((type) => {
3043
+ return type !== null;
3044
+ });
3045
+ };
3046
+ formatsPromise = probeCanvas().catch((error) => {
3047
+ formatsPromise = null;
3048
+ throw error;
3049
+ });
3050
+ return formatsPromise;
3051
+ }
3052
+
3053
+ // src/Mask/copyMask.ts
3054
+ function copyMask(src) {
3055
+ return src.slice();
3056
+ }
3057
+
3058
+ // src/Mask/invertMask.ts
3059
+ function invertBinaryMask(dst) {
3060
+ const len = dst.length;
3061
+ for (let i = 0; i < len; i++) {
3062
+ dst[i] = dst[i] === 0 ? 1 : 0;
3063
+ }
3064
+ }
3065
+ function invertAlphaMask(dst) {
3066
+ const len = dst.length;
3067
+ for (let i = 0; i < len; i++) {
3068
+ dst[i] = 255 - dst[i];
3069
+ }
3070
+ }
3071
+
3072
+ // src/Mask/mergeMasks.ts
3073
+ function mergeMasks(dst, dstWidth, src, opts) {
3074
+ const {
3075
+ x: targetX = 0,
3076
+ y: targetY = 0,
3077
+ w: width = 0,
3078
+ h: height = 0,
2431
3079
  alpha: globalAlpha = 255,
2432
- blendFn = FAST_BLEND_MODES[1 /* sourceOver */],
2433
- mask,
2434
3080
  maskType = 0 /* ALPHA */,
2435
3081
  mw,
2436
3082
  mx = 0,
2437
3083
  my = 0,
2438
3084
  invertMask = false
2439
3085
  } = opts;
2440
- if (globalAlpha === 0) return;
2441
- let x = targetX;
2442
- let y = targetY;
2443
- let sx = sourceX;
2444
- let sy = sourceY;
2445
- let w = width;
2446
- let h = height;
2447
- if (sx < 0) {
2448
- x -= sx;
2449
- w += sx;
2450
- sx = 0;
2451
- }
2452
- if (sy < 0) {
2453
- y -= sy;
2454
- h += sy;
2455
- sy = 0;
2456
- }
2457
- w = Math.min(w, src.width - sx);
2458
- h = Math.min(h, src.height - sy);
2459
- if (x < 0) {
2460
- sx -= x;
2461
- w += x;
2462
- x = 0;
2463
- }
2464
- if (y < 0) {
2465
- sy -= y;
2466
- h += y;
2467
- y = 0;
3086
+ if (width <= 0 || height <= 0 || globalAlpha === 0) {
3087
+ return;
2468
3088
  }
2469
- const actualW = Math.min(w, dst.width - x);
2470
- const actualH = Math.min(h, dst.height - y);
2471
- if (actualW <= 0 || actualH <= 0) return;
2472
- const dst32 = dst.data32;
2473
- const src32 = src.data32;
2474
- const dw = dst.width;
2475
- const sw = src.width;
2476
- const mPitch = mw ?? width;
2477
- const isAlphaMask = maskType === 0 /* ALPHA */;
2478
- const dx = x - targetX;
2479
- const dy = y - targetY;
2480
- let dIdx = y * dw + x;
2481
- let sIdx = sy * sw + sx;
2482
- let mIdx = (my + dy) * mPitch + (mx + dx);
2483
- const dStride = dw - actualW;
2484
- const sStride = sw - actualW;
2485
- const mStride = mPitch - actualW;
2486
- const isOverwrite = blendFn.isOverwrite;
2487
- for (let iy = 0; iy < actualH; iy++) {
2488
- for (let ix = 0; ix < actualW; ix++) {
2489
- const baseSrcColor = src32[sIdx];
2490
- const baseSrcAlpha = baseSrcColor >>> 24;
2491
- if (baseSrcAlpha === 0 && !isOverwrite) {
2492
- dIdx++;
2493
- sIdx++;
2494
- mIdx++;
3089
+ const sPitch = mw ?? width;
3090
+ const isAlpha = maskType === 0 /* ALPHA */;
3091
+ for (let iy = 0; iy < height; iy++) {
3092
+ const dy = targetY + iy;
3093
+ const sy = my + iy;
3094
+ if (dy < 0 || sy < 0) {
3095
+ continue;
3096
+ }
3097
+ for (let ix = 0; ix < width; ix++) {
3098
+ const dx = targetX + ix;
3099
+ const sx = mx + ix;
3100
+ if (dx < 0 || dx >= dstWidth || sx < 0 || sx >= sPitch) {
2495
3101
  continue;
2496
3102
  }
3103
+ const dIdx = dy * dstWidth + dx;
3104
+ const sIdx = sy * sPitch + sx;
3105
+ const mVal = src[sIdx];
2497
3106
  let weight = globalAlpha;
2498
- if (mask) {
2499
- const mVal = mask[mIdx];
2500
- if (isAlphaMask) {
2501
- const effectiveM = invertMask ? 255 - mVal : mVal;
2502
- if (effectiveM === 0) {
2503
- dIdx++;
2504
- sIdx++;
2505
- mIdx++;
2506
- continue;
2507
- }
2508
- if (globalAlpha === 255) {
2509
- weight = effectiveM;
2510
- } else if (effectiveM === 255) {
2511
- weight = globalAlpha;
2512
- } else {
2513
- weight = effectiveM * globalAlpha + 128 >> 8;
2514
- }
2515
- } else {
2516
- const isHit = invertMask ? mVal === 0 : mVal === 1;
2517
- if (!isHit) {
2518
- dIdx++;
2519
- sIdx++;
2520
- mIdx++;
2521
- continue;
2522
- }
2523
- weight = globalAlpha;
2524
- }
2525
- if (weight === 0) {
2526
- dIdx++;
2527
- sIdx++;
2528
- mIdx++;
3107
+ if (isAlpha) {
3108
+ const effectiveM = invertMask ? 255 - mVal : mVal;
3109
+ if (effectiveM === 0) {
3110
+ dst[dIdx] = 0;
2529
3111
  continue;
2530
3112
  }
2531
- }
2532
- let currentSrcColor = baseSrcColor;
2533
- if (weight < 255) {
2534
- let currentSrcAlpha = baseSrcAlpha;
2535
- if (baseSrcAlpha === 255) {
2536
- currentSrcAlpha = weight;
2537
- } else {
2538
- currentSrcAlpha = baseSrcAlpha * weight + 128 >> 8;
2539
- }
2540
- if (!isOverwrite && currentSrcAlpha === 0) {
2541
- dIdx++;
2542
- sIdx++;
2543
- mIdx++;
3113
+ weight = globalAlpha === 255 ? effectiveM : effectiveM * globalAlpha + 128 >> 8;
3114
+ } else {
3115
+ const isHit = invertMask ? mVal === 0 : mVal === 1;
3116
+ if (!isHit) {
3117
+ dst[dIdx] = 0;
2544
3118
  continue;
2545
3119
  }
2546
- currentSrcColor = (baseSrcColor & 16777215 | currentSrcAlpha << 24) >>> 0;
3120
+ weight = globalAlpha;
3121
+ }
3122
+ if (weight === 0) {
3123
+ dst[dIdx] = 0;
3124
+ continue;
3125
+ }
3126
+ const da = dst[dIdx];
3127
+ if (da === 0) {
3128
+ } else if (weight === 255) {
3129
+ } else if (da === 255) {
3130
+ dst[dIdx] = weight;
3131
+ } else {
3132
+ dst[dIdx] = da * weight + 128 >> 8;
2547
3133
  }
2548
- dst32[dIdx] = blendFn(currentSrcColor, dst32[dIdx]);
2549
- dIdx++;
2550
- sIdx++;
2551
- mIdx++;
2552
3134
  }
2553
- dIdx += dStride;
2554
- sIdx += sStride;
2555
- mIdx += mStride;
2556
3135
  }
2557
3136
  }
2558
3137
 
2559
- // src/PixelData/fillPixelData.ts
2560
- function fillPixelData(dst, color, _x, _y, _w, _h) {
2561
- let x;
2562
- let y;
2563
- let w;
2564
- let h;
2565
- if (typeof _x === "object") {
2566
- x = _x.x ?? 0;
2567
- y = _x.y ?? 0;
2568
- w = _x.w ?? dst.width;
2569
- h = _x.h ?? dst.height;
2570
- } else if (typeof _x === "number") {
2571
- x = _x;
2572
- y = _y;
2573
- w = _w;
2574
- h = _h;
2575
- } else {
2576
- x = 0;
2577
- y = 0;
2578
- w = dst.width;
2579
- h = dst.height;
3138
+ // src/PixelData/PixelData.ts
3139
+ var PixelData = class _PixelData {
3140
+ data32;
3141
+ imageData;
3142
+ get width() {
3143
+ return this.imageData.width;
2580
3144
  }
2581
- if (x < 0) {
2582
- w += x;
2583
- x = 0;
3145
+ get height() {
3146
+ return this.imageData.height;
2584
3147
  }
2585
- if (y < 0) {
2586
- h += y;
2587
- y = 0;
3148
+ constructor(imageData) {
3149
+ this.data32 = imageDataToUInt32Array(imageData);
3150
+ this.imageData = imageData;
2588
3151
  }
2589
- const actualW = Math.min(w, dst.width - x);
2590
- const actualH = Math.min(h, dst.height - y);
2591
- if (actualW <= 0 || actualH <= 0) {
2592
- return;
3152
+ set(imageData) {
3153
+ this.imageData = imageData;
3154
+ this.data32 = imageDataToUInt32Array(imageData);
2593
3155
  }
2594
- const dst32 = dst.data32;
2595
- const dw = dst.width;
2596
- if (actualW === dw && actualH === dst.height && x === 0 && y === 0) {
2597
- dst32.fill(color);
2598
- return;
3156
+ /**
3157
+ * Creates a deep copy of the PixelData using the environment's ImageData constructor.
3158
+ */
3159
+ copy() {
3160
+ const buffer = new Uint8ClampedArray(this.imageData.data);
3161
+ const ImageConstructor = typeof ImageData !== "undefined" ? ImageData : this.imageData.constructor;
3162
+ const newImageData = new ImageConstructor(
3163
+ buffer,
3164
+ this.width,
3165
+ this.height
3166
+ );
3167
+ return new _PixelData(newImageData);
2599
3168
  }
2600
- for (let iy = 0; iy < actualH; iy++) {
2601
- const start = (y + iy) * dw + x;
2602
- const end = start + actualW;
2603
- dst32.fill(color, start, end);
3169
+ };
3170
+
3171
+ // src/PixelData/applyCircleBrushToPixelData.ts
3172
+ function applyCircleBrushToPixelData(target, color, centerX, centerY, brushSize, alpha = 255, fallOff, blendFn = sourceOverPerfect) {
3173
+ const r = brushSize / 2;
3174
+ const rSqr = r * r;
3175
+ const centerOffset = brushSize % 2 === 0 ? 0.5 : 0;
3176
+ const xStart = Math.max(0, Math.ceil(centerX - r));
3177
+ const xEnd = Math.min(target.width - 1, Math.floor(centerX + r));
3178
+ const yStart = Math.max(0, Math.ceil(centerY - r));
3179
+ const yEnd = Math.min(target.height - 1, Math.floor(centerY + r));
3180
+ const data32 = target.data32;
3181
+ const targetWidth = target.width;
3182
+ const baseColor = color & 16777215;
3183
+ const invR = 1 / r;
3184
+ const constantSrc = (alpha << 24 | baseColor) >>> 0;
3185
+ for (let cy = yStart; cy <= yEnd; cy++) {
3186
+ const dy = cy - centerY + centerOffset;
3187
+ const dySqr = dy * dy;
3188
+ const rowOffset = cy * targetWidth;
3189
+ for (let cx = xStart; cx <= xEnd; cx++) {
3190
+ const dx = cx - centerX + centerOffset;
3191
+ const dSqr = dx * dx + dySqr;
3192
+ if (dSqr <= rSqr) {
3193
+ const idx = rowOffset + cx;
3194
+ if (fallOff) {
3195
+ const strength = fallOff(Math.sqrt(dSqr) * invR);
3196
+ const fAlpha = alpha * strength & 255;
3197
+ const src = (fAlpha << 24 | baseColor) >>> 0;
3198
+ data32[idx] = blendFn(src, data32[idx]);
3199
+ } else {
3200
+ data32[idx] = blendFn(constantSrc, data32[idx]);
3201
+ }
3202
+ }
3203
+ }
3204
+ }
3205
+ }
3206
+
3207
+ // src/PixelData/applyRectBrushToPixelData.ts
3208
+ function applyRectBrushToPixelData(target, color, centerX, centerY, brushWidth, brushHeight, alpha = 255, fallOff, blendFn = sourceOverPerfect) {
3209
+ const targetWidth = target.width;
3210
+ const targetHeight = target.height;
3211
+ const data32 = target.data32;
3212
+ const rawStartX = Math.floor(centerX - brushWidth / 2);
3213
+ const rawStartY = Math.floor(centerY - brushHeight / 2);
3214
+ const endX = Math.min(targetWidth, rawStartX + brushWidth);
3215
+ const endY = Math.min(targetHeight, rawStartY + brushHeight);
3216
+ const startX = Math.max(0, rawStartX);
3217
+ const startY = Math.max(0, rawStartY);
3218
+ const baseColor = color & 16777215;
3219
+ const constantSrc = (alpha << 24 | baseColor) >>> 0;
3220
+ const invHalfW = 1 / (brushWidth / 2);
3221
+ const invHalfH = 1 / (brushHeight / 2);
3222
+ for (let py = startY; py < endY; py++) {
3223
+ const rowOffset = py * targetWidth;
3224
+ const dy = Math.abs(py + 0.5 - centerY) * invHalfH;
3225
+ for (let px = startX; px < endX; px++) {
3226
+ if (fallOff) {
3227
+ const dx = Math.abs(px + 0.5 - centerX) * invHalfW;
3228
+ const dist = dx > dy ? dx : dy;
3229
+ const fAlpha = alpha * fallOff(dist) | 0;
3230
+ const src = (fAlpha << 24 | baseColor) >>> 0;
3231
+ data32[rowOffset + px] = blendFn(src, data32[rowOffset + px]);
3232
+ } else {
3233
+ data32[rowOffset + px] = blendFn(constantSrc, data32[rowOffset + px]);
3234
+ }
3235
+ }
2604
3236
  }
2605
3237
  }
3238
+ function getRectBrushBounds(centerX, centerY, brushWidth, brushHeight, targetWidth, targetHeight) {
3239
+ const rawStartX = Math.floor(centerX - brushWidth / 2);
3240
+ const rawStartY = Math.floor(centerY - brushHeight / 2);
3241
+ const rawEndX = rawStartX + brushWidth;
3242
+ const rawEndY = rawStartY + brushHeight;
3243
+ const startX = targetWidth !== void 0 ? Math.max(0, rawStartX) : rawStartX;
3244
+ const startY = targetHeight !== void 0 ? Math.max(0, rawStartY) : rawStartY;
3245
+ const endX = targetWidth !== void 0 ? Math.min(targetWidth, rawEndX) : rawEndX;
3246
+ const endY = targetHeight !== void 0 ? Math.min(targetHeight, rawEndY) : rawEndY;
3247
+ return {
3248
+ x: startX,
3249
+ y: startY,
3250
+ w: endX - startX,
3251
+ h: endY - startY
3252
+ };
3253
+ }
2606
3254
 
2607
3255
  // src/PixelData/clearPixelData.ts
2608
3256
  function clearPixelData(dst, rect) {
@@ -2650,16 +3298,6 @@ function extractPixelData(source, _x, _y, _w, _h) {
2650
3298
  return result;
2651
3299
  }
2652
3300
 
2653
- // src/PixelData/invertPixelData.ts
2654
- function invertPixelData(pixelData) {
2655
- const data32 = pixelData.data32;
2656
- const len = data32.length;
2657
- for (let i = 0; i < len; i++) {
2658
- data32[i] = data32[i] ^ 16777215;
2659
- }
2660
- return pixelData;
2661
- }
2662
-
2663
3301
  // src/PixelData/reflectPixelData.ts
2664
3302
  function reflectPixelDataHorizontal(pixelData) {
2665
3303
  const width = pixelData.width;
@@ -2750,24 +3388,54 @@ function rotateSquareInPlace(pixelData) {
2750
3388
  }
2751
3389
  }
2752
3390
  }
3391
+
3392
+ // src/PixelData/writePixelDataBuffer.ts
3393
+ function writePixelDataBuffer(target, data, _x, _y, _w, _h) {
3394
+ const { x, y, w, h } = typeof _x === "object" ? _x : {
3395
+ x: _x,
3396
+ y: _y,
3397
+ w: _w,
3398
+ h: _h
3399
+ };
3400
+ const dstW = target.width;
3401
+ const dstH = target.height;
3402
+ const dstData = target.data32;
3403
+ const x0 = Math.max(0, x);
3404
+ const y0 = Math.max(0, y);
3405
+ const x1 = Math.min(dstW, x + w);
3406
+ const y1 = Math.min(dstH, y + h);
3407
+ if (x1 <= x0 || y1 <= y0) {
3408
+ return;
3409
+ }
3410
+ const rowLen = x1 - x0;
3411
+ const srcCol = x0 - x;
3412
+ const srcYOffset = y0 - y;
3413
+ const actualH = y1 - y0;
3414
+ for (let row = 0; row < actualH; row++) {
3415
+ const dstStart = (y0 + row) * dstW + x0;
3416
+ const srcRow = srcYOffset + row;
3417
+ const srcStart = srcRow * w + srcCol;
3418
+ dstData.set(data.subarray(srcStart, srcStart + rowLen), dstStart);
3419
+ }
3420
+ }
2753
3421
  // Annotate the CommonJS export names for ESM import in node:
2754
3422
  0 && (module.exports = {
2755
- BlendMode,
2756
- FAST_BLENDER_REGISTRY,
2757
- FAST_BLEND_MODES,
2758
- FAST_BLEND_MODE_BY_NAME,
2759
- FAST_BLEND_TO_INDEX,
2760
- INDEX_TO_FAST_BLEND,
2761
- INDEX_TO_PERFECT_BLEND,
3423
+ BASE_FAST_BLEND_MODE_FUNCTIONS,
3424
+ BASE_PERFECT_BLEND_MODE_FUNCTIONS,
3425
+ BaseBlendMode,
3426
+ HistoryManager,
2762
3427
  IndexedImage,
2763
3428
  MaskType,
2764
- PERFECT_BLENDER_REGISTRY,
2765
- PERFECT_BLEND_MODES,
2766
- PERFECT_BLEND_MODE_BY_NAME,
2767
- PERFECT_BLEND_TO_INDEX,
3429
+ PixelAccumulator,
2768
3430
  PixelData,
3431
+ PixelEngineConfig,
3432
+ PixelTile,
3433
+ PixelWriter,
2769
3434
  UnsupportedFormatError,
3435
+ applyCircleBrushToPixelData,
2770
3436
  applyMaskToPixelData,
3437
+ applyPatchTiles,
3438
+ applyRectBrushToPixelData,
2771
3439
  base64DecodeArrayBuffer,
2772
3440
  base64EncodeArrayBuffer,
2773
3441
  blendColorPixelData,
@@ -2806,6 +3474,7 @@ function rotateSquareInPlace(pixelData) {
2806
3474
  floodFillSelection,
2807
3475
  getImageDataFromClipboard,
2808
3476
  getIndexedImageColorCounts,
3477
+ getRectBrushBounds,
2809
3478
  getSupportedPixelFormats,
2810
3479
  hardLightFast,
2811
3480
  hardLightPerfect,
@@ -2834,14 +3503,25 @@ function rotateSquareInPlace(pixelData) {
2834
3503
  linearDodgePerfect,
2835
3504
  linearLightFast,
2836
3505
  linearLightPerfect,
3506
+ makeBlendModeRegistry,
3507
+ makeFastBlendModeRegistry,
3508
+ makeFullPixelMutator,
3509
+ makePerfectBlendModeRegistry,
2837
3510
  makePixelCanvas,
2838
3511
  makeReusableCanvas,
2839
3512
  makeReusableImageData,
2840
3513
  mergeMasks,
2841
3514
  multiplyFast,
2842
3515
  multiplyPerfect,
3516
+ mutatorApplyMask,
3517
+ mutatorBlendColor,
3518
+ mutatorBlendPixel,
3519
+ mutatorBlendPixelData,
3520
+ mutatorFill,
3521
+ mutatorInvert,
2843
3522
  overlayFast,
2844
3523
  overlayPerfect,
3524
+ overwriteBase,
2845
3525
  overwriteFast,
2846
3526
  overwritePerfect,
2847
3527
  packColor,
@@ -2876,8 +3556,9 @@ function rotateSquareInPlace(pixelData) {
2876
3556
  vividLightFast,
2877
3557
  vividLightPerfect,
2878
3558
  writeImageData,
2879
- writeImageDataPixels,
3559
+ writeImageDataBuffer,
2880
3560
  writeImageDataToClipboard,
2881
- writeImgBlobToClipboard
3561
+ writeImgBlobToClipboard,
3562
+ writePixelDataBuffer
2882
3563
  });
2883
3564
  //# sourceMappingURL=index.dev.cjs.map