pixel-data-js 0.16.0 → 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 (33) hide show
  1. package/dist/index.dev.cjs +1648 -908
  2. package/dist/index.dev.cjs.map +1 -1
  3. package/dist/index.dev.js +1629 -906
  4. package/dist/index.dev.js.map +1 -1
  5. package/dist/index.prod.cjs +1648 -908
  6. package/dist/index.prod.cjs.map +1 -1
  7. package/dist/index.prod.d.ts +287 -67
  8. package/dist/index.prod.js +1629 -906
  9. package/dist/index.prod.js.map +1 -1
  10. package/package.json +1 -1
  11. package/src/BlendModes/BlendModeRegistry.ts +34 -50
  12. package/src/BlendModes/blend-modes-perfect.ts +313 -136
  13. package/src/BlendModes/blend-modes.ts +6 -0
  14. package/src/History/HistoryManager.ts +83 -0
  15. package/src/History/PixelAccumulator.ts +191 -0
  16. package/src/History/PixelEngineConfig.ts +18 -0
  17. package/src/History/PixelMutator/mutatorApplyMask.ts +20 -0
  18. package/src/History/PixelMutator/mutatorBlendColor.ts +22 -0
  19. package/src/History/PixelMutator/mutatorBlendPixel.ts +37 -0
  20. package/src/History/PixelMutator/mutatorBlendPixelData.ts +24 -0
  21. package/src/History/PixelMutator/mutatorFillPixelData.ts +21 -0
  22. package/src/History/PixelMutator/mutatorInvert.ts +18 -0
  23. package/src/History/PixelMutator.ts +18 -0
  24. package/src/History/PixelPatchTiles.ts +52 -0
  25. package/src/History/PixelWriter.ts +79 -0
  26. package/src/ImageData/{writeImageDataPixels.ts → writeImageDataBuffer.ts} +3 -3
  27. package/src/PixelData/applyCircleBrushToPixelData.ts +69 -0
  28. package/src/PixelData/applyMaskToPixelData.ts +1 -1
  29. package/src/PixelData/applyRectBrushToPixelData.ts +102 -0
  30. package/src/PixelData/invertPixelData.ts +74 -7
  31. package/src/PixelData/writePixelDataBuffer.ts +65 -0
  32. package/src/_types.ts +31 -11
  33. package/src/index.ts +18 -1
@@ -1,6 +1,6 @@
1
1
  import type { BlendColor32, Color32 } from '../_types'
2
2
  import { BaseBlendMode, overwriteBase } from './blend-modes'
3
- import { type BaseBlendModeRegistry, makeBlendModeRegistry } from './BlendModeRegistry'
3
+ import { makeBlendModeRegistry } from './BlendModeRegistry'
4
4
 
5
5
  export const overwritePerfect = overwriteBase
6
6
 
@@ -9,17 +9,25 @@ export const sourceOverPerfect: BlendColor32 = (src, dst) => {
9
9
  if (sa === 255) return src
10
10
  if (sa === 0) return dst
11
11
 
12
+ const invA = 255 - sa
13
+
12
14
  const sr = src & 0xFF, sg = (src >>> 8) & 0xFF, sb = (src >>> 16) & 0xFF
13
15
  const dr = dst & 0xFF, dg = (dst >>> 8) & 0xFF, db = (dst >>> 16) & 0xFF
14
-
15
16
  const da = (dst >>> 24) & 0xFF
16
17
 
17
- // Alpha Lerp inlined
18
- const invA = 255 - sa
19
- const r = (sr * sa + dr * invA) / 255 | 0
20
- const g = (sg * sa + dg * invA) / 255 | 0
21
- const b = (sb * sa + db * invA) / 255 | 0
22
- const a = (255 * sa + da * invA) / 255 | 0
18
+ // Exact division by 255 using bit-shifts
19
+ // Formula: (v + 1 + (v >> 8)) >> 8
20
+ const tR = (sr * sa + dr * invA)
21
+ const r = (tR + 1 + (tR >> 8)) >> 8
22
+
23
+ const tG = (sg * sa + dg * invA)
24
+ const g = (tG + 1 + (tG >> 8)) >> 8
25
+
26
+ const tB = (sb * sa + db * invA)
27
+ const b = (tB + 1 + (tB >> 8)) >> 8
28
+
29
+ const tA = (255 * sa + da * invA)
30
+ const a = (tA + 1 + (tA >> 8)) >> 8
23
31
 
24
32
  return ((a << 24) | (b << 16) | (g << 8) | r) >>> 0 as Color32
25
33
  }
@@ -39,10 +47,16 @@ export const darkenPerfect: BlendColor32 = (src, dst) => {
39
47
  // Alpha Lerp inlined
40
48
  const invA = 255 - sa
41
49
 
42
- const r = (br * sa + dr * invA) / 255 | 0
43
- const g = (bg * sa + dg * invA) / 255 | 0
44
- const b = (bb * sa + db * invA) / 255 | 0
45
- const a = (255 * sa + ((dst >>> 24) & 0xFF) * invA) / 255 | 0
50
+ const da = (dst >>> 24) & 0xFF
51
+
52
+ const tR = br * sa + dr * invA
53
+ const r = (tR + 1 + (tR >> 8)) >> 8
54
+ const tG = bg * sa + dg * invA
55
+ const g = (tG + 1 + (tG >> 8)) >> 8
56
+ const tB = bb * sa + db * invA
57
+ const b = (tB + 1 + (tB >> 8)) >> 8
58
+ const tA = 255 * sa + da * invA
59
+ const a = (tA + 1 + (tA >> 8)) >> 8
46
60
 
47
61
  return ((a << 24) | (b << 16) | (g << 8) | r) >>> 0 as Color32
48
62
  }
@@ -51,23 +65,43 @@ export const darkenPerfect: BlendColor32 = (src, dst) => {
51
65
  export const multiplyPerfect: BlendColor32 = (src, dst) => {
52
66
  const sa = (src >>> 24) & 0xFF
53
67
  if (sa === 0) return dst
54
- const sr = src & 0xFF, sg = (src >>> 8) & 0xFF, sb = (src >>> 16) & 0xFF
55
- const dr = dst & 0xFF, dg = (dst >>> 8) & 0xFF, db = (dst >>> 16) & 0xFF
56
68
 
57
- // Consistent floor rounding for all channels
58
- const br = (sr * dr / 255) | 0
59
- const bg = (sg * dg / 255) | 0
60
- const bb = (sb * db / 255) | 0
69
+ const dr = dst & 0xFF
70
+ const dg = (dst >>> 8) & 0xFF
71
+ const db = (dst >>> 16) & 0xFF
72
+ const da = (dst >>> 24) & 0xFF
73
+
74
+ const sr = src & 0xFF
75
+ const sg = (src >>> 8) & 0xFF
76
+ const sb = (src >>> 16) & 0xFF
77
+
78
+ // Calculate base multiply result: (sr * dr) / 255
79
+ const mR = sr * dr
80
+ const br = (mR + 1 + (mR >> 8)) >> 8
81
+
82
+ const mG = sg * dg
83
+ const bg = (mG + 1 + (mG >> 8)) >> 8
84
+
85
+ const mB = sb * db
86
+ const bb = (mB + 1 + (mB >> 8)) >> 8
87
+
88
+ // If fully opaque, return with full alpha
61
89
  if (sa === 255) return (0xFF000000 | (bb << 16) | (bg << 8) | br) >>> 0 as Color32
62
90
 
63
91
  // Alpha Lerp inlined
64
92
  const invA = 255 - sa
65
- const da = (dst >>> 24) & 0xFF
66
93
 
67
- const r = (br * sa + dr * invA) / 255 | 0
68
- const g = (bg * sa + dg * invA) / 255 | 0
69
- const b = (bb * sa + db * invA) / 255 | 0
70
- const a = (255 * sa + da * invA) / 255 | 0
94
+ const tR = br * sa + dr * invA
95
+ const r = (tR + 1 + (tR >> 8)) >> 8
96
+
97
+ const tG = bg * sa + dg * invA
98
+ const g = (tG + 1 + (tG >> 8)) >> 8
99
+
100
+ const tB = bb * sa + db * invA
101
+ const b = (tB + 1 + (tB >> 8)) >> 8
102
+
103
+ const tA = 255 * sa + da * invA
104
+ const a = (tA + 1 + (tA >> 8)) >> 8
71
105
 
72
106
  return ((a << 24) | (b << 16) | (g << 8) | r) >>> 0 as Color32
73
107
  }
@@ -77,20 +111,42 @@ export const colorBurnPerfect: BlendColor32 = (src, dst) => {
77
111
  const sa = (src >>> 24) & 0xFF
78
112
  if (sa === 0) return dst
79
113
 
80
- const sr = src & 0xFF, sg = (src >>> 8) & 0xFF, sb = (src >>> 16) & 0xFF
81
- const dr = dst & 0xFF, dg = (dst >>> 8) & 0xFF, db = (dst >>> 16) & 0xFF
82
- const br = dr === 255 ? 255 : sr === 0 ? 0 : Math.max(0, (255 - (((255 - dr) * 255 / sr) | 0)))
83
- const bg = dg === 255 ? 255 : sg === 0 ? 0 : Math.max(0, (255 - (((255 - dg) * 255 / sg) | 0)))
84
- const bb = db === 255 ? 255 : sb === 0 ? 0 : Math.max(0, (255 - (((255 - db) * 255 / sb) | 0)))
114
+ const dr = dst & 0xFF
115
+ const dg = (dst >>> 8) & 0xFF
116
+ const db = (dst >>> 16) & 0xFF
117
+
118
+ const sr = src & 0xFF
119
+ const sg = (src >>> 8) & 0xFF
120
+ const sb = (src >>> 16) & 0xFF
121
+
122
+ // Color Burn Core Math: 255 - ((255 - dst) * 255 / src)
123
+ // We use | 0 to truncate the division result immediately.
124
+ const resR = dr === 255 ? 255 : sr === 0 ? 0 : 255 - (((255 - dr) * 255 / sr) | 0)
125
+ const br = resR < 0 ? 0 : resR
126
+
127
+ const resG = dg === 255 ? 255 : sg === 0 ? 0 : 255 - (((255 - dg) * 255 / sg) | 0)
128
+ const bg = resG < 0 ? 0 : resG
129
+
130
+ const resB = db === 255 ? 255 : sb === 0 ? 0 : 255 - (((255 - db) * 255 / sb) | 0)
131
+ const bb = resB < 0 ? 0 : resB
132
+
85
133
  if (sa === 255) return (0xFF000000 | (bb << 16) | (bg << 8) | br) >>> 0 as Color32
86
134
 
135
+ // Alpha Lerp inlined
87
136
  const invA = 255 - sa
88
137
  const da = (dst >>> 24) & 0xFF
89
138
 
90
- const r = (br * sa + dr * invA) / 255 | 0
91
- const g = (bg * sa + dg * invA) / 255 | 0
92
- const b = (bb * sa + db * invA) / 255 | 0
93
- const a = (255 * sa + da * invA) / 255 | 0
139
+ const tR = br * sa + dr * invA
140
+ const r = (tR + 1 + (tR >> 8)) >> 8
141
+
142
+ const tG = bg * sa + dg * invA
143
+ const g = (tG + 1 + (tG >> 8)) >> 8
144
+
145
+ const tB = bb * sa + db * invA
146
+ const b = (tB + 1 + (tB >> 8)) >> 8
147
+
148
+ const tA = 255 * sa + da * invA
149
+ const a = (tA + 1 + (tA >> 8)) >> 8
94
150
 
95
151
  return ((a << 24) | (b << 16) | (g << 8) | r) >>> 0 as Color32
96
152
  }
@@ -113,10 +169,16 @@ export const linearBurnPerfect: BlendColor32 = (src, dst) => {
113
169
 
114
170
  // Alpha Lerp inlined
115
171
  const invA = 255 - sa
116
- const r = (br * sa + dr * invA) / 255 | 0
117
- const g = (bg * sa + dg * invA) / 255 | 0
118
- const b = (bb * sa + db * invA) / 255 | 0
119
- const a = (255 * sa + ((dst >>> 24) & 0xFF) * invA) / 255 | 0
172
+ const da = (dst >>> 24) & 0xFF
173
+
174
+ const tR = br * sa + dr * invA
175
+ const r = (tR + 1 + (tR >> 8)) >> 8
176
+ const tG = bg * sa + dg * invA
177
+ const g = (tG + 1 + (tG >> 8)) >> 8
178
+ const tB = bb * sa + db * invA
179
+ const b = (tB + 1 + (tB >> 8)) >> 8
180
+ const tA = 255 * sa + da * invA
181
+ const a = (tA + 1 + (tA >> 8)) >> 8
120
182
 
121
183
  return ((a << 24) | (b << 16) | (g << 8) | r) >>> 0 as Color32
122
184
  }
@@ -150,10 +212,16 @@ export const darkerPerfect: BlendColor32 = (src, dst) => {
150
212
 
151
213
  // 3. Alpha Lerp inlined
152
214
  const invA = 255 - sa
153
- const r = (br * sa + dr * invA) / 255 | 0
154
- const g = (bg * sa + dg * invA) / 255 | 0
155
- const b = (bb * sa + db * invA) / 255 | 0
156
- const a = (255 * sa + ((dst >>> 24) & 0xFF) * invA) / 255 | 0
215
+ const da = (dst >>> 24) & 0xFF
216
+
217
+ const tR = br * sa + dr * invA
218
+ const r = (tR + 1 + (tR >> 8)) >> 8
219
+ const tG = bg * sa + dg * invA
220
+ const g = (tG + 1 + (tG >> 8)) >> 8
221
+ const tB = bb * sa + db * invA
222
+ const b = (tB + 1 + (tB >> 8)) >> 8
223
+ const tA = 255 * sa + da * invA
224
+ const a = (tA + 1 + (tA >> 8)) >> 8
157
225
 
158
226
  return ((a << 24) | (b << 16) | (g << 8) | r) >>> 0 as Color32
159
227
  }
@@ -171,10 +239,16 @@ export const lightenPerfect: BlendColor32 = (src, dst) => {
171
239
  if (sa === 255) return (0xFF000000 | (bb << 16) | (bg << 8) | br) >>> 0 as Color32
172
240
  // Alpha Lerp inlined
173
241
  const invA = 255 - sa
174
- const r = (br * sa + dr * invA) / 255 | 0
175
- const g = (bg * sa + dg * invA) / 255 | 0
176
- const b = (bb * sa + db * invA) / 255 | 0
177
- const a = (255 * sa + ((dst >>> 24) & 0xFF) * invA) / 255 | 0
242
+ const da = (dst >>> 24) & 0xFF
243
+
244
+ const tR = br * sa + dr * invA
245
+ const r = (tR + 1 + (tR >> 8)) >> 8
246
+ const tG = bg * sa + dg * invA
247
+ const g = (tG + 1 + (tG >> 8)) >> 8
248
+ const tB = bb * sa + db * invA
249
+ const b = (tB + 1 + (tB >> 8)) >> 8
250
+ const tA = 255 * sa + da * invA
251
+ const a = (tA + 1 + (tA >> 8)) >> 8
178
252
 
179
253
  return ((a << 24) | (b << 16) | (g << 8) | r) >>> 0 as Color32
180
254
  }
@@ -197,10 +271,16 @@ export const screenPerfect: BlendColor32 = (src, dst) => {
197
271
  // Alpha Lerp inlined
198
272
  const invA = 255 - sa
199
273
 
200
- const r = (br * sa + dr * invA) / 255 | 0
201
- const g = (bg * sa + dg * invA) / 255 | 0
202
- const b = (bb * sa + db * invA) / 255 | 0
203
- const a = (255 * sa + ((dst >>> 24) & 0xFF) * invA) / 255 | 0
274
+ const da = (dst >>> 24) & 0xFF
275
+
276
+ const tR = br * sa + dr * invA
277
+ const r = (tR + 1 + (tR >> 8)) >> 8
278
+ const tG = bg * sa + dg * invA
279
+ const g = (tG + 1 + (tG >> 8)) >> 8
280
+ const tB = bb * sa + db * invA
281
+ const b = (tB + 1 + (tB >> 8)) >> 8
282
+ const tA = 255 * sa + da * invA
283
+ const a = (tA + 1 + (tA >> 8)) >> 8
204
284
 
205
285
  return ((a << 24) | (b << 16) | (g << 8) | r) >>> 0 as Color32
206
286
  }
@@ -210,21 +290,42 @@ export const colorDodgePerfect: BlendColor32 = (src, dst) => {
210
290
  const sa = (src >>> 24) & 0xFF
211
291
  if (sa === 0) return dst
212
292
 
213
- const dr = dst & 0xFF, dg = (dst >>> 8) & 0xFF, db = (dst >>> 16) & 0xFF
214
- const sr = src & 0xFF, sg = (src >>> 8) & 0xFF, sb = (src >>> 16) & 0xFF
293
+ const dr = dst & 0xFF
294
+ const dg = (dst >>> 8) & 0xFF
295
+ const db = (dst >>> 16) & 0xFF
296
+
297
+ const sr = src & 0xFF
298
+ const sg = (src >>> 8) & 0xFF
299
+ const sb = (src >>> 16) & 0xFF
300
+
301
+ // Color Dodge Core Math: (dst * 255) / (255 - src)
302
+ // We use ternary checks to handle the sr === 255 division-by-zero guard.
303
+ const resR = sr === 255 ? 255 : (dr * 255 / (255 - sr)) | 0
304
+ const br = resR > 255 ? 255 : resR
305
+
306
+ const resG = sg === 255 ? 255 : (dg * 255 / (255 - sg)) | 0
307
+ const bg = resG > 255 ? 255 : resG
215
308
 
216
- const br = sr === 255 ? 255 : Math.min(255, ((dr * 255 / (255 - sr)) | 0))
217
- const bg = sg === 255 ? 255 : Math.min(255, ((dg * 255 / (255 - sg)) | 0))
218
- const bb = sb === 255 ? 255 : Math.min(255, ((db * 255 / (255 - sb)) | 0))
309
+ const resB = sb === 255 ? 255 : (db * 255 / (255 - sb)) | 0
310
+ const bb = resB > 255 ? 255 : resB
219
311
 
220
312
  if (sa === 255) return (0xFF000000 | (bb << 16) | (bg << 8) | br) >>> 0 as Color32
221
313
 
222
314
  // Alpha Lerp inlined
223
315
  const invA = 255 - sa
224
- const r = (br * sa + dr * invA) / 255 | 0
225
- const g = (bg * sa + dg * invA) / 255 | 0
226
- const b = (bb * sa + db * invA) / 255 | 0
227
- const a = (255 * sa + ((dst >>> 24) & 0xFF) * invA) / 255 | 0
316
+ const da = (dst >>> 24) & 0xFF
317
+
318
+ const tR = br * sa + dr * invA
319
+ const r = (tR + 1 + (tR >> 8)) >> 8
320
+
321
+ const tG = bg * sa + dg * invA
322
+ const g = (tG + 1 + (tG >> 8)) >> 8
323
+
324
+ const tB = bb * sa + db * invA
325
+ const b = (tB + 1 + (tB >> 8)) >> 8
326
+
327
+ const tA = 255 * sa + da * invA
328
+ const a = (tA + 1 + (tA >> 8)) >> 8
228
329
 
229
330
  return ((a << 24) | (b << 16) | (g << 8) | r) >>> 0 as Color32
230
331
  }
@@ -246,10 +347,16 @@ export const linearDodgePerfect: BlendColor32 = (src, dst) => {
246
347
 
247
348
  // Alpha Lerp inlined
248
349
  const invA = 255 - sa
249
- const r = (br * sa + dr * invA) / 255 | 0
250
- const g = (bg * sa + dg * invA) / 255 | 0
251
- const b = (bb * sa + db * invA) / 255 | 0
252
- const a = (255 * sa + ((dst >>> 24) & 0xFF) * invA) / 255 | 0
350
+ const da = (dst >>> 24) & 0xFF
351
+
352
+ const tR = br * sa + dr * invA
353
+ const r = (tR + 1 + (tR >> 8)) >> 8
354
+ const tG = bg * sa + dg * invA
355
+ const g = (tG + 1 + (tG >> 8)) >> 8
356
+ const tB = bb * sa + db * invA
357
+ const b = (tB + 1 + (tB >> 8)) >> 8
358
+ const tA = 255 * sa + da * invA
359
+ const a = (tA + 1 + (tA >> 8)) >> 8
253
360
 
254
361
  return ((a << 24) | (b << 16) | (g << 8) | r) >>> 0 as Color32
255
362
  }
@@ -282,10 +389,16 @@ export const lighterPerfect: BlendColor32 = (src, dst) => {
282
389
 
283
390
  // Alpha Lerp
284
391
  const invA = 255 - sa
285
- const r = (br * sa + dr * invA) / 255 | 0
286
- const g = (bg * sa + dg * invA) / 255 | 0
287
- const b = (bb * sa + db * invA) / 255 | 0
288
- const a = (255 * sa + ((dst >>> 24) & 0xFF) * invA) / 255 | 0
392
+ const da = (dst >>> 24) & 0xFF
393
+
394
+ const tR = br * sa + dr * invA
395
+ const r = (tR + 1 + (tR >> 8)) >> 8
396
+ const tG = bg * sa + dg * invA
397
+ const g = (tG + 1 + (tG >> 8)) >> 8
398
+ const tB = bb * sa + db * invA
399
+ const b = (tB + 1 + (tB >> 8)) >> 8
400
+ const tA = 255 * sa + da * invA
401
+ const a = (tA + 1 + (tA >> 8)) >> 8
289
402
 
290
403
  return ((a << 24) | (b << 16) | (g << 8) | r) >>> 0 as Color32
291
404
  }
@@ -304,10 +417,16 @@ export const overlayPerfect: BlendColor32 = (src, dst) => {
304
417
 
305
418
  // Alpha Lerp inlined
306
419
  const invA = 255 - sa
307
- const r = (br * sa + dr * invA) / 255 | 0
308
- const g = (bg * sa + dg * invA) / 255 | 0
309
- const b = (bb * sa + db * invA) / 255 | 0
310
- const a = (255 * sa + ((dst >>> 24) & 0xFF) * invA) / 255 | 0
420
+ const da = (dst >>> 24) & 0xFF
421
+
422
+ const tR = br * sa + dr * invA
423
+ const r = (tR + 1 + (tR >> 8)) >> 8
424
+ const tG = bg * sa + dg * invA
425
+ const g = (tG + 1 + (tG >> 8)) >> 8
426
+ const tB = bb * sa + db * invA
427
+ const b = (tB + 1 + (tB >> 8)) >> 8
428
+ const tA = 255 * sa + da * invA
429
+ const a = (tA + 1 + (tA >> 8)) >> 8
311
430
 
312
431
  return ((a << 24) | (b << 16) | (g << 8) | r) >>> 0 as Color32
313
432
  }
@@ -319,17 +438,32 @@ export const softLightPerfect: BlendColor32 = (src, dst) => {
319
438
 
320
439
  const dr = dst & 0xFF, dg = (dst >>> 8) & 0xFF, db = (dst >>> 16) & 0xFF
321
440
  const sr = src & 0xFF, sg = (src >>> 8) & 0xFF, sb = (src >>> 16) & 0xFF
322
- const br = (((255 - dr) * ((sr * dr / 255) | 0) + dr * (255 - (((255 - sr) * (255 - dr) / 255) | 0))) / 255) | 0
323
- const bg = (((255 - dg) * ((sg * dg / 255) | 0) + dg * (255 - (((255 - sg) * (255 - dg) / 255) | 0))) / 255) | 0
324
- const bb = (((255 - db) * ((sb * db / 255) | 0) + db * (255 - (((255 - sb) * (255 - db) / 255) | 0))) / 255) | 0
441
+
442
+ const mR = (sr * dr)
443
+ const scR = (255 - sr) * (255 - dr)
444
+ 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
445
+
446
+ const mG = (sg * dg)
447
+ const scG = (255 - sg) * (255 - dg)
448
+ 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
449
+
450
+ const mB = (sb * db)
451
+ const scB = (255 - sb) * (255 - db)
452
+ 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
453
+
325
454
  if (sa === 255) return (0xFF000000 | (bb << 16) | (bg << 8) | br) >>> 0 as Color32
326
455
 
327
- // Alpha Lerp inlined
328
456
  const invA = 255 - sa
329
- const r = (br * sa + dr * invA) / 255 | 0
330
- const g = (bg * sa + dg * invA) / 255 | 0
331
- const b = (bb * sa + db * invA) / 255 | 0
332
- const a = (255 * sa + ((dst >>> 24) & 0xFF) * invA) / 255 | 0
457
+ const da = (dst >>> 24) & 0xFF
458
+
459
+ const tR = br * sa + dr * invA
460
+ const r = (tR + 1 + (tR >> 8)) >> 8
461
+ const tG = bg * sa + dg * invA
462
+ const g = (tG + 1 + (tG >> 8)) >> 8
463
+ const tB = bb * sa + db * invA
464
+ const b = (tB + 1 + (tB >> 8)) >> 8
465
+ const tA = 255 * sa + da * invA
466
+ const a = (tA + 1 + (tA >> 8)) >> 8
333
467
 
334
468
  return ((a << 24) | (b << 16) | (g << 8) | r) >>> 0 as Color32
335
469
  }
@@ -348,10 +482,16 @@ export const hardLightPerfect: BlendColor32 = (src, dst) => {
348
482
 
349
483
  // Alpha Lerp inlined
350
484
  const invA = 255 - sa
351
- const r = (br * sa + dr * invA) / 255 | 0
352
- const g = (bg * sa + dg * invA) / 255 | 0
353
- const b = (bb * sa + db * invA) / 255 | 0
354
- const a = (255 * sa + ((dst >>> 24) & 0xFF) * invA) / 255 | 0
485
+ const da = (dst >>> 24) & 0xFF
486
+
487
+ const tR = br * sa + dr * invA
488
+ const r = (tR + 1 + (tR >> 8)) >> 8
489
+ const tG = bg * sa + dg * invA
490
+ const g = (tG + 1 + (tG >> 8)) >> 8
491
+ const tB = bb * sa + db * invA
492
+ const b = (tB + 1 + (tB >> 8)) >> 8
493
+ const tA = 255 * sa + da * invA
494
+ const a = (tA + 1 + (tA >> 8)) >> 8
355
495
 
356
496
  return ((a << 24) | (b << 16) | (g << 8) | r) >>> 0 as Color32
357
497
  }
@@ -373,10 +513,16 @@ export const vividLightPerfect: BlendColor32 = (src, dst) => {
373
513
 
374
514
  // Alpha Lerp inlined
375
515
  const invA = 255 - sa
376
- const r = (br * sa + dr * invA) / 255 | 0
377
- const g = (bg * sa + dg * invA) / 255 | 0
378
- const b = (bb * sa + db * invA) / 255 | 0
379
- const a = (255 * sa + ((dst >>> 24) & 0xFF) * invA) / 255 | 0
516
+ const da = (dst >>> 24) & 0xFF
517
+
518
+ const tR = br * sa + dr * invA
519
+ const r = (tR + 1 + (tR >> 8)) >> 8
520
+ const tG = bg * sa + dg * invA
521
+ const g = (tG + 1 + (tG >> 8)) >> 8
522
+ const tB = bb * sa + db * invA
523
+ const b = (tB + 1 + (tB >> 8)) >> 8
524
+ const tA = 255 * sa + da * invA
525
+ const a = (tA + 1 + (tA >> 8)) >> 8
380
526
 
381
527
  return ((a << 24) | (b << 16) | (g << 8) | r) >>> 0 as Color32
382
528
  }
@@ -398,10 +544,16 @@ export const linearLightPerfect: BlendColor32 = (src, dst) => {
398
544
 
399
545
  // Alpha Lerp inlined
400
546
  const invA = 255 - sa
401
- const r = (br * sa + dr * invA) / 255 | 0
402
- const g = (bg * sa + dg * invA) / 255 | 0
403
- const b = (bb * sa + db * invA) / 255 | 0
404
- const a = (255 * sa + ((dst >>> 24) & 0xFF) * invA) / 255 | 0
547
+ const da = (dst >>> 24) & 0xFF
548
+
549
+ const tR = br * sa + dr * invA
550
+ const r = (tR + 1 + (tR >> 8)) >> 8
551
+ const tG = bg * sa + dg * invA
552
+ const g = (tG + 1 + (tG >> 8)) >> 8
553
+ const tB = bb * sa + db * invA
554
+ const b = (tB + 1 + (tB >> 8)) >> 8
555
+ const tA = 255 * sa + da * invA
556
+ const a = (tA + 1 + (tA >> 8)) >> 8
405
557
 
406
558
  return ((a << 24) | (b << 16) | (g << 8) | r) >>> 0 as Color32
407
559
  }
@@ -426,10 +578,15 @@ export const pinLightPerfect: BlendColor32 = (src, dst) => {
426
578
 
427
579
  const invA = 255 - sa
428
580
  const da = (dst >>> 24) & 0xFF
429
- const r = (br * sa + dr * invA + 128) / 255 | 0
430
- const g = (bg * sa + dg * invA + 128) / 255 | 0
431
- const b = (bb * sa + db * invA + 128) / 255 | 0
432
- const a = (255 * sa + da * invA + 128) / 255 | 0
581
+
582
+ const tR = br * sa + dr * invA
583
+ const r = (tR + 1 + (tR >> 8)) >> 8
584
+ const tG = bg * sa + dg * invA
585
+ const g = (tG + 1 + (tG >> 8)) >> 8
586
+ const tB = bb * sa + db * invA
587
+ const b = (tB + 1 + (tB >> 8)) >> 8
588
+ const tA = 255 * sa + da * invA
589
+ const a = (tA + 1 + (tA >> 8)) >> 8
433
590
 
434
591
  return ((a << 24) | (b << 16) | (g << 8) | r) >>> 0 as Color32
435
592
  }
@@ -448,10 +605,16 @@ export const hardMixPerfect: BlendColor32 = (src, dst) => {
448
605
 
449
606
  // Alpha Lerp inlined
450
607
  const invA = 255 - sa
451
- const r = (br * sa + dr * invA) / 255 | 0
452
- const g = (bg * sa + dg * invA) / 255 | 0
453
- const b = (bb * sa + db * invA) / 255 | 0
454
- const a = (255 * sa + ((dst >>> 24) & 0xFF) * invA) / 255 | 0
608
+ const da = (dst >>> 24) & 0xFF
609
+
610
+ const tR = br * sa + dr * invA
611
+ const r = (tR + 1 + (tR >> 8)) >> 8
612
+ const tG = bg * sa + dg * invA
613
+ const g = (tG + 1 + (tG >> 8)) >> 8
614
+ const tB = bb * sa + db * invA
615
+ const b = (tB + 1 + (tB >> 8)) >> 8
616
+ const tA = 255 * sa + da * invA
617
+ const a = (tA + 1 + (tA >> 8)) >> 8
455
618
 
456
619
  return ((a << 24) | (b << 16) | (g << 8) | r) >>> 0 as Color32
457
620
  }
@@ -461,27 +624,26 @@ export const differencePerfect: BlendColor32 = (src, dst) => {
461
624
  const sa = (src >>> 24) & 0xFF
462
625
  if (sa === 0) return dst
463
626
 
464
- const dr = dst & 0xFF
465
- const dg = (dst >>> 8) & 0xFF
466
- const db = (dst >>> 16) & 0xFF
467
- const sr = src & 0xFF
468
- const sg = (src >>> 8) & 0xFF
469
- const sb = (src >>> 16) & 0xFF
627
+ const dr = dst & 0xFF, dg = (dst >>> 8) & 0xFF, db = (dst >>> 16) & 0xFF
628
+ const sr = src & 0xFF, sg = (src >>> 8) & 0xFF, sb = (src >>> 16) & 0xFF
470
629
 
471
- const br = Math.abs(dr - sr)
472
- const bg = Math.abs(dg - sg)
473
- const bb = Math.abs(db - sb)
630
+ const br = dr > sr ? dr - sr : sr - dr
631
+ const bg = dg > sg ? dg - sg : sg - dg
632
+ const bb = db > sb ? db - sb : sb - db
474
633
 
475
- if (sa === 255) {
476
- return (0xFF000000 | (bb << 16) | (bg << 8) | br) >>> 0 as Color32
477
- }
634
+ if (sa === 255) return (0xFF000000 | (bb << 16) | (bg << 8) | br) >>> 0 as Color32
478
635
 
479
636
  const invA = 255 - sa
480
637
  const da = (dst >>> 24) & 0xFF
481
- const r = (br * sa + dr * invA + 128) / 255 | 0
482
- const g = (bg * sa + dg * invA + 128) / 255 | 0
483
- const b = (bb * sa + db * invA + 128) / 255 | 0
484
- const a = (255 * sa + da * invA + 128) / 255 | 0
638
+
639
+ const tR = br * sa + dr * invA
640
+ const r = (tR + 1 + (tR >> 8)) >> 8
641
+ const tG = bg * sa + dg * invA
642
+ const g = (tG + 1 + (tG >> 8)) >> 8
643
+ const tB = bb * sa + db * invA
644
+ const b = (tB + 1 + (tB >> 8)) >> 8
645
+ const tA = 255 * sa + da * invA
646
+ const a = (tA + 1 + (tA >> 8)) >> 8
485
647
 
486
648
  return ((a << 24) | (b << 16) | (g << 8) | r) >>> 0 as Color32
487
649
  }
@@ -498,22 +660,28 @@ export const exclusionPerfect: BlendColor32 = (src, dst) => {
498
660
  const sg = (src >>> 8) & 0xFF
499
661
  const sb = (src >>> 16) & 0xFF
500
662
 
501
- // Using >> 7 (divide by 128) instead of / 255
502
- // This is equivalent to (2 * s * d) / 256
503
- const br = dr + sr - ((dr * sr) >> 7)
504
- const bg = dg + sg - ((dg * sg) >> 7)
505
- const bb = db + sb - ((db * sb) >> 7)
663
+ const r2 = dr * sr
664
+ const br = dr + sr - (((r2 + r2) + 1 + ((r2 + r2) >> 8)) >> 8)
506
665
 
507
- if (sa === 255) {
508
- return (0xFF000000 | (bb << 16) | (bg << 8) | br) >>> 0 as Color32
509
- }
666
+ const g2 = dg * sg
667
+ const bg = dg + sg - (((g2 + g2) + 1 + ((g2 + g2) >> 8)) >> 8)
668
+
669
+ const b2 = db * sb
670
+ const bb = db + sb - (((b2 + b2) + 1 + ((b2 + b2) >> 8)) >> 8)
671
+
672
+ if (sa === 255) return (0xFF000000 | (bb << 16) | (bg << 8) | br) >>> 0 as Color32
510
673
 
511
674
  const invA = 255 - sa
512
675
  const da = (dst >>> 24) & 0xFF
513
- const r = (br * sa + dr * invA) / 255 | 0
514
- const g = (bg * sa + dg * invA) / 255 | 0
515
- const b = (bb * sa + db * invA) / 255 | 0
516
- const a = (255 * sa + da * invA) / 255 | 0
676
+
677
+ const tR = br * sa + dr * invA
678
+ const r = (tR + 1 + (tR >> 8)) >> 8
679
+ const tG = bg * sa + dg * invA
680
+ const g = (tG + 1 + (tG >> 8)) >> 8
681
+ const tB = bb * sa + db * invA
682
+ const b = (tB + 1 + (tB >> 8)) >> 8
683
+ const tA = 255 * sa + da * invA
684
+ const a = (tA + 1 + (tA >> 8)) >> 8
517
685
 
518
686
  return ((a << 24) | (b << 16) | (g << 8) | r) >>> 0 as Color32
519
687
  }
@@ -535,10 +703,16 @@ export const subtractPerfect: BlendColor32 = (src, dst) => {
535
703
 
536
704
  // Alpha Lerp inlined
537
705
  const invA = 255 - sa
538
- const r = (br * sa + dr * invA) / 255 | 0
539
- const g = (bg * sa + dg * invA) / 255 | 0
540
- const b = (bb * sa + db * invA) / 255 | 0
541
- const a = (255 * sa + ((dst >>> 24) & 0xFF) * invA) / 255 | 0
706
+ const da = (dst >>> 24) & 0xFF
707
+
708
+ const tR = br * sa + dr * invA
709
+ const r = (tR + 1 + (tR >> 8)) >> 8
710
+ const tG = bg * sa + dg * invA
711
+ const g = (tG + 1 + (tG >> 8)) >> 8
712
+ const tB = bb * sa + db * invA
713
+ const b = (tB + 1 + (tB >> 8)) >> 8
714
+ const tA = 255 * sa + da * invA
715
+ const a = (tA + 1 + (tA >> 8)) >> 8
542
716
 
543
717
  return ((a << 24) | (b << 16) | (g << 8) | r) >>> 0 as Color32
544
718
  }
@@ -557,15 +731,18 @@ export const dividePerfect: BlendColor32 = (src, dst) => {
557
731
 
558
732
  // Alpha Lerp inlined
559
733
  const invA = 255 - sa
560
- const r = (br * sa + dr * invA) / 255 | 0
561
- const g = (bg * sa + dg * invA) / 255 | 0
562
- const b = (bb * sa + db * invA) / 255 | 0
563
- const a = (255 * sa + ((dst >>> 24) & 0xFF) * invA) / 255 | 0
734
+ const da = (dst >>> 24) & 0xFF
564
735
 
565
- return ((a << 24) | (b << 16) | (g << 8) | r) >>> 0 as Color32
566
- }
736
+ const tR = br * sa + dr * invA
737
+ const r = (tR + 1 + (tR >> 8)) >> 8
738
+ const tG = bg * sa + dg * invA
739
+ const g = (tG + 1 + (tG >> 8)) >> 8
740
+ const tB = bb * sa + db * invA
741
+ const b = (tB + 1 + (tB >> 8)) >> 8
742
+ const tA = 255 * sa + da * invA
743
+ const a = (tA + 1 + (tA >> 8)) >> 8
567
744
 
568
- export interface PerfectBlendModes extends BaseBlendModeRegistry {
745
+ return ((a << 24) | (b << 16) | (g << 8) | r) >>> 0 as Color32
569
746
  }
570
747
 
571
748
  export const BASE_PERFECT_BLEND_MODE_FUNCTIONS: Record<number, BlendColor32> = {
@@ -26,5 +26,11 @@ export const BaseBlendMode = {
26
26
  divide: 22,
27
27
  } as const
28
28
 
29
+ export interface RequiredBlendModes {
30
+ overwrite: 0;
31
+ }
32
+
33
+ export type BaseBlendModes = RequiredBlendModes & Record<string, number>
34
+
29
35
  export const overwriteBase: BlendColor32 = (src, _dst) => src
30
36
  overwriteBase.isOverwrite = true